@@ -42,6 +42,10 @@ void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* messa
void throw_sqlite3_exception(JNIEnv* env, int errcode,
const char* sqlite3Message, const char* message);
-}
+void throw_sqlite3_exception_errcode(JNIEnv* env,
+ int errcode,
+ int extended_err_code,
+ const char* message);
+}
#endif // _SQLITE3_EXCEPTION_H
diff --git a/src/net/sqlcipher/AbstractCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractCursor.java
similarity index 92%
rename from src/net/sqlcipher/AbstractCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractCursor.java
index 651a1fea..f3eaf8aa 100644
--- a/src/net/sqlcipher/AbstractCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractCursor.java
@@ -36,12 +36,14 @@
* This is an abstract cursor class that handles a lot of the common code
* that all cursors need to deal with and is provided for convenience reasons.
*/
-public abstract class AbstractCursor implements android.database.CrossProcessCursor {
+public abstract class AbstractCursor implements android.database.CrossProcessCursor, net.sqlcipher.Cursor {
private static final String TAG = "Cursor";
DataSetObservable mDataSetObservable = new DataSetObservable();
ContentObservable mContentObservable = new ContentObservable();
+ private Bundle mExtras = Bundle.EMPTY;
+
/* -------------------------------------------------------- */
/* These need to be implemented by subclasses */
abstract public int getCount();
@@ -56,6 +58,8 @@ public abstract class AbstractCursor implements android.database.CrossProcessCur
abstract public double getDouble(int column);
abstract public boolean isNull(int column);
+ abstract public int getType(int column);
+
// TODO implement getBlob in all cursor types
public byte[] getBlob(int column) {
throw new UnsupportedOperationException("getBlob is not supported");
@@ -73,11 +77,11 @@ public CursorWindow getWindow() {
public int getColumnCount() {
return getColumnNames().length;
}
-
+
public void deactivate() {
deactivateInternal();
}
-
+
/**
* @hide
*/
@@ -88,10 +92,10 @@ public void deactivateInternal() {
}
mDataSetObservable.notifyInvalidated();
}
-
+
public boolean requery() {
if (mSelfObserver != null && mSelfObserverRegistered == false) {
-
+
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
mSelfObserverRegistered = true;
}
@@ -102,7 +106,7 @@ public boolean requery() {
public boolean isClosed() {
return mClosed;
}
-
+
public void close() {
mClosed = true;
mContentObservable.unregisterAll();
@@ -139,7 +143,7 @@ public boolean onMove(int oldPosition, int newPosition) {
return true;
}
-
+
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
// Default implementation, uses getString
String result = getString(columnIndex);
@@ -151,9 +155,11 @@ public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
result.getChars(0, result.length(), data, 0);
}
buffer.sizeCopied = result.length();
+ } else {
+ buffer.sizeCopied = 0;
}
}
-
+
/* -------------------------------------------------------- */
/* Implementation */
public AbstractCursor() {
@@ -198,47 +204,14 @@ public final boolean moveToPosition(int position) {
return result;
}
-
+
/**
* Copy data from cursor to CursorWindow
* @param position start position of data
* @param window
*/
public void fillWindow(int position, android.database.CursorWindow window) {
- if (position < 0 || position > getCount()) {
- return;
- }
- window.acquireReference();
- try {
- int oldpos = mPos;
- mPos = position - 1;
- window.clear();
- window.setStartPosition(position);
- int columnNum = getColumnCount();
- window.setNumColumns(columnNum);
- while (moveToNext() && window.allocRow()) {
- for (int i = 0; i < columnNum; i++) {
- String field = getString(i);
- if (field != null) {
- if (!window.putString(field, mPos, i)) {
- window.freeLastRow();
- break;
- }
- } else {
- if (!window.putNull(mPos, i)) {
- window.freeLastRow();
- break;
- }
- }
- }
- }
-
- mPos = oldpos;
- } catch (IllegalStateException e){
- // simply ignore it
- } finally {
- window.releaseReference();
- }
+ DatabaseUtils.cursorFillWindow(this, position, window);
}
public final boolean move(int offset) {
@@ -396,7 +369,7 @@ public boolean update(int columnIndex, Object obj) {
// Long.valueOf() returns null sometimes!
// Long rowid = Long.valueOf(getLong(mRowIdColumnIndex));
- Long rowid = new Long(getLong(mRowIdColumnIndex));
+ Long rowid = Long.valueOf(getLong(mRowIdColumnIndex));
if (rowid == null) {
throw new IllegalStateException("null rowid. mRowIdColumnIndex = " + mRowIdColumnIndex);
}
@@ -415,7 +388,7 @@ public boolean update(int columnIndex, Object obj) {
/**
* Returns true
if there are pending updates that have not yet been committed.
- *
+ *
* @return true
if there are pending updates that have not yet been committed.
* @hide
* @deprecated
@@ -462,7 +435,7 @@ public void unregisterContentObserver(ContentObserver observer) {
mContentObservable.unregisterObserver(observer);
}
}
-
+
/**
* This is hidden until the data set change model has been re-evaluated.
* @hide
@@ -470,18 +443,18 @@ public void unregisterContentObserver(ContentObserver observer) {
protected void notifyDataSetChange() {
mDataSetObservable.notifyChanged();
}
-
+
/**
* This is hidden until the data set change model has been re-evaluated.
* @hide
*/
protected DataSetObservable getDataSetObservable() {
return mDataSetObservable;
-
+
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
-
+
}
public void unregisterDataSetObserver(DataSetObserver observer) {
@@ -523,12 +496,20 @@ public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
}
}
+ public Uri getNotificationUri() {
+ return mNotifyUri;
+ }
+
public boolean getWantsAllOnMoveCalls() {
return false;
}
+ public void setExtras(Bundle extras) {
+ mExtras = (extras == null) ? Bundle.EMPTY : extras;
+ }
+
public Bundle getExtras() {
- return Bundle.EMPTY;
+ return mExtras;
}
public Bundle respond(Bundle extras) {
@@ -630,6 +611,12 @@ public void onChange(boolean selfChange) {
protected int mRowIdColumnIndex;
protected int mPos;
+
+ /**
+ * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
+ * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
+ * pointing at.
+ */
protected Long mCurrentRowID;
protected ContentResolver mContentResolver;
protected boolean mClosed = false;
diff --git a/src/net/sqlcipher/AbstractWindowedCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractWindowedCursor.java
similarity index 97%
rename from src/net/sqlcipher/AbstractWindowedCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractWindowedCursor.java
index c1f707d2..deb25f33 100644
--- a/src/net/sqlcipher/AbstractWindowedCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractWindowedCursor.java
@@ -50,18 +50,18 @@ public String getString(int columnIndex)
return mWindow.getString(mPos, columnIndex);
}
-
+
@Override
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer)
{
checkPosition();
-
+
synchronized(mUpdatedRows) {
if (isFieldUpdated(columnIndex)) {
super.copyStringToBuffer(columnIndex, buffer);
}
}
-
+
mWindow.copyStringToBuffer(mPos, columnIndex, buffer);
}
@@ -210,11 +210,17 @@ public boolean isFloat(int columnIndex)
return mWindow.isFloat(mPos, columnIndex);
}
+ @Override
+ public int getType(int columnIndex) {
+ checkPosition();
+ return mWindow.getType(mPos, columnIndex);
+ }
+
@Override
protected void checkPosition()
{
super.checkPosition();
-
+
if (mWindow == null) {
throw new StaleDataException("Access closed cursor");
}
@@ -224,7 +230,7 @@ protected void checkPosition()
public CursorWindow getWindow() {
return mWindow;
}
-
+
/**
* Set a new cursor window to cursor, usually set a remote cursor window
* @param window cursor window
@@ -235,7 +241,7 @@ public void setWindow(CursorWindow window) {
}
mWindow = window;
}
-
+
public boolean hasWindow() {
return mWindow != null;
}
diff --git a/src/net/sqlcipher/BulkCursorNative.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.java
similarity index 97%
rename from src/net/sqlcipher/BulkCursorNative.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.java
index d2069d97..868dde6a 100644
--- a/src/net/sqlcipher/BulkCursorNative.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.java
@@ -28,7 +28,7 @@
/**
* Native implementation of the bulk cursor. This is only for use in implementing
* IPC, application code should use the Cursor interface.
- *
+ *
* {@hide}
*/
public abstract class BulkCursorNative extends Binder implements IBulkCursor
@@ -54,7 +54,7 @@ static public IBulkCursor asInterface(IBinder obj)
return new BulkCursorProxy(obj);
}
-
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -100,7 +100,7 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
reply.writeNoException();
return true;
}
-
+
case CLOSE_TRANSACTION: {
data.enforceInterface(IBulkCursor.descriptor);
close();
@@ -166,7 +166,7 @@ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
case RESPOND_TRANSACTION: {
data.enforceInterface(IBulkCursor.descriptor);
- Bundle extras = data.readBundle();
+ Bundle extras = data.readBundle(getClass().getClassLoader());
Bundle returnExtras = respond(extras);
reply.writeNoException();
reply.writeBundle(returnExtras);
@@ -215,7 +215,7 @@ public CursorWindow getWindow(int startPos) throws RemoteException
mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
-
+
CursorWindow window = null;
if (reply.readInt() == 1) {
window = CursorWindow.newFromParcel(reply);
@@ -253,7 +253,7 @@ public int count() throws RemoteException
boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
-
+
int count;
if (result == false) {
count = -1;
@@ -275,14 +275,14 @@ public String[] getColumnNames() throws RemoteException
mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
-
+
String[] columnNames = null;
int numColumns = reply.readInt();
columnNames = new String[numColumns];
for (int i = 0; i < numColumns; i++) {
columnNames[i] = reply.readString();
}
-
+
data.recycle();
reply.recycle();
return columnNames;
@@ -315,7 +315,7 @@ public void close() throws RemoteException
data.recycle();
reply.recycle();
}
-
+
public int requery(IContentObserver observer, CursorWindow window) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -326,7 +326,7 @@ public int requery(IContentObserver observer, CursorWindow window) throws Remote
window.writeToParcel(data, 0);
boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0);
-
+
DatabaseUtils.readExceptionFromParcel(reply);
int count;
@@ -334,7 +334,7 @@ public int requery(IContentObserver observer, CursorWindow window) throws Remote
count = -1;
} else {
count = reply.readInt();
- mExtras = reply.readBundle();
+ mExtras = reply.readBundle(getClass().getClassLoader());
}
data.recycle();
@@ -355,7 +355,7 @@ public boolean updateRows(Map values) throws RemoteException
mRemote.transact(UPDATE_ROWS_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
-
+
boolean result = (reply.readInt() == 1 ? true : false);
data.recycle();
@@ -376,7 +376,7 @@ public boolean deleteRow(int position) throws RemoteException
mRemote.transact(DELETE_ROW_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
-
+
boolean result = (reply.readInt() == 1 ? true : false);
data.recycle();
@@ -412,7 +412,7 @@ public Bundle getExtras() throws RemoteException {
DatabaseUtils.readExceptionFromParcel(reply);
- mExtras = reply.readBundle();
+ mExtras = reply.readBundle(getClass().getClassLoader());
data.recycle();
reply.recycle();
}
@@ -431,7 +431,7 @@ public Bundle respond(Bundle extras) throws RemoteException {
DatabaseUtils.readExceptionFromParcel(reply);
- Bundle returnExtras = reply.readBundle();
+ Bundle returnExtras = reply.readBundle(getClass().getClassLoader());
data.recycle();
reply.recycle();
return returnExtras;
diff --git a/src/net/sqlcipher/BulkCursorToCursorAdaptor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.java
similarity index 99%
rename from src/net/sqlcipher/BulkCursorToCursorAdaptor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.java
index c5753daf..932507e3 100644
--- a/src/net/sqlcipher/BulkCursorToCursorAdaptor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.java
@@ -139,7 +139,7 @@ public void deactivate() {
}
mWindow = null;
}
-
+
@Override
public void close() {
super.close();
@@ -148,7 +148,7 @@ public void close() {
} catch (RemoteException ex) {
Log.w(TAG, "Remote process exception when closing");
}
- mWindow = null;
+ mWindow = null;
}
@Override
@@ -189,7 +189,7 @@ public boolean deleteRow() {
if (result != false) {
// The window contains the old value, discard it
mWindow = null;
-
+
// Fix up the position
mCount = mBulkCursor.count();
if (mPos < mCount) {
@@ -246,7 +246,7 @@ public boolean commitUpdates(Map extends Long,
try {
boolean result = mBulkCursor.updateRows(mUpdatedRows);
-
+
if (result == true) {
mUpdatedRows.clear();
@@ -288,31 +288,31 @@ public Bundle respond(Bundle extras) {
@Override
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
// TODO Auto-generated method stub
-
+
}
@Override
public void registerContentObserver(ContentObserver observer) {
// TODO Auto-generated method stub
-
+
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
// TODO Auto-generated method stub
-
+
}
@Override
public void unregisterContentObserver(ContentObserver observer) {
// TODO Auto-generated method stub
-
+
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
// TODO Auto-generated method stub
-
+
}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/CrossProcessCursorWrapper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CrossProcessCursorWrapper.java
new file mode 100644
index 00000000..76581b57
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CrossProcessCursorWrapper.java
@@ -0,0 +1,26 @@
+package net.sqlcipher;
+
+import android.database.CrossProcessCursor;
+import android.database.CursorWindow;
+
+public class CrossProcessCursorWrapper extends CursorWrapper implements CrossProcessCursor {
+
+ public CrossProcessCursorWrapper(Cursor cursor) {
+ super(cursor);
+ }
+
+ @Override
+ public CursorWindow getWindow() {
+ return null;
+ }
+
+ @Override
+ public void fillWindow(int position, CursorWindow window) {
+ DatabaseUtils.cursorFillWindow(this, position, window);
+ }
+
+ @Override
+ public boolean onMove(int oldPosition, int newPosition) {
+ return true;
+ }
+}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/Cursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/Cursor.java
new file mode 100644
index 00000000..ac706719
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/Cursor.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 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 net.sqlcipher;
+
+/**
+ * Extension of android.database.Cursor to support getType() for API < 11.
+ */
+public interface Cursor extends android.database.Cursor {
+ /*
+ * Values returned by {@link #getType(int)}.
+ * These should be consistent with the corresponding types defined in CursorWindow.h
+ */
+ /** Value returned by {@link #getType(int)} if the specified column is null */
+ static final int FIELD_TYPE_NULL = 0;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is integer */
+ static final int FIELD_TYPE_INTEGER = 1;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is float */
+ static final int FIELD_TYPE_FLOAT = 2;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is string */
+ static final int FIELD_TYPE_STRING = 3;
+
+ /** Value returned by {@link #getType(int)} if the specified column type is blob */
+ static final int FIELD_TYPE_BLOB = 4;
+
+ /**
+ * Returns data type of the given column's value.
+ * The preferred type of the column is returned but the data may be converted to other types
+ * as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)}
+ * etc.
+ *
+ * Returned column types are
+ *
+ * - {@link #FIELD_TYPE_NULL}
+ * - {@link #FIELD_TYPE_INTEGER}
+ * - {@link #FIELD_TYPE_FLOAT}
+ * - {@link #FIELD_TYPE_STRING}
+ * - {@link #FIELD_TYPE_BLOB}
+ *
+ *
+ *
+ * @param columnIndex the zero-based index of the target column.
+ * @return column value type
+ */
+ int getType(int columnIndex);
+}
diff --git a/src/net/sqlcipher/CursorIndexOutOfBoundsException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorIndexOutOfBoundsException.java
similarity index 100%
rename from src/net/sqlcipher/CursorIndexOutOfBoundsException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/CursorIndexOutOfBoundsException.java
diff --git a/src/net/sqlcipher/CursorWindow.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java
similarity index 70%
rename from src/net/sqlcipher/CursorWindow.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java
index 78d08882..b130dc26 100644
--- a/src/net/sqlcipher/CursorWindow.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java
@@ -17,9 +17,22 @@
package net.sqlcipher;
import android.database.CharArrayBuffer;
+
+import android.content.res.Resources;
+import android.database.sqlite.SQLiteClosable;
+import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.Process;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+
+import net.sqlcipher.CursorWindowAllocation;
+import net.sqlcipher.DefaultCursorWindowAllocation;
/**
* A buffer containing multiple cursor rows.
@@ -27,11 +40,25 @@
public class CursorWindow extends android.database.CursorWindow implements Parcelable {
/** The pointer to the native window class */
@SuppressWarnings("unused")
- private int nWindow;
+ /** The pointer to the native window class. set by the native methods in
+ * android_database_CursorWindow.cpp
+ */
+ private long nWindow;
private int mStartPos;
+ private int mRequiredPos;
- /**
+ private static CursorWindowAllocation allocation = new DefaultCursorWindowAllocation();
+
+ public static void setCursorWindowAllocation(CursorWindowAllocation value){
+ allocation = value;
+ }
+
+ public static CursorWindowAllocation getCursorWindowAllocation() {
+ return allocation;
+ }
+
+ /**
* Creates a new empty window.
*
* @param localWindow true if this window will be used in this process only
@@ -39,7 +66,13 @@ public class CursorWindow extends android.database.CursorWindow implements Parce
public CursorWindow(boolean localWindow) {
super(localWindow);
mStartPos = 0;
- native_init(localWindow);
+ if(allocation == null){
+ allocation = new DefaultCursorWindowAllocation();
+ }
+ native_init(localWindow,
+ allocation.getInitialAllocationSize(),
+ allocation.getGrowthPaddingSize(),
+ allocation.getMaxAllocationSize());
}
/**
@@ -59,11 +92,19 @@ public int getStartPosition() {
*/
public void setStartPosition(int pos) {
mStartPos = pos;
- }
-
+ }
+
+ public int getRequiredPosition(){
+ return mRequiredPos;
+ }
+
+ public void setRequiredPosition(int pos) {
+ mRequiredPos = pos;
+ }
+
/**
* Returns the number of rows in this window.
- *
+ *
* @return the number of rows in this window.
*/
public int getNumRows() {
@@ -74,10 +115,10 @@ public int getNumRows() {
releaseReference();
}
}
-
+
private native int getNumRows_native();
/**
- * Set number of Columns
+ * Set number of Columns
* @param columnNum
* @return true if success
*/
@@ -89,9 +130,9 @@ public boolean setNumColumns(int columnNum) {
releaseReference();
}
}
-
+
private native boolean setNumColumns_native(int columnNum);
-
+
/**
* Allocate a row in cursor window
* @return false if cursor window is out of memory
@@ -104,9 +145,9 @@ public boolean allocRow(){
releaseReference();
}
}
-
- private native boolean allocRow_native();
-
+
+ private native boolean allocRow_native();
+
/**
* Free the last row
*/
@@ -118,7 +159,7 @@ public void freeLastRow(){
releaseReference();
}
}
-
+
private native void freeLastRow_native();
/**
@@ -136,8 +177,8 @@ public boolean putBlob(byte[] value, int row, int col) {
releaseReference();
}
}
-
- private native boolean putBlob_native(byte[] value, int row, int col);
+
+ private native boolean putBlob_native(byte[] value, int row, int col);
/**
* Copy String to cursor window
@@ -154,9 +195,9 @@ public boolean putString(String value, int row, int col) {
releaseReference();
}
}
-
- private native boolean putString_native(String value, int row, int col);
-
+
+ private native boolean putString_native(String value, int row, int col);
+
/**
* Copy integer to cursor window
* @param value
@@ -172,12 +213,12 @@ public boolean putLong(long value, int row, int col) {
releaseReference();
}
}
-
+
private native boolean putLong_native(long value, int row, int col);
-
+
/**
- * Copy double to cursor window
+ * Copy double to cursor window
* @param value
* @param row
* @param col
@@ -191,8 +232,8 @@ public boolean putDouble(double value, int row, int col) {
releaseReference();
}
}
-
- private native boolean putDouble_native(double value, int row, int col);
+
+ private native boolean putDouble_native(double value, int row, int col);
/**
* Set the [row, col] value to NULL
@@ -208,13 +249,13 @@ public boolean putNull(int row, int col) {
releaseReference();
}
}
-
+
private native boolean putNull_native(int row, int col);
-
+
/**
* Returns {@code true} if given field is {@code NULL}.
- *
+ *
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is {@code NULL}
@@ -227,9 +268,9 @@ public boolean isNull(int row, int col) {
releaseReference();
}
}
-
+
private native boolean isNull_native(int row, int col);
-
+
/**
* Returns a byte array for the given field.
*
@@ -246,14 +287,50 @@ public byte[] getBlob(int row, int col) {
}
}
+ /**
+ * Returns the value at (row
, col
) as a byte
array.
+ *
+ * If the value is null, then null
is returned. If the
+ * type of column col
is a string type, then the result
+ * is the array of bytes that make up the internal representation of the
+ * string value. If the type of column col
is integral or floating-point,
+ * then an {@link SQLiteException} is thrown.
+ */
private native byte[] getBlob_native(int row, int col);
+ /**
+ * Returns data type of the given column's value.
+ *
+ * Returned column types are
+ *
+ * - {@link Cursor#FIELD_TYPE_NULL}
+ * - {@link Cursor#FIELD_TYPE_INTEGER}
+ * - {@link Cursor#FIELD_TYPE_FLOAT}
+ * - {@link Cursor#FIELD_TYPE_STRING}
+ * - {@link Cursor#FIELD_TYPE_BLOB}
+ *
+ *
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return the value type
+ */
+ public int getType(int row, int col) {
+ acquireReference();
+ try {
+ return getType_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
/**
* Checks if a field contains either a blob or is null.
*
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is {@code NULL} or a blob
+ * @deprecated use {@link #getType(int, int)} instead
*/
public boolean isBlob(int row, int col) {
acquireReference();
@@ -270,6 +347,7 @@ public boolean isBlob(int row, int col) {
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is a long
+ * @deprecated use {@link #getType(int, int)} instead
*/
public boolean isLong(int row, int col) {
acquireReference();
@@ -286,6 +364,7 @@ public boolean isLong(int row, int col) {
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is a float
+ * @deprecated use {@link #getType(int, int)} instead
*/
public boolean isFloat(int row, int col) {
acquireReference();
@@ -302,6 +381,7 @@ public boolean isFloat(int row, int col) {
* @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return {@code true} if given field is {@code NULL} or a String
+ * @deprecated use {@link #getType(int, int)} instead
*/
public boolean isString(int row, int col) {
acquireReference();
@@ -317,31 +397,47 @@ public boolean isString(int row, int col) {
private native boolean isInteger_native(int row, int col);
private native boolean isFloat_native(int row, int col);
+ private native int getType_native(int row, int col);
+
/**
* Returns a String for the given field.
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a String value for the given field
*/
public String getString(int row, int col) {
acquireReference();
try {
- return getString_native(row - mStartPos, col);
+ return getString_native(row - mStartPos, col);
} finally {
releaseReference();
}
}
-
+
+ /**
+ * Returns the value at (row
, col
) as a String
.
+ *
+ * If the value is null, then null
is returned. If the
+ * type of column col
is integral, then the result is the string
+ * that is obtained by formatting the integer value with the printf
+ * family of functions using format specifier %lld
. If the
+ * type of column col
is floating-point, then the result is the string
+ * that is obtained by formatting the floating-point value with the
+ * printf
family of functions using format specifier %g
.
+ * If the type of column col
is a blob type, then an
+ * {@link SQLiteException} is thrown.
+ */
private native String getString_native(int row, int col);
+ //private native byte[] getString_native(int row, int col);
/**
* copy the text for the given field in the provided char array.
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
- * @param buffer the CharArrayBuffer to copy the text into,
- * If the requested string is larger than the buffer
+ * @param buffer the CharArrayBuffer to copy the text into,
+ * If the requested string is larger than the buffer
* a new char buffer will be created to hold the string. and assigne to
* CharArrayBuffer.data
*/
@@ -363,15 +459,15 @@ public void copyStringToBuffer(int row, int col, CharArrayBuffer buffer) {
releaseReference();
}
}
-
+
private native char[] copyStringToBuffer_native(
int row, int col, int bufferSize, CharArrayBuffer buffer);
-
+
/**
* Returns a long for the given field.
* row is 0 based
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a long value for the given field
*/
@@ -383,14 +479,25 @@ public long getLong(int row, int col) {
releaseReference();
}
}
-
+
+ /**
+ * Returns the value at (row
, col
) as a long
.
+ *
+ *
If the value is null, then 0L
is returned. If the
+ * type of column col
is a string type, then the result
+ * is the long
that is obtained by parsing the string value with
+ * strtoll
. If the type of column col
is
+ * floating-point, then the result is the floating-point value casted to a long
.
+ * If the type of column col
is a blob type, then an
+ * {@link SQLiteException} is thrown.
+ */
private native long getLong_native(int row, int col);
/**
* Returns a double for the given field.
* row is 0 based
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a double value for the given field
*/
@@ -402,14 +509,25 @@ public double getDouble(int row, int col) {
releaseReference();
}
}
-
+
+ /**
+ * Returns the value at (row
, col
) as a double
.
+ *
+ *
If the value is null, then 0.0
is returned. If the
+ * type of column col
is a string type, then the result
+ * is the double
that is obtained by parsing the string value with
+ * strtod
. If the type of column col
is
+ * integral, then the result is the integer value casted to a double
.
+ * If the type of column col
is a blob type, then an
+ * {@link SQLiteException} is thrown.
+ */
private native double getDouble_native(int row, int col);
/**
* Returns a short for the given field.
* row is 0 based
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a short value for the given field
*/
@@ -424,8 +542,8 @@ public short getShort(int row, int col) {
/**
* Returns an int for the given field.
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return an int value for the given field
*/
@@ -437,12 +555,12 @@ public int getInt(int row, int col) {
releaseReference();
}
}
-
+
/**
* Returns a float for the given field.
* row is 0 based
- *
- * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
* @param col the column to read from
* @return a float value for the given field
*/
@@ -453,8 +571,8 @@ public float getFloat(int row, int col) {
} finally {
releaseReference();
}
- }
-
+ }
+
/**
* Clears out the existing contents of the window, making it safe to reuse
* for new data. Note that the number of columns in the window may NOT
@@ -463,7 +581,7 @@ public float getFloat(int row, int col) {
public void clear() {
acquireReference();
try {
- mStartPos = 0;
+ mStartPos = 0;
native_clear();
} finally {
releaseReference();
@@ -479,15 +597,18 @@ public void clear() {
public void close() {
releaseReference();
}
-
+
private native void close_native();
@Override
protected void finalize() {
// Just in case someone forgot to call close...
+ if (nWindow == 0) {
+ return;
+ }
close_native();
}
-
+
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
public CursorWindow createFromParcel(Parcel source) {
@@ -513,9 +634,9 @@ public void writeToParcel(Parcel dest, int flags) {
}
public CursorWindow(Parcel source,int foo) {
-
+
super(true);
-
+
IBinder nativeBinder = source.readStrongBinder();
mStartPos = source.readInt();
@@ -526,7 +647,8 @@ public CursorWindow(Parcel source,int foo) {
private native IBinder native_getBinder();
/** Does the native side initialization for an empty window */
- private native void native_init(boolean localOnly);
+ private native void native_init(boolean localOnly, long initialSize,
+ long growthPaddingSize, long maxSize);
/** Does the native side initialization with an existing binder from another process */
private native void native_init(IBinder nativeBinder);
@@ -534,6 +656,7 @@ public CursorWindow(Parcel source,int foo) {
@Override
protected void onAllReferencesReleased() {
close_native();
- super.onAllReferencesReleased();
+
+ super.onAllReferencesReleased();
}
}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindowAllocation.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindowAllocation.java
new file mode 100644
index 00000000..6b4c47f2
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindowAllocation.java
@@ -0,0 +1,7 @@
+package net.sqlcipher;
+
+public interface CursorWindowAllocation {
+ long getInitialAllocationSize();
+ long getGrowthPaddingSize();
+ long getMaxAllocationSize();
+}
diff --git a/src/net/sqlcipher/SQLException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.java
similarity index 59%
rename from src/net/sqlcipher/SQLException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.java
index 8c8c0373..aae6be0f 100644
--- a/src/net/sqlcipher/SQLException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.java
@@ -17,14 +17,23 @@
package net.sqlcipher;
/**
- * An exception that indicates there was an error with SQL parsing or execution.
+ * Extension of android.database.CursorWrapper to support getType() for API < 11.
*/
-public class SQLException extends RuntimeException
-{
- public SQLException() {}
+public class CursorWrapper extends android.database.CursorWrapper implements Cursor {
- public SQLException(String error)
- {
- super(error);
+ private final Cursor mCursor;
+
+ public CursorWrapper(Cursor cursor) {
+ super(cursor);
+ mCursor = cursor;
+ }
+
+ public int getType(int columnIndex) {
+ return mCursor.getType(columnIndex);
+ }
+
+ public Cursor getWrappedCursor() {
+ return mCursor;
}
}
+
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/CustomCursorWindowAllocation.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CustomCursorWindowAllocation.java
new file mode 100644
index 00000000..9575dafe
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CustomCursorWindowAllocation.java
@@ -0,0 +1,30 @@
+package net.sqlcipher;
+
+import net.sqlcipher.CursorWindowAllocation;
+
+public class CustomCursorWindowAllocation implements CursorWindowAllocation {
+
+ private long initialAllocationSize = 0L;
+ private long growthPaddingSize = 0L;
+ private long maxAllocationSize = 0L;
+
+ public CustomCursorWindowAllocation(long initialSize,
+ long growthPaddingSize,
+ long maxAllocationSize){
+ this.initialAllocationSize = initialSize;
+ this.growthPaddingSize = growthPaddingSize;
+ this.maxAllocationSize = maxAllocationSize;
+ }
+
+ public long getInitialAllocationSize() {
+ return initialAllocationSize;
+ }
+
+ public long getGrowthPaddingSize() {
+ return growthPaddingSize;
+ }
+
+ public long getMaxAllocationSize() {
+ return maxAllocationSize;
+ }
+}
diff --git a/src/net/sqlcipher/database/SQLiteException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseErrorHandler.java
similarity index 51%
rename from src/net/sqlcipher/database/SQLiteException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseErrorHandler.java
index 2c7f11a3..58096f1d 100644
--- a/src/net/sqlcipher/database/SQLiteException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseErrorHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package net.sqlcipher.database;
+package net.sqlcipher;
-import net.sqlcipher.*;
+import net.sqlcipher.database.SQLiteDatabase;
/**
- * A SQLite exception that indicates there was an error with SQL parsing or execution.
+ * An interface to let the apps define the actions to take when the following errors are detected
+ * database corruption
*/
-public class SQLiteException extends SQLException {
- public SQLiteException() {}
+public interface DatabaseErrorHandler {
- public SQLiteException(String error) {
- super(error);
- }
+ /**
+ * defines the method to be invoked when database corruption is detected.
+ * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
+ * is detected.
+ */
+ void onCorruption(SQLiteDatabase dbObj);
}
diff --git a/src/net/sqlcipher/DatabaseUtils.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseUtils.java
similarity index 87%
rename from src/net/sqlcipher/DatabaseUtils.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseUtils.java
index 34b9a220..a89bebab 100644
--- a/src/net/sqlcipher/DatabaseUtils.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseUtils.java
@@ -16,15 +16,7 @@
package net.sqlcipher;
-import android.database.Cursor;
-
-import net.sqlcipher.database.SQLiteAbortException;
-import net.sqlcipher.database.SQLiteConstraintException;
import net.sqlcipher.database.SQLiteDatabase;
-import net.sqlcipher.database.SQLiteDatabaseCorruptException;
-import net.sqlcipher.database.SQLiteDiskIOException;
-import net.sqlcipher.database.SQLiteException;
-import net.sqlcipher.database.SQLiteFullException;
import net.sqlcipher.database.SQLiteProgram;
import net.sqlcipher.database.SQLiteStatement;
@@ -34,10 +26,16 @@
import java.util.HashMap;
import java.util.Map;
-import org.apache.commons.codec.binary.Hex;
-
import android.content.ContentValues;
import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteAbortException;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
+import android.database.sqlite.SQLiteDiskIOException;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
import android.os.Parcel;
import android.text.TextUtils;
import android.util.Config;
@@ -75,7 +73,7 @@ public static final void writeExceptionToParcel(Parcel reply, Exception e) {
code = 3;
} else if (e instanceof SQLiteAbortException) {
code = 4;
- } else if (e instanceof SQLiteConstraintException) {
+ } else if (e instanceof android.database.sqlite.SQLiteConstraintException) {
code = 5;
} else if (e instanceof SQLiteDatabaseCorruptException) {
code = 6;
@@ -194,6 +192,37 @@ public static void bindObjectToProgram(SQLiteProgram prog, int index,
}
}
+ /**
+ * Returns data type of the given object's value.
+ *
+ * Returned values are
+ *
+ * - {@link Cursor#FIELD_TYPE_NULL}
+ * - {@link Cursor#FIELD_TYPE_INTEGER}
+ * - {@link Cursor#FIELD_TYPE_FLOAT}
+ * - {@link Cursor#FIELD_TYPE_STRING}
+ * - {@link Cursor#FIELD_TYPE_BLOB}
+ *
+ *
+ *
+ * @param obj the object whose value type is to be returned
+ * @return object value type
+ * @hide
+ */
+ public static int getTypeOfObject(Object obj) {
+ if (obj == null) {
+ return 0; /* Cursor.FIELD_TYPE_NULL */
+ } else if (obj instanceof byte[]) {
+ return 4; /* Cursor.FIELD_TYPE_BLOB */
+ } else if (obj instanceof Float || obj instanceof Double) {
+ return 2; /* Cursor.FIELD_TYPE_FLOAT */
+ } else if (obj instanceof Long || obj instanceof Integer) {
+ return 1; /* Cursor.FIELD_TYPE_INTEGER */
+ } else {
+ return 3; /* Cursor.FIELD_TYPE_STRING */
+ }
+ }
+
/**
* Appends an SQL string to the given StringBuilder, including the opening
* and closing single quotes. Any single quotes internal to sqlString will
@@ -294,10 +323,23 @@ public static String getCollationKey(String name) {
*/
public static String getHexCollationKey(String name) {
byte [] arr = getCollationKeyInBytes(name);
- char[] keys = Hex.encodeHex(arr);
+ char[] keys = encodeHex(arr, HEX_DIGITS_LOWER);
return new String(keys, 0, getKeyLen(arr) * 2);
}
+ private static final char[] HEX_DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ private static char[] encodeHex(final byte[] data, final char[] toDigits) {
+ final int l = data.length;
+ final char[] out = new char[l << 1];
+ // two characters form the hex value.
+ for (int i = 0, j = 0; i < l; i++) {
+ out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
+ out[j++] = toDigits[0x0F & data[i]];
+ }
+ return out;
+ }
+
private static int getKeyLen(byte[] arr) {
if (arr[arr.length - 1] != 0) {
return arr.length;
@@ -455,7 +497,7 @@ public static String dumpCurrentRowToString(Cursor cursor) {
*
* @param cursor The cursor to read from
* @param field The TEXT field to read
- * @param values The {@link ContentValues} to put the value into, with the field as the key
+ * @param values The ContentValues to put the value into, with the field as the key
*/
public static void cursorStringToContentValues(Cursor cursor, String field,
ContentValues values) {
@@ -480,7 +522,7 @@ public static void cursorStringToInsertHelper(Cursor cursor, String field,
*
* @param cursor The cursor to read from
* @param field The TEXT field to read
- * @param values The {@link ContentValues} to put the value into, with the field as the key
+ * @param values The ContentValues to put the value into, with the field as the key
* @param key The key to store the value with in the map
*/
public static void cursorStringToContentValues(Cursor cursor, String field,
@@ -493,7 +535,7 @@ public static void cursorStringToContentValues(Cursor cursor, String field,
*
* @param cursor The cursor to read from
* @param field The INTEGER field to read
- * @param values The {@link ContentValues} to put the value into, with the field as the key
+ * @param values The ContentValues to put the value into, with the field as the key
*/
public static void cursorIntToContentValues(Cursor cursor, String field, ContentValues values) {
cursorIntToContentValues(cursor, field, values, field);
@@ -504,7 +546,7 @@ public static void cursorIntToContentValues(Cursor cursor, String field, Content
*
* @param cursor The cursor to read from
* @param field The INTEGER field to read
- * @param values The {@link ContentValues} to put the value into, with the field as the key
+ * @param values The ContentValues to put the value into, with the field as the key
* @param key The key to store the value with in the map
*/
public static void cursorIntToContentValues(Cursor cursor, String field, ContentValues values,
@@ -522,7 +564,7 @@ public static void cursorIntToContentValues(Cursor cursor, String field, Content
*
* @param cursor The cursor to read from
* @param field The INTEGER field to read
- * @param values The {@link ContentValues} to put the value into, with the field as the key
+ * @param values The ContentValues to put the value into, with the field as the key
*/
public static void cursorLongToContentValues(Cursor cursor, String field, ContentValues values)
{
@@ -534,7 +576,7 @@ public static void cursorLongToContentValues(Cursor cursor, String field, Conten
*
* @param cursor The cursor to read from
* @param field The INTEGER field to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
* @param key The key to store the value with in the map
*/
public static void cursorLongToContentValues(Cursor cursor, String field, ContentValues values,
@@ -553,7 +595,7 @@ public static void cursorLongToContentValues(Cursor cursor, String field, Conten
*
* @param cursor The cursor to read from
* @param field The REAL field to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorDoubleToCursorValues(Cursor cursor, String field, ContentValues values)
{
@@ -565,7 +607,7 @@ public static void cursorDoubleToCursorValues(Cursor cursor, String field, Conte
*
* @param cursor The cursor to read from
* @param field The REAL field to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
* @param key The key to store the value with in the map
*/
public static void cursorDoubleToContentValues(Cursor cursor, String field,
@@ -582,7 +624,7 @@ public static void cursorDoubleToContentValues(Cursor cursor, String field,
* Read the entire contents of a cursor row and store them in a ContentValues.
*
* @param cursor the cursor to read from.
- * @param values the {@link ContentValues} to put the row into.
+ * @param values the ContentValues to put the row into.
*/
public static void cursorRowToContentValues(Cursor cursor, ContentValues values) {
AbstractWindowedCursor awc =
@@ -678,7 +720,7 @@ public static String stringForQuery(SQLiteStatement prog, String[] selectionArgs
*
* @param cursor The cursor to read from
* @param column The column to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorStringToContentValuesIfPresent(Cursor cursor, ContentValues values,
String column) {
@@ -694,7 +736,7 @@ public static void cursorStringToContentValuesIfPresent(Cursor cursor, ContentVa
*
* @param cursor The cursor to read from
* @param column The column to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorLongToContentValuesIfPresent(Cursor cursor, ContentValues values,
String column) {
@@ -710,7 +752,7 @@ public static void cursorLongToContentValuesIfPresent(Cursor cursor, ContentValu
*
* @param cursor The cursor to read from
* @param column The column to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorShortToContentValuesIfPresent(Cursor cursor, ContentValues values,
String column) {
@@ -726,7 +768,7 @@ public static void cursorShortToContentValuesIfPresent(Cursor cursor, ContentVal
*
* @param cursor The cursor to read from
* @param column The column to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorIntToContentValuesIfPresent(Cursor cursor, ContentValues values,
String column) {
@@ -742,7 +784,7 @@ public static void cursorIntToContentValuesIfPresent(Cursor cursor, ContentValue
*
* @param cursor The cursor to read from
* @param column The column to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorFloatToContentValuesIfPresent(Cursor cursor, ContentValues values,
String column) {
@@ -758,7 +800,7 @@ public static void cursorFloatToContentValuesIfPresent(Cursor cursor, ContentVal
*
* @param cursor The cursor to read from
* @param column The column to read
- * @param values The {@link ContentValues} to put the value into
+ * @param values The ContentValues to put the value into
*/
public static void cursorDoubleToContentValuesIfPresent(Cursor cursor, ContentValues values,
String column) {
@@ -1106,6 +1148,64 @@ public void close() {
}
}
+ public static void cursorFillWindow(final Cursor cursor,
+ int position, final android.database.CursorWindow window) {
+ if (position < 0 || position >= cursor.getCount()) {
+ return;
+ }
+ final int oldPos = cursor.getPosition();
+ final int numColumns = cursor.getColumnCount();
+ window.clear();
+ window.setStartPosition(position);
+ window.setNumColumns(numColumns);
+ if (cursor.moveToPosition(position)) {
+ do {
+ if (!window.allocRow()) {
+ break;
+ }
+ for (int i = 0; i < numColumns; i++) {
+ final int type = cursor.getType(i);
+ final boolean success;
+ switch (type) {
+ case Cursor.FIELD_TYPE_NULL:
+ success = window.putNull(position, i);
+ break;
+
+ case Cursor.FIELD_TYPE_INTEGER:
+ success = window.putLong(cursor.getLong(i), position, i);
+ break;
+
+ case Cursor.FIELD_TYPE_FLOAT:
+ success = window.putDouble(cursor.getDouble(i), position, i);
+ break;
+
+ case Cursor.FIELD_TYPE_BLOB: {
+ final byte[] value = cursor.getBlob(i);
+ success = value != null ? window.putBlob(value, position, i)
+ : window.putNull(position, i);
+ break;
+ }
+
+ default: // assume value is convertible to String
+ case Cursor.FIELD_TYPE_STRING: {
+ final String value = cursor.getString(i);
+ success = value != null ? window.putString(value, position, i)
+ : window.putNull(position, i);
+ break;
+ }
+ }
+ if (!success) {
+ window.freeLastRow();
+ break;
+ }
+ }
+ position += 1;
+ } while (cursor.moveToNext());
+ }
+ cursor.moveToPosition(oldPos);
+ }
+
+
/**
* Creates a db and populates it with the sql statements in sqlStatements.
*
@@ -1119,10 +1219,10 @@ public void close() {
/*
static public void createDbFromSqlStatements(
Context context, String dbName, int dbVersion, String sqlStatements) {
-
+
//TODO TODO TODO what needs ot happen here
SQLiteDatabase db = context.openOrCreateDatabase(dbName, 0, null);
-
+
// TODO: this is not quite safe since it assumes that all semicolons at the end of a line
// terminate statements. It is possible that a text field contains ;\n. We will have to fix
// this if that turns out to be a problem.
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultCursorWindowAllocation.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultCursorWindowAllocation.java
new file mode 100644
index 00000000..47b5f548
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultCursorWindowAllocation.java
@@ -0,0 +1,21 @@
+package net.sqlcipher;
+
+import net.sqlcipher.CursorWindowAllocation;
+
+public class DefaultCursorWindowAllocation implements CursorWindowAllocation {
+
+ private long initialAllocationSize = 1024 * 1024;
+ private long WindowAllocationUnbounded = 0;
+
+ public long getInitialAllocationSize() {
+ return initialAllocationSize;
+ }
+
+ public long getGrowthPaddingSize() {
+ return initialAllocationSize;
+ }
+
+ public long getMaxAllocationSize() {
+ return WindowAllocationUnbounded;
+ }
+}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultDatabaseErrorHandler.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultDatabaseErrorHandler.java
new file mode 100644
index 00000000..f9de1b52
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultDatabaseErrorHandler.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 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 net.sqlcipher;
+
+import java.io.File;
+import java.util.List;
+
+import net.sqlcipher.database.SQLiteDatabase;
+
+import android.database.sqlite.SQLiteException;
+import android.util.Log;
+import android.util.Pair;
+
+/**
+ * Default class used to define the actions to take when the database corruption is reported
+ * by sqlite.
+ *
+ * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used
+ * as the default {@link DatabaseErrorHandler}.
+ */
+public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
+
+ private final String TAG = getClass().getSimpleName();
+
+ /**
+ * defines the default method to be invoked when database corruption is detected.
+ * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
+ * is detected.
+ */
+ public void onCorruption(SQLiteDatabase dbObj) {
+ // NOTE: Unlike the AOSP, this version does NOT attempt to delete any attached databases.
+ // TBD: Are we really certain that the attached databases would really be corrupt?
+ Log.e(TAG, "Corruption reported by sqlite on database, deleting: " + dbObj.getPath());
+
+ if (dbObj.isOpen()) {
+ Log.e(TAG, "Database object for corrupted database is already open, closing");
+
+ try {
+ dbObj.close();
+ } catch (Exception e) {
+ /* ignored */
+ Log.e(TAG, "Exception closing Database object for corrupted database, ignored", e);
+ }
+ }
+
+ deleteDatabaseFile(dbObj.getPath());
+ }
+
+ private void deleteDatabaseFile(String fileName) {
+ if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
+ return;
+ }
+ Log.e(TAG, "deleting the database file: " + fileName);
+ try {
+ new File(fileName).delete();
+ } catch (Exception e) {
+ /* print warning and ignore exception */
+ Log.w(TAG, "delete failed: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/net/sqlcipher/IBulkCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/IBulkCursor.java
similarity index 100%
rename from src/net/sqlcipher/IBulkCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/IBulkCursor.java
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/InvalidRowColumnException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/InvalidRowColumnException.java
new file mode 100644
index 00000000..275b28d9
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/InvalidRowColumnException.java
@@ -0,0 +1,14 @@
+package net.sqlcipher;
+
+/**
+ * An exception that indicates there was an error accessing a specific row/column.
+ */
+public class InvalidRowColumnException extends RuntimeException
+{
+ public InvalidRowColumnException() {}
+
+ public InvalidRowColumnException(String error)
+ {
+ super(error);
+ }
+}
diff --git a/src/net/sqlcipher/MatrixCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.java
similarity index 98%
rename from src/net/sqlcipher/MatrixCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.java
index 140a291e..6ca0798c 100644
--- a/src/net/sqlcipher/MatrixCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.java
@@ -274,6 +274,11 @@ public double getDouble(int column) {
return Double.parseDouble(value.toString());
}
+ @Override
+ public int getType(int column) {
+ return DatabaseUtils.getTypeOfObject(get(column));
+ }
+
@Override
public boolean isNull(int column) {
return get(column) == null;
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/RowAllocationException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/RowAllocationException.java
new file mode 100644
index 00000000..a4680568
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/RowAllocationException.java
@@ -0,0 +1,15 @@
+package net.sqlcipher;
+
+/**
+ * An exception that indicates there was an error attempting to allocate a row
+ * for the CursorWindow.
+ */
+public class RowAllocationException extends RuntimeException
+{
+ public RowAllocationException() {}
+
+ public RowAllocationException(String error)
+ {
+ super(error);
+ }
+}
diff --git a/src/net/sqlcipher/StaleDataException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/StaleDataException.java
similarity index 100%
rename from src/net/sqlcipher/StaleDataException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/StaleDataException.java
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/UnknownTypeException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/UnknownTypeException.java
new file mode 100644
index 00000000..4da359ff
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/UnknownTypeException.java
@@ -0,0 +1,14 @@
+package net.sqlcipher;
+
+/**
+ * An exception that indicates an unknown type was returned.
+ */
+public class UnknownTypeException extends RuntimeException
+{
+ public UnknownTypeException() {}
+
+ public UnknownTypeException(String error)
+ {
+ super(error);
+ }
+}
diff --git a/src/net/sqlcipher/database/DatabaseObjectNotClosedException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/DatabaseObjectNotClosedException.java
similarity index 100%
rename from src/net/sqlcipher/database/DatabaseObjectNotClosedException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/DatabaseObjectNotClosedException.java
diff --git a/src/net/sqlcipher/database/SQLiteClosable.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteClosable.java
similarity index 100%
rename from src/net/sqlcipher/database/SQLiteClosable.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteClosable.java
diff --git a/src/net/sqlcipher/database/SQLiteCompiledSql.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.java
similarity index 88%
rename from src/net/sqlcipher/database/SQLiteCompiledSql.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.java
index 7302311a..52787c14 100644
--- a/src/net/sqlcipher/database/SQLiteCompiledSql.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.java
@@ -36,7 +36,7 @@
/**
* Native linkage, do not modify. This comes from the database.
*/
- /* package */ int nHandle = 0;
+ /* package */ long nHandle = 0;
/**
* Native linkage, do not modify. When non-0 this holds a reference to a valid
@@ -44,11 +44,10 @@
* checked in this class when the database lock is held to determine if there
* is a valid native-side program or not.
*/
- /* package */ int nStatement = 0;
+ /* package */ long nStatement = 0;
/** the following are for debugging purposes */
private String mSqlStmt = null;
- private Throwable mStackTrace = null;
/** when in cache and is in use, this member is set */
private boolean mInUse = false;
@@ -59,7 +58,6 @@
}
mDatabase = db;
mSqlStmt = sql;
- mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
this.nHandle = db.mNativeHandle;
compile(sql, true);
}
@@ -95,20 +93,15 @@ private void compile(String sql, boolean forceCompilation) {
}
}
- /* package */ void releaseSqlStatement() {
+ /* package */ synchronized void releaseSqlStatement() {
// Note that native_finalize() checks to make sure that nStatement is
// non-null before destroying it.
if (nStatement != 0) {
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
Log.v(TAG, "closed and deallocated DbObj (id#" + nStatement +")");
}
- try {
- mDatabase.lock();
- native_finalize();
- nStatement = 0;
- } finally {
- mDatabase.unlock();
- }
+ native_finalize();
+ nStatement = 0;
}
}
@@ -145,10 +138,6 @@ protected void finalize() throws Throwable {
if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) {
Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")");
}
- int len = mSqlStmt.length();
- Log.w(TAG, "Releasing statement in a finalizer. Please ensure " +
- "that you explicitly call close() on your cursor: " +
- mSqlStmt.substring(0, (len > 100) ? 100 : len), mStackTrace);
releaseSqlStatement();
} finally {
super.finalize();
diff --git a/src/net/sqlcipher/database/SQLiteContentHelper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.java
similarity index 99%
rename from src/net/sqlcipher/database/SQLiteContentHelper.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.java
index 09c9114e..3300b610 100644
--- a/src/net/sqlcipher/database/SQLiteContentHelper.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.java
@@ -61,7 +61,7 @@ public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, St
fd = (android.os.ParcelFileDescriptor)m.invoke(file);
} catch (Exception e) {
android.util.Log.i("SQLiteContentHelper", "SQLiteCursor.java: " + e);
- }
+ }
AssetFileDescriptor afd = new AssetFileDescriptor(fd, 0, file.length());
return afd;
} catch (IOException ex) {
@@ -95,7 +95,7 @@ private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String
}
MemoryFile file = new MemoryFile(null, bytes.length);
file.writeBytes(bytes, 0, 0, bytes.length);
-
+
// file.deactivate();
return file;
} finally {
diff --git a/src/net/sqlcipher/database/SQLiteCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursor.java
similarity index 78%
rename from src/net/sqlcipher/database/SQLiteCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursor.java
index 4f293519..a216d986 100644
--- a/src/net/sqlcipher/database/SQLiteCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursor.java
@@ -17,9 +17,10 @@
package net.sqlcipher.database;
import net.sqlcipher.AbstractWindowedCursor;
+import net.sqlcipher.BuildConfig;
import net.sqlcipher.CursorWindow;
-import net.sqlcipher.SQLException;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -27,6 +28,7 @@
import android.database.CharArrayBuffer;
import android.database.DataSetObserver;
+import android.database.SQLException;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
@@ -63,14 +65,18 @@ public class SQLiteCursor extends AbstractWindowedCursor {
/** The number of rows in the cursor */
private int mCount = NO_COUNT;
+ private int mCursorWindowCapacity = 0;
+
+ private boolean fillWindowForwardOnly = false;
+
/** A mapping of column names to column indices, to speed up lookups */
private Map mColumnNameMap;
/** Used to find out where a cursor was allocated in case it never got released. */
private Throwable mStackTrace;
-
- /**
- * mMaxRead is the max items that each cursor window reads
+
+ /**
+ * mMaxRead is the max items that each cursor window reads
* default to a very high value
*/
private int mMaxRead = Integer.MAX_VALUE;
@@ -78,13 +84,17 @@ public class SQLiteCursor extends AbstractWindowedCursor {
private int mCursorState = 0;
private ReentrantLock mLock = null;
private boolean mPendingData = false;
-
+
+ public void setFillWindowForwardOnly(boolean value) {
+ fillWindowForwardOnly = value;
+ }
+
/**
* support for a cursor variant that doesn't always read all results
- * initialRead is the initial number of items that cursor window reads
+ * initialRead is the initial number of items that cursor window reads
* if query contains more than this number of items, a thread will be
- * created and handle the left over items so that caller can show
- * results as soon as possible
+ * created and handle the left over items so that caller can show
+ * results as soon as possible
* @param initialRead initial number of items that cursor read
* @param maxRead leftover items read at maxRead items per time
* @hide
@@ -94,20 +104,20 @@ public void setLoadStyle(int initialRead, int maxRead) {
mInitialRead = initialRead;
mLock = new ReentrantLock(true);
}
-
+
private void queryThreadLock() {
if (mLock != null) {
- mLock.lock();
+ mLock.lock();
}
}
-
+
private void queryThreadUnlock() {
if (mLock != null) {
- mLock.unlock();
+ mLock.unlock();
}
}
-
-
+
+
/**
* @hide
*/
@@ -123,7 +133,7 @@ private void sendMessage() {
} else {
mPendingData = true;
}
-
+
}
public void run() {
// use cached mWindow, to avoid get null mWindow
@@ -131,6 +141,9 @@ public void run() {
Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND);
// the cursor's state doesn't change
while (true) {
+ if(mLock == null){
+ mLock = new ReentrantLock(true);
+ }
mLock.lock();
if (mCursorState != mThreadState) {
mLock.unlock();
@@ -143,7 +156,7 @@ public void run() {
if (count == NO_COUNT){
mCount += mMaxRead;
sendMessage();
- } else {
+ } else {
mCount = count;
sendMessage();
break;
@@ -158,33 +171,41 @@ public void run() {
mLock.unlock();
}
}
- }
+ }
}
-
-
+
+
/**
* @hide
- */
- protected class MainThreadNotificationHandler extends Handler {
+ */
+ protected static class MainThreadNotificationHandler extends Handler {
+
+ private final WeakReference wrappedCursor;
+
+ MainThreadNotificationHandler(SQLiteCursor cursor) {
+ wrappedCursor = new WeakReference(cursor);
+ }
+
public void handleMessage(Message msg) {
-
- notifyDataSetChange();
-
+ SQLiteCursor cursor = wrappedCursor.get();
+ if(cursor != null){
+ cursor.notifyDataSetChange();
+ }
}
}
-
+
/**
* @hide
*/
- protected MainThreadNotificationHandler mNotificationHandler;
-
+ protected MainThreadNotificationHandler mNotificationHandler;
+
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
- if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) &&
+ if ((Integer.MAX_VALUE != mMaxRead || Integer.MAX_VALUE != mInitialRead) &&
mNotificationHandler == null) {
queryThreadLock();
try {
- mNotificationHandler = new MainThreadNotificationHandler();
+ mNotificationHandler = new MainThreadNotificationHandler(this);
if (mPendingData) {
notifyDataSetChange();
mPendingData = false;
@@ -193,9 +214,9 @@ public void registerDataSetObserver(DataSetObserver observer) {
queryThreadUnlock();
}
}
-
+
}
-
+
/**
* Execute a query and provide access to its result set through a Cursor
* interface. For a query such as: {@code SELECT name, birth, phone FROM
@@ -232,11 +253,11 @@ public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
for (int i = 0; i < columnCount; i++) {
String columnName = mQuery.columnNameLocked(i);
mColumns[i] = columnName;
- if (Config.LOGV) {
+ if(BuildConfig.DEBUG){
Log.v("DatabaseWindow", "mColumns[" + i + "] is "
+ mColumns[i]);
}
-
+
// Make note of the row ID column index for quick access to it
if ("_id".equals(columnName)) {
mRowIdColumnIndex = i;
@@ -273,7 +294,8 @@ public int getCount() {
return mCount;
}
- private void fillWindow (int startPos) {
+ private void fillWindow (int requiredPos) {
+ int startPos = 0;
if (mWindow == null) {
// If there isn't a window set already it will only be accessed locally
mWindow = new CursorWindow(true /* the window is local only */);
@@ -286,14 +308,29 @@ private void fillWindow (int startPos) {
queryThreadUnlock();
}
}
+ if(fillWindowForwardOnly) {
+ startPos = requiredPos;
+ } else {
+ startPos = mCount == NO_COUNT
+ ? cursorPickFillWindowStartPosition(requiredPos, 0)
+ : cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
+ }
mWindow.setStartPosition(startPos);
+ mWindow.setRequiredPosition(requiredPos);
+ if(BuildConfig.DEBUG){
+ Log.v(TAG, String.format("Filling cursor window with start position:%d required position:%d",
+ startPos, requiredPos));
+ }
mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
+ if(mCursorWindowCapacity == 0) {
+ mCursorWindowCapacity = mWindow.getNumRows();
+ }
// return -1 means not finished
if (mCount == NO_COUNT){
mCount = startPos + mInitialRead;
Thread t = new Thread(new QueryThread(mCursorState), "query thread");
t.start();
- }
+ }
}
@Override
@@ -313,8 +350,10 @@ public int getColumnIndex(String columnName) {
final int periodIndex = columnName.lastIndexOf('.');
if (periodIndex != -1) {
Exception e = new Exception();
- Log.e(TAG, "requesting column name with table name -- " + columnName, e);
- columnName = columnName.substring(periodIndex + 1);
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "requesting column name with table name -- " + columnName, e);
+ columnName = columnName.substring(periodIndex + 1);
+ }
}
Integer i = mColumnNameMap.get(columnName);
@@ -335,10 +374,12 @@ public boolean deleteRow() {
// Only allow deletes if there is an ID column, and the ID has been read from it
if (mRowIdColumnIndex == -1 || mCurrentRowID == null) {
+ if(BuildConfig.DEBUG){
Log.e(TAG,
- "Could not delete row because either the row ID column is not available or it" +
- "has not been read.");
- return false;
+ "Could not delete row because either the row ID column is not available or it" +
+ "has not been read.");
+ }
+ return false;
}
boolean success;
@@ -402,9 +443,11 @@ public boolean supportsUpdates() {
public boolean commitUpdates(Map extends Long,
? extends Map> additionalValues) {
if (!supportsUpdates()) {
+ if(BuildConfig.DEBUG){
Log.e(TAG, "commitUpdates not supported on this cursor, did you "
- + "include the _id column?");
- return false;
+ + "include the _id column?");
+ }
+ return false;
}
/*
@@ -487,13 +530,13 @@ public boolean commitUpdates(Map extends Long,
}
private void deactivateCommon() {
- if (Config.LOGV) Log.v(TAG, "<<< Releasing cursor " + this);
+ if(BuildConfig.DEBUG) Log.v(TAG, "<<< Releasing cursor " + this);
mCursorState = 0;
if (mWindow != null) {
mWindow.close();
mWindow = null;
}
- if (Config.LOGV) Log.v("DatabaseWindow", "closing window in release()");
+ if(BuildConfig.DEBUG) Log.v("DatabaseWindow", "closing window in release()");
}
@Override
@@ -544,21 +587,21 @@ public boolean requery() {
mDatabase.unlock();
}
- if (Config.LOGV) {
- Log.v("DatabaseWindow", "closing window in requery()");
- Log.v(TAG, "--- Requery()ed cursor " + this + ": " + mQuery);
+ if(BuildConfig.DEBUG){
+ Log.v("DatabaseWindow", "closing window in requery()");
+ Log.v(TAG, "--- Requery()ed cursor " + this + ": " + mQuery);
}
boolean result = super.requery();
- if (Config.LOGV) {
- long timeEnd = System.currentTimeMillis();
- Log.v(TAG, "requery (" + (timeEnd - timeStart) + " ms): " + mDriver.toString());
+ if(BuildConfig.DEBUG){
+ long timeEnd = System.currentTimeMillis();
+ Log.v(TAG, "requery (" + (timeEnd - timeStart) + " ms): " + mDriver.toString());
}
return result;
}
@Override
- public void setWindow(CursorWindow window) {
+ public void setWindow(CursorWindow window) {
if (mWindow != null) {
mCursorState++;
queryThreadLock();
@@ -588,16 +631,18 @@ protected void finalize() {
// if the cursor hasn't been closed yet, close it first
if (mWindow != null) {
int len = mQuery.mSql.length();
- Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Finalizing a Cursor that has not been deactivated or closed. " +
"database = " + mDatabase.getPath() + ", table = " + mEditTable +
", query = " + mQuery.mSql.substring(0, (len > 100) ? 100 : len),
mStackTrace);
+ }
close();
SQLiteDebug.notifyActiveCursorFinalized();
} else {
- if (Config.LOGV) {
- Log.v(TAG, "Finalizing cursor on database = " + mDatabase.getPath() +
- ", table = " + mEditTable + ", query = " + mQuery.mSql);
+ if(BuildConfig.DEBUG) {
+ Log.v(TAG, "Finalizing cursor on database = " + mDatabase.getPath() +
+ ", table = " + mEditTable + ", query = " + mQuery.mSql);
}
}
} finally {
@@ -606,42 +651,50 @@ protected void finalize() {
}
-
+
@Override
- public void fillWindow(int startPos, android.database.CursorWindow window) {
-
- /*
- window.setStartPosition(startPos);
- mCount = mQuery.fillWindow((net.sqlcipher.database.CursorWindow)window, mInitialRead, 0);
- // return -1 means not finished
- if (mCount == NO_COUNT){
- mCount = startPos + mInitialRead;
- Thread t = new Thread(new QueryThread(mCursorState), "query thread");
- t.start();
- } */
-
- if (mWindow == null) {
- // If there isn't a window set already it will only be accessed locally
- mWindow = new CursorWindow(true /* the window is local only */);
- } else {
- mCursorState++;
- queryThreadLock();
- try {
- mWindow.clear();
- } finally {
- queryThreadUnlock();
- }
- }
- mWindow.setStartPosition(startPos);
- mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
- // return -1 means not finished
- if (mCount == NO_COUNT){
- mCount = startPos + mInitialRead;
- Thread t = new Thread(new QueryThread(mCursorState), "query thread");
- t.start();
+ public void fillWindow(int requiredPos, android.database.CursorWindow window) {
+ int startPos = 0;
+ if (mWindow == null) {
+ // If there isn't a window set already it will only be accessed locally
+ mWindow = new CursorWindow(true /* the window is local only */);
+ } else {
+ mCursorState++;
+ queryThreadLock();
+ try {
+ mWindow.clear();
+ } finally {
+ queryThreadUnlock();
}
-
-
+ }
+ if(fillWindowForwardOnly) {
+ startPos = requiredPos;
+ } else {
+ startPos = mCount == NO_COUNT
+ ? cursorPickFillWindowStartPosition(requiredPos, 0)
+ : cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
+ }
+ mWindow.setStartPosition(startPos);
+ mWindow.setRequiredPosition(requiredPos);
+ if(BuildConfig.DEBUG) {
+ Log.v(TAG, String.format("Filling cursor window with start position:%d required position:%d",
+ startPos, requiredPos));
+ }
+ mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
+ if(mCursorWindowCapacity == 0) {
+ mCursorWindowCapacity = mWindow.getNumRows();
+ }
+ // return -1 means not finished
+ if (mCount == NO_COUNT){
+ mCount = startPos + mInitialRead;
+ Thread t = new Thread(new QueryThread(mCursorState), "query thread");
+ t.start();
+ }
}
+ public int cursorPickFillWindowStartPosition(
+ int cursorPosition, int cursorWindowCapacity) {
+ return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
+ }
+
}
diff --git a/src/net/sqlcipher/database/SQLiteCursorDriver.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursorDriver.java
similarity index 95%
rename from src/net/sqlcipher/database/SQLiteCursorDriver.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursorDriver.java
index 89624d84..1ea66aba 100644
--- a/src/net/sqlcipher/database/SQLiteCursorDriver.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursorDriver.java
@@ -26,12 +26,12 @@
public interface SQLiteCursorDriver {
/**
* Executes the query returning a Cursor over the result set.
- *
+ *
* @param factory The CursorFactory to use when creating the Cursors, or
* null if standard SQLiteCursors should be returned.
* @return a Cursor over the result set
*/
- android.database.Cursor query(CursorFactory factory, String[] bindArgs);
+ Cursor query(CursorFactory factory, String[] bindArgs);
/**
* Called by a SQLiteCursor when it is released.
@@ -40,7 +40,7 @@ public interface SQLiteCursorDriver {
/**
* Called by a SQLiteCursor when it is requeryed.
- *
+ *
* @return The new count value.
*/
void cursorRequeried(android.database.Cursor cursor);
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabase.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabase.java
new file mode 100644
index 00000000..a4419653
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabase.java
@@ -0,0 +1,3272 @@
+/*
+ * Copyright (C) 2006 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 net.sqlcipher.database;
+
+import net.sqlcipher.BuildConfig;
+import net.sqlcipher.Cursor;
+import net.sqlcipher.CrossProcessCursorWrapper;
+import net.sqlcipher.DatabaseUtils;
+import net.sqlcipher.DatabaseErrorHandler;
+import net.sqlcipher.DefaultDatabaseErrorHandler;
+import net.sqlcipher.database.SQLiteStatement;
+import net.sqlcipher.database.SQLiteDebug.DbStats;
+import net.sqlcipher.database.SQLiteDatabaseHook;
+import net.sqlcipher.database.SQLiteQueryStats;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.regex.Pattern;
+import java.util.zip.ZipInputStream;
+
+import android.content.ContentValues;
+
+import android.content.Context;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
+import android.database.sqlite.SQLiteException;
+
+import android.os.CancellationSignal;
+import android.os.Debug;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Config;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.UnsupportedEncodingException;
+import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteQuery;
+
+/**
+ * Exposes methods to manage a SQLCipher database.
+ * SQLiteDatabase has methods to create, delete, execute SQL commands, and
+ * perform other common database management tasks.
+ *
A call to loadLibs(…)
should occur before attempting to
+ * create or open a database connection.
+ *
Database names must be unique within an application, not across all
+ * applications.
+ *
+ */
+public class SQLiteDatabase extends SQLiteClosable implements
+ SupportSQLiteDatabase {
+ private static final String TAG = "Database";
+ private static final int EVENT_DB_OPERATION = 52000;
+ private static final int EVENT_DB_CORRUPT = 75004;
+ private static final String KEY_ENCODING = "UTF-8";
+
+ private enum SQLiteDatabaseTransactionType {
+ Deferred,
+ Immediate,
+ Exclusive,
+ }
+
+ /**
+ * The version number of the SQLCipher for Android Java client library.
+ */
+ public static final String SQLCIPHER_ANDROID_VERSION = BuildConfig.VERSION_NAME;
+
+ // Stores reference to all databases opened in the current process.
+ // (The referent Object is not used at this time.)
+ // INVARIANT: Guarded by sActiveDatabases.
+ private static WeakHashMap sActiveDatabases =
+ new WeakHashMap();
+
+ public int status(int operation, boolean reset){
+ return native_status(operation, reset);
+ }
+
+ /**
+ * Change the password of the open database using sqlite3_rekey().
+ *
+ * @param password new database password
+ *
+ * @throws SQLiteException if there is an issue changing the password internally
+ * OR if the database is not open
+ *
+ * FUTURE @todo throw IllegalStateException if the database is not open and
+ * update the test suite
+ */
+ public void changePassword(String password) throws SQLiteException {
+ /* safeguard: */
+ if (!isOpen()) {
+ throw new SQLiteException("database not open");
+ }
+ if (password != null) {
+ byte[] keyMaterial = getBytes(password.toCharArray());
+ rekey(keyMaterial);
+ Arrays.fill(keyMaterial, (byte) 0);
+ }
+ }
+
+ /**
+ * Change the password of the open database using sqlite3_rekey().
+ *
+ * @param password new database password (char array)
+ *
+ * @throws SQLiteException if there is an issue changing the password internally
+ * OR if the database is not open
+ *
+ * FUTURE @todo throw IllegalStateException if the database is not open and
+ * update the test suite
+ */
+ public void changePassword(char[] password) throws SQLiteException {
+ /* safeguard: */
+ if (!isOpen()) {
+ throw new SQLiteException("database not open");
+ }
+ if (password != null) {
+ byte[] keyMaterial = getBytes(password);
+ rekey(keyMaterial);
+ Arrays.fill(keyMaterial, (byte) 0);
+ }
+ }
+
+ private static void loadICUData(Context context, File workingDir) {
+ OutputStream out = null;
+ ZipInputStream in = null;
+ File icuDir = new File(workingDir, "icu");
+ File icuDataFile = new File(icuDir, "icudt46l.dat");
+ try {
+ if(!icuDir.exists()) icuDir.mkdirs();
+ if(!icuDataFile.exists()) {
+ in = new ZipInputStream(context.getAssets().open("icudt46l.zip"));
+ in.getNextEntry();
+ out = new FileOutputStream(icuDataFile);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ }
+ }
+ catch (Exception ex) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Error copying icu dat file", ex);
+ }
+ if(icuDataFile.exists()){
+ icuDataFile.delete();
+ }
+ throw new RuntimeException(ex);
+ }
+ finally {
+ try {
+ if(in != null){
+ in.close();
+ }
+ if(out != null){
+ out.flush();
+ out.close();
+ }
+ } catch (IOException ioe){
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Error in closing streams IO streams after expanding ICU dat file", ioe);
+ }
+ throw new RuntimeException(ioe);
+ }
+ }
+ }
+
+ /**
+ * Implement this interface to provide custom strategy for loading jni libraries.
+ */
+ public interface LibraryLoader {
+ /**
+ * Load jni libraries by given names.
+ * Straightforward implementation will be calling {@link System#loadLibrary(String name)}
+ * for every provided library name.
+ *
+ * @param libNames library names that sqlcipher need to load
+ */
+ void loadLibraries(String... libNames);
+ }
+
+ /**
+ * Loads the native SQLCipher library into the application process.
+ */
+ public static synchronized void loadLibs (Context context) {
+ loadLibs(context, context.getFilesDir());
+ }
+
+ /**
+ * Loads the native SQLCipher library into the application process.
+ */
+ public static synchronized void loadLibs (Context context, File workingDir) {
+ loadLibs(context, workingDir, new LibraryLoader() {
+ @Override
+ public void loadLibraries(String... libNames) {
+ for (String libName : libNames) {
+ System.loadLibrary(libName);
+ }
+ }
+ });
+ }
+
+ /**
+ * Loads the native SQLCipher library into the application process.
+ */
+ public static synchronized void loadLibs(Context context, LibraryLoader libraryLoader) {
+ loadLibs(context, context.getFilesDir(), libraryLoader);
+ }
+
+ /**
+ * Loads the native SQLCipher library into the application process.
+ */
+ public static synchronized void loadLibs (Context context, File workingDir, LibraryLoader libraryLoader) {
+ libraryLoader.loadLibraries("sqlcipher");
+
+ // System.loadLibrary("stlport_shared");
+ // System.loadLibrary("sqlcipher_android");
+ // System.loadLibrary("database_sqlcipher");
+
+ // boolean systemICUFileExists = new File("/system/usr/icu/icudt46l.dat").exists();
+
+ // String icuRootPath = systemICUFileExists ? "/system/usr" : workingDir.getAbsolutePath();
+ // setICURoot(icuRootPath);
+ // if(!systemICUFileExists){
+ // loadICUData(context, workingDir);
+ // }
+ }
+
+ /**
+ * Algorithms used in ON CONFLICT clause
+ * http://www.sqlite.org/lang_conflict.html
+ */
+ /**
+ * When a constraint violation occurs, an immediate ROLLBACK occurs,
+ * thus ending the current transaction, and the command aborts with a
+ * return code of SQLITE_CONSTRAINT. If no transaction is active
+ * (other than the implied transaction that is created on every command)
+ * then this algorithm works the same as ABORT.
+ */
+ public static final int CONFLICT_ROLLBACK = 1;
+
+ /**
+ * When a constraint violation occurs,no ROLLBACK is executed
+ * so changes from prior commands within the same transaction
+ * are preserved. This is the default behavior.
+ */
+ public static final int CONFLICT_ABORT = 2;
+
+ /**
+ * When a constraint violation occurs, the command aborts with a return
+ * code SQLITE_CONSTRAINT. But any changes to the database that
+ * the command made prior to encountering the constraint violation
+ * are preserved and are not backed out.
+ */
+ public static final int CONFLICT_FAIL = 3;
+
+ /**
+ * When a constraint violation occurs, the one row that contains
+ * the constraint violation is not inserted or changed.
+ * But the command continues executing normally. Other rows before and
+ * after the row that contained the constraint violation continue to be
+ * inserted or updated normally. No error is returned.
+ */
+ public static final int CONFLICT_IGNORE = 4;
+
+ /**
+ * When a UNIQUE constraint violation occurs, the pre-existing rows that
+ * are causing the constraint violation are removed prior to inserting
+ * or updating the current row. Thus the insert or update always occurs.
+ * The command continues executing normally. No error is returned.
+ * If a NOT NULL constraint violation occurs, the NULL value is replaced
+ * by the default value for that column. If the column has no default
+ * value, then the ABORT algorithm is used. If a CHECK constraint
+ * violation occurs then the IGNORE algorithm is used. When this conflict
+ * resolution strategy deletes rows in order to satisfy a constraint,
+ * it does not invoke delete triggers on those rows.
+ * This behavior might change in a future release.
+ */
+ public static final int CONFLICT_REPLACE = 5;
+
+ /**
+ * use the following when no conflict action is specified.
+ */
+ public static final int CONFLICT_NONE = 0;
+ private static final String[] CONFLICT_VALUES = new String[]
+ {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
+
+ /**
+ * Maximum Length Of A LIKE Or GLOB Pattern
+ * The pattern matching algorithm used in the default LIKE and GLOB implementation
+ * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
+ * the pattern) for certain pathological cases. To avoid denial-of-service attacks
+ * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
+ * The default value of this limit is 50000. A modern workstation can evaluate
+ * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
+ * The denial of service problem only comes into play when the pattern length gets
+ * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
+ * are at most a few dozen bytes in length, paranoid application developers may
+ * want to reduce this parameter to something in the range of a few hundred
+ * if they know that external users are able to generate arbitrary patterns.
+ */
+ public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
+
+ /**
+ * Flag for {@link #openDatabase} to open the database for reading and writing.
+ * If the disk is full, this may fail even before you actually write anything.
+ *
+ * {@more} Note that the value of this flag is 0, so it is the default.
+ */
+ public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
+
+ /**
+ * Flag for {@link #openDatabase} to open the database for reading only.
+ * This is the only reliable way to open a database if the disk may be full.
+ */
+ public static final int OPEN_READONLY = 0x00000001; // update native code if changing
+
+ private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
+
+ /**
+ * Flag for {@link #openDatabase} to open the database without support for localized collators.
+ *
+ * {@more} This causes the collator LOCALIZED
not to be created.
+ * You must be consistent when using this flag to use the setting the database was
+ * created with. If this is set, {@link #setLocale} will do nothing.
+ */
+ public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
+
+ /**
+ * Flag for {@link #openDatabase} to create the database file if it does not already exist.
+ */
+ public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
+
+ /**
+ * SQLite memory database name
+ */
+ public static final String MEMORY = ":memory:";
+
+ /**
+ * Indicates whether the most-recently started transaction has been marked as successful.
+ */
+ private boolean mInnerTransactionIsSuccessful;
+
+ /**
+ * Valid during the life of a transaction, and indicates whether the entire transaction (the
+ * outer one and all of the inner ones) so far has been successful.
+ */
+ private boolean mTransactionIsSuccessful;
+
+ /**
+ * Valid during the life of a transaction.
+ */
+ private SQLiteTransactionListener mTransactionListener;
+
+ /** Synchronize on this when accessing the database */
+ private final ReentrantLock mLock = new ReentrantLock(true);
+
+ private long mLockAcquiredWallTime = 0L;
+ private long mLockAcquiredThreadTime = 0L;
+
+ // limit the frequency of complaints about each database to one within 20 sec
+ // unless run command adb shell setprop log.tag.Database VERBOSE
+ private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
+ /** If the lock is held this long then a warning will be printed when it is released. */
+ private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
+ private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
+ private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
+
+ private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
+
+ // The pattern we remove from database filenames before
+ // potentially logging them.
+ private static final Pattern EMAIL_IN_DB_PATTERN = Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
+
+ private long mLastLockMessageTime = 0L;
+
+ // Things related to query logging/sampling for debugging
+ // slow/frequent queries during development. Always log queries
+ // which take (by default) 500ms+; shorter queries are sampled
+ // accordingly. Commit statements, which are typically slow, are
+ // logged together with the most recently executed SQL statement,
+ // for disambiguation. The 500ms value is configurable via a
+ // SystemProperty, but developers actively debugging database I/O
+ // should probably use the regular log tunable,
+ // LOG_SLOW_QUERIES_PROPERTY, defined below.
+ private static int sQueryLogTimeInMillis = 0; // lazily initialized
+ private static final int QUERY_LOG_SQL_LENGTH = 64;
+ private static final String COMMIT_SQL = "COMMIT;";
+ private String mLastSqlStatement = null;
+
+ // String prefix for slow database query EventLog records that show
+ // lock acquistions of the database.
+ /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
+
+ /** Used by native code, do not rename */
+ /* package */ long mNativeHandle = 0;
+
+ /** Used to make temp table names unique */
+ /* package */ int mTempTableSequence = 0;
+
+ /** The path for the database file */
+ private String mPath;
+
+ /** The anonymized path for the database file for logging purposes */
+ private String mPathForLogs = null; // lazily populated
+
+ /** The flags passed to open/create */
+ private int mFlags;
+
+ /** The optional factory to use when creating new Cursors */
+ private CursorFactory mFactory;
+
+ private WeakHashMap mPrograms;
+
+ /**
+ * for each instance of this class, a cache is maintained to store
+ * the compiled query statement ids returned by sqlite database.
+ * key = sql statement with "?" for bind args
+ * value = {@link SQLiteCompiledSql}
+ * If an application opens the database and keeps it open during its entire life, then
+ * there will not be an overhead of compilation of sql statements by sqlite.
+ *
+ * why is this cache NOT static? because sqlite attaches compiledsql statements to the
+ * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
+ * invoked.
+ *
+ * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
+ * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
+ * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
+ */
+ /* package */ Map mCompiledQueries = new HashMap();
+ /**
+ * @hide
+ */
+ public static final int MAX_SQL_CACHE_SIZE = 250;
+ private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
+ private int mCacheFullWarnings;
+ private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
+
+ /** {@link DatabaseErrorHandler} to be used when SQLite returns any of the following errors
+ * Corruption
+ * */
+ private final DatabaseErrorHandler mErrorHandler;
+
+ /** maintain stats about number of cache hits and misses */
+ private int mNumCacheHits;
+ private int mNumCacheMisses;
+
+ /** the following 2 members maintain the time when a database is opened and closed */
+ private String mTimeOpened = null;
+ private String mTimeClosed = null;
+
+ /** Used to find out where this object was created in case it never got closed. */
+ private Throwable mStackTrace = null;
+
+ // System property that enables logging of slow queries. Specify the threshold in ms.
+ private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
+ private final int mSlowQueryThreshold;
+
+ /**
+ * @param closable
+ */
+ void addSQLiteClosable(SQLiteClosable closable) {
+ lock();
+ try {
+ mPrograms.put(closable, null);
+ } finally {
+ unlock();
+ }
+ }
+
+ void removeSQLiteClosable(SQLiteClosable closable) {
+ lock();
+ try {
+ mPrograms.remove(closable);
+ } finally {
+ unlock();
+ }
+ }
+
+ @Override
+ protected void onAllReferencesReleased() {
+ if (isOpen()) {
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ mTimeClosed = getTime();
+ }
+ dbclose();
+
+ synchronized (sActiveDatabases) {
+ sActiveDatabases.remove(this);
+ }
+ }
+ }
+
+ /**
+ * Attempts to release memory that SQLite holds but does not require to
+ * operate properly. Typically this memory will come from the page cache.
+ *
+ * @return the number of bytes actually released
+ */
+ static public native int releaseMemory();
+
+ /**
+ * Control whether or not the SQLiteDatabase is made thread-safe by using locks
+ * around critical sections. This is pretty expensive, so if you know that your
+ * DB will only be used by a single thread then you should set this to false.
+ * The default is true.
+ * @param lockingEnabled set to true to enable locks, false otherwise
+ */
+ public void setLockingEnabled(boolean lockingEnabled) {
+ mLockingEnabled = lockingEnabled;
+ }
+
+ /**
+ * If set then the SQLiteDatabase is made thread-safe by using locks
+ * around critical sections
+ */
+ private boolean mLockingEnabled = true;
+
+ /* package */
+ void onCorruption() {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Calling error handler for corrupt database (detected) " + mPath);
+ }
+
+ // NOTE: DefaultDatabaseErrorHandler deletes the corrupt file, EXCEPT for memory database
+ mErrorHandler.onCorruption(this);
+ }
+
+ /**
+ * Locks the database for exclusive access. The database lock must be held when
+ * touch the native sqlite3* object since it is single threaded and uses
+ * a polling lock contention algorithm. The lock is recursive, and may be acquired
+ * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
+ *
+ * @see #unlock()
+ */
+ /* package */ void lock() {
+ if (!mLockingEnabled) return;
+ mLock.lock();
+ if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
+ if (mLock.getHoldCount() == 1) {
+ // Use elapsed real-time since the CPU may sleep when waiting for IO
+ mLockAcquiredWallTime = SystemClock.elapsedRealtime();
+ mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
+ }
+ }
+ }
+
+ /**
+ * Locks the database for exclusive access. The database lock must be held when
+ * touch the native sqlite3* object since it is single threaded and uses
+ * a polling lock contention algorithm. The lock is recursive, and may be acquired
+ * multiple times by the same thread.
+ *
+ * @see #unlockForced()
+ */
+ private void lockForced() {
+ mLock.lock();
+ if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
+ if (mLock.getHoldCount() == 1) {
+ // Use elapsed real-time since the CPU may sleep when waiting for IO
+ mLockAcquiredWallTime = SystemClock.elapsedRealtime();
+ mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
+ }
+ }
+ }
+
+ /**
+ * Releases the database lock. This is a no-op if mLockingEnabled is false.
+ *
+ * @see #unlock()
+ */
+ /* package */ void unlock() {
+ if (!mLockingEnabled) return;
+ if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
+ if (mLock.getHoldCount() == 1) {
+ checkLockHoldTime();
+ }
+ }
+ mLock.unlock();
+ }
+
+ /**
+ * Releases the database lock.
+ *
+ * @see #unlockForced()
+ */
+ private void unlockForced() {
+ if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
+ if (mLock.getHoldCount() == 1) {
+ checkLockHoldTime();
+ }
+ }
+ mLock.unlock();
+ }
+
+ private void checkLockHoldTime() {
+ // Use elapsed real-time since the CPU may sleep when waiting for IO
+ long elapsedTime = SystemClock.elapsedRealtime();
+ long lockedTime = elapsedTime - mLockAcquiredWallTime;
+ if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
+ !Log.isLoggable(TAG, Log.VERBOSE) &&
+ (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
+ return;
+ }
+ if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
+ int threadTime = (int)
+ ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
+ if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
+ lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
+ mLastLockMessageTime = elapsedTime;
+ String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
+ + threadTime + "ms";
+ if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
+ if(BuildConfig.DEBUG){
+ Log.d(TAG, msg, new Exception());
+ }
+ } else {
+ if(BuildConfig.DEBUG){
+ Log.d(TAG, msg);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs a PRAGMA integrity_check; command against the database.
+ * @return true if the integrity check is ok, otherwise false
+ */
+ public boolean isDatabaseIntegrityOk() {
+ Pair result = getResultFromPragma("PRAGMA integrity_check;");
+ return result.first ? result.second.equals("ok") : result.first;
+ }
+
+ /**
+ * Returns a list of attached databases including the main database
+ * by executing PRAGMA database_list
+ * @return a list of pairs of database name and filename
+ */
+ public List> getAttachedDbs() {
+ return getAttachedDbs(this);
+ }
+
+ /**
+ * Sets the journal mode of the database to WAL
+ * @return true if successful, false otherwise
+ */
+ public boolean enableWriteAheadLogging() {
+ if(inTransaction()) {
+ String message = "Write Ahead Logging cannot be enabled while in a transaction";
+ throw new IllegalStateException(message);
+ }
+ List> attachedDbs = getAttachedDbs(this);
+ if(attachedDbs != null && attachedDbs.size() > 1) return false;
+ if(isReadOnly() || getPath().equals(MEMORY)) return false;
+ String command = "PRAGMA journal_mode = WAL;";
+ rawExecSQL(command);
+ return true;
+ }
+
+ /**
+ * Sets the journal mode of the database to DELETE (the default mode)
+ */
+ public void disableWriteAheadLogging() {
+ if(inTransaction()) {
+ String message = "Write Ahead Logging cannot be disabled while in a transaction";
+ throw new IllegalStateException(message);
+ }
+ String command = "PRAGMA journal_mode = DELETE;";
+ rawExecSQL(command);
+ }
+
+ /**
+ * @return true if the journal mode is set to WAL, otherwise false
+ */
+ public boolean isWriteAheadLoggingEnabled() {
+ Pair result = getResultFromPragma("PRAGMA journal_mode;");
+ return result.first ? result.second.equals("wal") : result.first;
+ }
+
+ /**
+ * Enables or disables foreign key constraints
+ * @param enable used to determine whether or not foreign key constraints are on
+ */
+ public void setForeignKeyConstraintsEnabled(boolean enable) {
+ if(inTransaction()) {
+ String message = "Foreign key constraints may not be changed while in a transaction";
+ throw new IllegalStateException(message);
+ }
+ String command = String.format("PRAGMA foreign_keys = %s;",
+ enable ? "ON" : "OFF");
+ execSQL(command);
+ }
+
+ /**
+ * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
+ * the work done in that transaction and all of the nested transactions will be committed or
+ * rolled back. The changes will be rolled back if any transaction is ended without being
+ * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
+ *
+ * Here is the standard idiom for transactions:
+ *
+ *
+ * db.beginTransaction();
+ * try {
+ * ...
+ * db.setTransactionSuccessful();
+ * } finally {
+ * db.endTransaction();
+ * }
+ *
+ *
+ * @throws IllegalStateException if the database is not open
+ */
+ public void beginTransaction() {
+ beginTransactionWithListener((SQLiteTransactionListener)null /* transactionStatusCallback */);
+ }
+
+ /**
+ * Begins a transaction in Exlcusive mode. Transactions can be nested. When
+ * the outer transaction is ended all of the work done in that transaction
+ * and all of the nested transactions will be committed or rolled back. The
+ * changes will be rolled back if any transaction is ended without being
+ * marked as clean (by calling setTransactionSuccessful). Otherwise they
+ * will be committed.
+ *
+ * Here is the standard idiom for transactions:
+ *
+ *
+ * db.beginTransactionWithListener(listener);
+ * try {
+ * ...
+ * db.setTransactionSuccessful();
+ * } finally {
+ * db.endTransaction();
+ * }
+ *
+ * @param transactionListener listener that should be notified when the transaction begins,
+ * commits, or is rolled back, either explicitly or by a call to
+ * {@link #yieldIfContendedSafely}.
+ *
+ * @throws IllegalStateException if the database is not open
+ */
+ public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
+ beginTransactionWithListenerInternal(transactionListener,
+ SQLiteDatabaseTransactionType.Exclusive);
+ }
+
+ /**
+ * Begins a transaction in Immediate mode
+ */
+ public void beginTransactionNonExclusive() {
+ beginTransactionWithListenerInternal(null,
+ SQLiteDatabaseTransactionType.Immediate);
+ }
+
+ /**
+ * Begins a transaction in Immediate mode
+ * @param transactionListener is the listener used to report transaction events
+ */
+ public void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener transactionListener) {
+ beginTransactionWithListenerInternal(transactionListener,
+ SQLiteDatabaseTransactionType.Immediate);
+ }
+
+ /**
+ * End a transaction. See beginTransaction for notes about how to use this and when transactions
+ * are committed and rolled back.
+ *
+ * @throws IllegalStateException if the database is not open or is not locked by the current thread
+ */
+ public void endTransaction() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ if (!mLock.isHeldByCurrentThread()) {
+ throw new IllegalStateException("no transaction pending");
+ }
+ try {
+ if (mInnerTransactionIsSuccessful) {
+ mInnerTransactionIsSuccessful = false;
+ } else {
+ mTransactionIsSuccessful = false;
+ }
+ if (mLock.getHoldCount() != 1) {
+ return;
+ }
+ RuntimeException savedException = null;
+ if (mTransactionListener != null) {
+ try {
+ if (mTransactionIsSuccessful) {
+ mTransactionListener.onCommit();
+ } else {
+ mTransactionListener.onRollback();
+ }
+ } catch (RuntimeException e) {
+ savedException = e;
+ mTransactionIsSuccessful = false;
+ }
+ }
+ if (mTransactionIsSuccessful) {
+ execSQL(COMMIT_SQL);
+ } else {
+ try {
+ execSQL("ROLLBACK;");
+ if (savedException != null) {
+ throw savedException;
+ }
+ } catch (SQLException e) {
+ if(BuildConfig.DEBUG){
+ Log.d(TAG, "exception during rollback, maybe the DB previously "
+ + "performed an auto-rollback");
+ }
+ }
+ }
+ } finally {
+ mTransactionListener = null;
+ unlockForced();
+ if(BuildConfig.DEBUG){
+ Log.v(TAG, "unlocked " + Thread.currentThread()
+ + ", holdCount is " + mLock.getHoldCount());
+ }
+ }
+ }
+
+ /**
+ * Marks the current transaction as successful. Do not do any more database work between
+ * calling this and calling endTransaction. Do as little non-database work as possible in that
+ * situation too. If any errors are encountered between this and endTransaction the transaction
+ * will still be committed.
+ *
+ * @throws IllegalStateException if the database is not open, the current thread is not in a transaction,
+ * or the transaction is already marked as successful.
+ */
+ public void setTransactionSuccessful() {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ if (!mLock.isHeldByCurrentThread()) {
+ throw new IllegalStateException("no transaction pending");
+ }
+ if (mInnerTransactionIsSuccessful) {
+ throw new IllegalStateException(
+ "setTransactionSuccessful may only be called once per call to beginTransaction");
+ }
+ mInnerTransactionIsSuccessful = true;
+ }
+
+ /**
+ * return true if there is a transaction pending
+ */
+ public boolean inTransaction() {
+ return mLock.getHoldCount() > 0;
+ }
+
+ /**
+ * Checks if the database lock is held by this thread.
+ *
+ * @return true, if this thread is holding the database lock.
+ */
+ public boolean isDbLockedByCurrentThread() {
+ return mLock.isHeldByCurrentThread();
+ }
+
+ /**
+ * Checks if the database is locked by another thread. This is
+ * just an estimate, since this status can change at any time,
+ * including after the call is made but before the result has
+ * been acted upon.
+ *
+ * @return true if the transaction was yielded, false if queue was empty or database was not open
+ */
+ public boolean isDbLockedByOtherThreads() {
+ return !mLock.isHeldByCurrentThread() && mLock.isLocked();
+ }
+
+ /**
+ * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+ * successful so far. Do not call setTransactionSuccessful before calling this. When this
+ * returns a new transaction will have been created but not marked as successful.
+ *
+ * @return true if the transaction was yielded
+ *
+ * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
+ * will not be yielded. Use yieldIfContendedSafely instead.
+ */
+ @Deprecated
+ public boolean yieldIfContended() {
+ /* safeguard: */
+ if (!isOpen()) return false;
+
+ return yieldIfContendedHelper(false /* do not check yielding */,
+ -1 /* sleepAfterYieldDelay */);
+ }
+
+ /**
+ * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+ * successful so far. Do not call setTransactionSuccessful before calling this. When this
+ * returns a new transaction will have been created but not marked as successful. This assumes
+ * that there are no nested transactions (beginTransaction has only been called once) and will
+ * throw an exception if that is not the case.
+ *
+ * @return true if the transaction was yielded, false if queue was empty or database was not open
+ */
+ public boolean yieldIfContendedSafely() {
+ /* safeguard: */
+ if (!isOpen()) return false;
+
+ return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
+ }
+
+ /**
+ * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+ * successful so far. Do not call setTransactionSuccessful before calling this. When this
+ * returns a new transaction will have been created but not marked as successful. This assumes
+ * that there are no nested transactions (beginTransaction has only been called once) and will
+ * throw an exception if that is not the case.
+ *
+ * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
+ * the lock was actually yielded. This will allow other background threads to make some
+ * more progress than they would if we started the transaction immediately.
+ *
+ * @return true if the transaction was yielded, false if queue was empty or database was not open
+ *
+ * @throws IllegalStateException if the database is locked more than once by the current thread
+ * @throws InterruptedException if the thread was interrupted while sleeping
+ */
+ public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
+ /* safeguard: */
+ if (!isOpen()) return false;
+
+ return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
+ }
+
+ private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
+ if (mLock.getQueueLength() == 0) {
+ // Reset the lock acquire time since we know that the thread was willing to yield
+ // the lock at this time.
+ mLockAcquiredWallTime = SystemClock.elapsedRealtime();
+ mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
+ return false;
+ }
+ setTransactionSuccessful();
+ SQLiteTransactionListener transactionListener = mTransactionListener;
+ endTransaction();
+ if (checkFullyYielded) {
+ if (this.isDbLockedByCurrentThread()) {
+ throw new IllegalStateException(
+ "Db locked more than once. yielfIfContended cannot yield");
+ }
+ }
+ if (sleepAfterYieldDelay > 0) {
+ // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
+ // check if anyone is using the database. If the database is not contended,
+ // retake the lock and return.
+ long remainingDelay = sleepAfterYieldDelay;
+ while (remainingDelay > 0) {
+ try {
+ Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
+ remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
+ } catch (InterruptedException e) {
+ Thread.interrupted();
+ }
+ remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
+ if (mLock.getQueueLength() == 0) {
+ break;
+ }
+ }
+ }
+ beginTransactionWithListener(transactionListener);
+ return true;
+ }
+
+ /** Maps table names to info about what to which _sync_time column to set
+ * to NULL on an update. This is used to support syncing. */
+ private final Map mSyncUpdateInfo =
+ new HashMap();
+
+ public Map getSyncedTables() {
+ synchronized(mSyncUpdateInfo) {
+ HashMap tables = new HashMap();
+ for (String table : mSyncUpdateInfo.keySet()) {
+ SyncUpdateInfo info = mSyncUpdateInfo.get(table);
+ if (info.deletedTable != null) {
+ tables.put(table, info.deletedTable);
+ }
+ }
+ return tables;
+ }
+ }
+
+ /**
+ * Internal class used to keep track what needs to be marked as changed
+ * when an update occurs. This is used for syncing, so the sync engine
+ * knows what data has been updated locally.
+ */
+ static private class SyncUpdateInfo {
+ /**
+ * Creates the SyncUpdateInfo class.
+ *
+ * @param masterTable The table to set _sync_time to NULL in
+ * @param deletedTable The deleted table that corresponds to the
+ * master table
+ * @param foreignKey The key that refers to the primary key in table
+ */
+ SyncUpdateInfo(String masterTable, String deletedTable,
+ String foreignKey) {
+ this.masterTable = masterTable;
+ this.deletedTable = deletedTable;
+ this.foreignKey = foreignKey;
+ }
+
+ /** The table containing the _sync_time column */
+ String masterTable;
+
+ /** The deleted table that corresponds to the master table */
+ String deletedTable;
+
+ /** The key in the local table the row in table. It may be _id, if table
+ * is the local table. */
+ String foreignKey;
+ }
+
+ /**
+ * Used to allow returning sub-classes of {@link Cursor} when calling query.
+ */
+ public interface CursorFactory {
+ /**
+ * See
+ * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
+ * String, SQLiteQuery)}.
+ */
+ public Cursor newCursor(SQLiteDatabase db,
+ SQLiteCursorDriver masterQuery, String editTable,
+ SQLiteQuery query);
+ }
+
+ /**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, String password, CursorFactory factory, int flags) {
+ return openDatabase(path, password, factory, flags, null);
+ }
+
+ /**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
+ *
+ * Sets the locale of the database to the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file (char array)
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, char[] password, CursorFactory factory, int flags) {
+ return openDatabase(path, password, factory, flags, null, null);
+ }
+
+ /**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
+ * with optional hook to run on pre/post key events.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ * @param hook to run on pre/post key events
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, String password, CursorFactory factory, int flags, SQLiteDatabaseHook hook) {
+ return openDatabase(path, password, factory, flags, hook, null);
+ }
+
+ /**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
+ * with optional hook to run on pre/post key events.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file (char array)
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ * @param hook to run on pre/post key events (may be null)
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, char[] password, CursorFactory factory, int flags, SQLiteDatabaseHook hook) {
+ return openDatabase(path, password, factory, flags, hook, null);
+ }
+
+ /**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
+ * with optional hook to run on pre/post key events.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ * @param hook to run on pre/post key events
+ * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
+ * corruption (or null for default).
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, String password, CursorFactory factory, int flags,
+ SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
+ return openDatabase(path, password == null ? null : password.toCharArray(), factory, flags, hook, errorHandler);
+ }
+
+/**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
+ * with optional hook to run on pre/post key events.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file (char array)
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ * @param hook to run on pre/post key events (may be null)
+ * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
+ * corruption (or null for default).
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, char[] password, CursorFactory factory, int flags,
+ SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
+ byte[] keyMaterial = getBytes(password);
+ return openDatabase(path, keyMaterial, factory, flags, hook, errorHandler);
+ }
+
+ /**
+ * Open the database according to the flags {@link #OPEN_READWRITE}
+ * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}
+ * with optional hook to run on pre/post key events.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path to database file to open and/or create
+ * @param password to use to open and/or create database file (byte array)
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called, or null for default
+ * @param flags to control database access mode and other options
+ * @param hook to run on pre/post key events (may be null)
+ * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
+ * corruption (or null for default).
+ *
+ * @return the newly opened database
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public static SQLiteDatabase openDatabase(String path, byte[] password, CursorFactory factory, int flags,
+ SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
+ SQLiteDatabase sqliteDatabase = null;
+ DatabaseErrorHandler myErrorHandler = (errorHandler != null) ? errorHandler : new DefaultDatabaseErrorHandler();
+
+ try {
+ // Open the database.
+ sqliteDatabase = new SQLiteDatabase(path, factory, flags, myErrorHandler);
+ sqliteDatabase.openDatabaseInternal(password, hook);
+ } catch (SQLiteDatabaseCorruptException e) {
+ // Try to recover from this, if possible.
+ // FUTURE TBD: should we consider this for other open failures?
+
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Calling error handler for corrupt database " + path, e);
+ }
+
+ // NOTE: if this errorHandler.onCorruption() throws the exception _should_
+ // bubble back to the original caller.
+ // DefaultDatabaseErrorHandler deletes the corrupt file, EXCEPT for memory database
+ myErrorHandler.onCorruption(sqliteDatabase);
+
+ // try *once* again:
+ sqliteDatabase = new SQLiteDatabase(path, factory, flags, myErrorHandler);
+ sqliteDatabase.openDatabaseInternal(password, hook);
+ }
+
+ if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
+ sqliteDatabase.enableSqlTracing(path);
+ }
+ if (SQLiteDebug.DEBUG_SQL_TIME) {
+ sqliteDatabase.enableSqlProfiling(path);
+ }
+
+ synchronized (sActiveDatabases) {
+ sActiveDatabases.put(sqliteDatabase, null);
+ }
+
+ return sqliteDatabase;
+ }
+
+ /**
+ * Equivalent to openDatabase(file.getPath(), password, factory, CREATE_IF_NECESSARY, databaseHook).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(File file, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
+ return openOrCreateDatabase(file, password, factory, databaseHook, null);
+ }
+
+ /**
+ * Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(File file, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
+ DatabaseErrorHandler errorHandler) {
+ return openOrCreateDatabase(file == null ? null : file.getPath(), password, factory, databaseHook, errorHandler);
+ }
+
+ /**
+ * Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook);
+ }
+
+ public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
+ DatabaseErrorHandler errorHandler) {
+ return openDatabase(path, password == null ? null : password.toCharArray(), factory, CREATE_IF_NECESSARY, databaseHook, errorHandler);
+ }
+
+ public static SQLiteDatabase openOrCreateDatabase(String path, char[] password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook);
+ }
+
+ public static SQLiteDatabase openOrCreateDatabase(String path, char[] password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
+ DatabaseErrorHandler errorHandler) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook, errorHandler);
+ }
+
+ public static SQLiteDatabase openOrCreateDatabase(String path, byte[] password, CursorFactory factory, SQLiteDatabaseHook databaseHook) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook, null);
+ }
+
+ public static SQLiteDatabase openOrCreateDatabase(String path, byte[] password, CursorFactory factory, SQLiteDatabaseHook databaseHook,
+ DatabaseErrorHandler errorHandler) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, databaseHook, errorHandler);
+ }
+
+ /**
+ * Equivalent to openDatabase(file.getPath(), password, factory, CREATE_IF_NECESSARY).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(File file, String password, CursorFactory factory) {
+ return openOrCreateDatabase(file, password, factory, null);
+ }
+
+ /**
+ * Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(String path, String password, CursorFactory factory) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, null);
+ }
+
+ /**
+ * Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(String path, char[] password, CursorFactory factory) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, null);
+ }
+
+ /**
+ * Equivalent to openDatabase(path, password, factory, CREATE_IF_NECESSARY).
+ */
+ public static SQLiteDatabase openOrCreateDatabase(String path, byte[] password, CursorFactory factory) {
+ return openDatabase(path, password, factory, CREATE_IF_NECESSARY, null, null);
+ }
+
+ /**
+ * Create a memory backed SQLite database. Its contents will be destroyed
+ * when the database is closed.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called
+ * @param password to use to open and/or create database file
+ *
+ * @return a SQLiteDatabase object, or null if the database can't be created
+ *
+ * @throws SQLiteException if the database cannot be opened
+ */
+ public static SQLiteDatabase create(CursorFactory factory, String password) {
+ // This is a magic string with special meaning for SQLite.
+ return openDatabase(MEMORY, password == null ? null : password.toCharArray(), factory, CREATE_IF_NECESSARY);
+ }
+
+ /**
+ * Create a memory backed SQLite database. Its contents will be destroyed
+ * when the database is closed.
+ *
+ * Sets the locale of the database to the the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param factory an optional factory class that is called to instantiate a
+ * cursor when query is called
+ * @param password to use to open and/or create database file (char array)
+ *
+ * @return a SQLiteDatabase object, or null if the database can't be created
+ *
+ * @throws SQLiteException if the database cannot be opened
+ */
+ public static SQLiteDatabase create(CursorFactory factory, char[] password) {
+ return openDatabase(MEMORY, password, factory, CREATE_IF_NECESSARY);
+ }
+
+
+ /**
+ * Close the database.
+ */
+ public void close() {
+
+ if (!isOpen()) {
+ return; // already closed
+ }
+ lock();
+ try {
+ closeClosable();
+ // close this database instance - regardless of its reference count value
+ onAllReferencesReleased();
+ } finally {
+ unlock();
+ }
+ }
+
+ private void closeClosable() {
+ /* deallocate all compiled sql statement objects from mCompiledQueries cache.
+ * this should be done before de-referencing all {@link SQLiteClosable} objects
+ * from this database object because calling
+ * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
+ * to be closed. sqlite doesn't let a database close if there are
+ * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
+ */
+ deallocCachedSqlStatements();
+
+ Iterator> iter = mPrograms.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry entry = iter.next();
+ SQLiteClosable program = entry.getKey();
+ if (program != null) {
+ program.onAllReferencesReleasedFromContainer();
+ }
+ }
+ }
+
+ /**
+ * Native call to close the database.
+ */
+ private native void dbclose();
+
+ /**
+ * Gets the database version.
+ *
+ * @return the database version
+ *
+ * @throws IllegalStateException if the database is not open
+ */
+ public int getVersion() {
+ SQLiteStatement prog = null;
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ prog = new SQLiteStatement(this, "PRAGMA user_version;");
+ long version = prog.simpleQueryForLong();
+ return (int) version;
+ } finally {
+ if (prog != null) prog.close();
+ unlock();
+ }
+ }
+
+ /**
+ * Sets the database version.
+ *
+ * @param version the new database version
+ *
+ * @throws SQLiteException if there is an issue executing the sql internally
+ * @throws IllegalStateException if the database is not open
+ */
+ public void setVersion(int version) {
+ execSQL("PRAGMA user_version = " + version);
+ }
+
+ /**
+ * Returns the maximum size the database may grow to.
+ *
+ * @return the new maximum database size
+ */
+ public long getMaximumSize() {
+ SQLiteStatement prog = null;
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ prog = new SQLiteStatement(this,
+ "PRAGMA max_page_count;");
+ long pageCount = prog.simpleQueryForLong();
+ return pageCount * getPageSize();
+ } finally {
+ if (prog != null) prog.close();
+ unlock();
+ }
+ }
+
+ /**
+ * Sets the maximum size the database will grow to. The maximum size cannot
+ * be set below the current size.
+ *
+ * @param numBytes the maximum database size, in bytes
+ * @return the new maximum database size
+ */
+ public long setMaximumSize(long numBytes) {
+ SQLiteStatement prog = null;
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ long pageSize = getPageSize();
+ long numPages = numBytes / pageSize;
+ // If numBytes isn't a multiple of pageSize, bump up a page
+ if ((numBytes % pageSize) != 0) {
+ numPages++;
+ }
+ prog = new SQLiteStatement(this,
+ "PRAGMA max_page_count = " + numPages);
+ long newPageCount = prog.simpleQueryForLong();
+ return newPageCount * pageSize;
+ } finally {
+ if (prog != null) prog.close();
+ unlock();
+ }
+ }
+
+ /**
+ * Returns the current database page size, in bytes.
+ *
+ * @return the database page size, in bytes
+ */
+ public long getPageSize() {
+ SQLiteStatement prog = null;
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ prog = new SQLiteStatement(this,
+ "PRAGMA page_size;");
+ long size = prog.simpleQueryForLong();
+ return size;
+ } finally {
+ if (prog != null) prog.close();
+ unlock();
+ }
+ }
+
+ /**
+ * Sets the database page size. The page size must be a power of two. This
+ * method does not work if any data has been written to the database file,
+ * and must be called right after the database has been created.
+ *
+ * @param numBytes the database page size, in bytes
+ */
+ public void setPageSize(long numBytes) {
+ execSQL("PRAGMA page_size = " + numBytes);
+ }
+
+ /**
+ * Mark this table as syncable. When an update occurs in this table the
+* _sync_dirty field will be set to ensure proper syncing operation.
+ *
+ * @param table the table to mark as syncable
+ * @param deletedTable The deleted table that corresponds to the
+ * syncable table
+ *
+ * @throws SQLiteException if there is an issue executing the sql to mark the table as syncable
+ * OR if the database is not open
+ *
+ * FUTURE @todo throw IllegalStateException if the database is not open and
+ * update the test suite
+ *
+ * NOTE: This method was deprecated by the AOSP in Android API 11.
+ */
+ public void markTableSyncable(String table, String deletedTable) {
+ /* safeguard: */
+ if (!isOpen()) {
+ throw new SQLiteException("database not open");
+ }
+
+ markTableSyncable(table, "_id", table, deletedTable);
+ }
+
+ /**
+ * Mark this table as syncable, with the _sync_dirty residing in another
+ * table. When an update occurs in this table the _sync_dirty field of the
+ * row in updateTable with the _id in foreignKey will be set to
+ * ensure proper syncing operation.
+ *
+ * @param table an update on this table will trigger a sync time removal
+ * @param foreignKey this is the column in table whose value is an _id in
+ * updateTable
+ * @param updateTable this is the table that will have its _sync_dirty
+ *
+ * @throws SQLiteException if there is an issue executing the sql to mark the table as syncable
+ *
+ * FUTURE @todo throw IllegalStateException if the database is not open and
+ * update the test suite
+ *
+ * NOTE: This method was deprecated by the AOSP in Android API 11.
+ */
+ public void markTableSyncable(String table, String foreignKey,
+ String updateTable) {
+ /* safeguard: */
+ if (!isOpen()) {
+ throw new SQLiteException("database not open");
+ }
+
+ markTableSyncable(table, foreignKey, updateTable, null);
+ }
+
+ /**
+ * Mark this table as syncable, with the _sync_dirty residing in another
+ * table. When an update occurs in this table the _sync_dirty field of the
+ * row in updateTable with the _id in foreignKey will be set to
+ * ensure proper syncing operation.
+ *
+ * @param table an update on this table will trigger a sync time removal
+ * @param foreignKey this is the column in table whose value is an _id in
+ * updateTable
+ * @param updateTable this is the table that will have its _sync_dirty
+ * @param deletedTable The deleted table that corresponds to the
+ * updateTable
+ *
+ * @throws SQLiteException if there is an issue executing the sql
+ */
+ private void markTableSyncable(String table, String foreignKey,
+ String updateTable, String deletedTable) {
+ lock();
+ try {
+ native_execSQL("SELECT _sync_dirty FROM " + updateTable
+ + " LIMIT 0");
+ native_execSQL("SELECT " + foreignKey + " FROM " + table
+ + " LIMIT 0");
+ } finally {
+ unlock();
+ }
+
+ SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
+ foreignKey);
+ synchronized (mSyncUpdateInfo) {
+ mSyncUpdateInfo.put(table, info);
+ }
+ }
+
+ /**
+ * Call for each row that is updated in a cursor.
+ *
+ * @param table the table the row is in
+ * @param rowId the row ID of the updated row
+ */
+ /* package */ void rowUpdated(String table, long rowId) {
+ SyncUpdateInfo info;
+ synchronized (mSyncUpdateInfo) {
+ info = mSyncUpdateInfo.get(table);
+ }
+ if (info != null) {
+ execSQL("UPDATE " + info.masterTable
+ + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
+ + " FROM " + table + " WHERE _id=" + rowId + ")");
+ }
+ }
+
+ /**
+ * Finds the name of the first table, which is editable.
+ *
+ * @param tables a list of tables
+ * @return the first table listed
+ */
+ public static String findEditTable(String tables) {
+ if (!TextUtils.isEmpty(tables)) {
+ // find the first word terminated by either a space or a comma
+ int spacepos = tables.indexOf(' ');
+ int commapos = tables.indexOf(',');
+
+ if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
+ return tables.substring(0, spacepos);
+ } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
+ return tables.substring(0, commapos);
+ }
+ return tables;
+ } else {
+ throw new IllegalStateException("Invalid tables");
+ }
+ }
+
+ /**
+ * Compiles an SQL statement into a reusable pre-compiled statement object.
+ * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
+ * statement and fill in those values with {@link SQLiteProgram#bindString}
+ * and {@link SQLiteProgram#bindLong} each time you want to run the
+ * statement. Statements may not return result sets larger than 1x1.
+ *
+ * @param sql The raw SQL statement, may contain ? for unknown values to be
+ * bound later.
+ *
+ * @return A pre-compiled {@link SQLiteStatement} object. Note that
+ * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public SQLiteStatement compileStatement(String sql) throws SQLException {
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ return new SQLiteStatement(this, sql);
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Query the given URL, returning a {@link Cursor} over the result set.
+ *
+ * @param distinct true if you want each row to be unique, false otherwise.
+ * @param table The table name to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given table.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLiteException if there is an issue executing the sql or the SQL string is invalid
+ * @throws IllegalStateException if the database is not open
+ *
+ * @see Cursor
+ */
+ public Cursor query(boolean distinct, String table, String[] columns,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String orderBy, String limit) {
+ return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
+ groupBy, having, orderBy, limit);
+ }
+
+ /**
+ * Query the given URL, returning a {@link Cursor} over the result set.
+ *
+ * @param cursorFactory the cursor factory to use, or null for the default factory
+ * @param distinct true if you want each row to be unique, false otherwise.
+ * @param table The table name to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given table.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @see Cursor
+ */
+ public Cursor queryWithFactory(CursorFactory cursorFactory,
+ boolean distinct, String table, String[] columns,
+ String selection, String[] selectionArgs, String groupBy,
+ String having, String orderBy, String limit) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ String sql = SQLiteQueryBuilder.buildQueryString(
+ distinct, table, columns, selection, groupBy, having, orderBy, limit);
+
+ return rawQueryWithFactory(
+ cursorFactory, sql, selectionArgs, findEditTable(table));
+ }
+
+ /**
+ * Query the given table, returning a {@link Cursor} over the result set.
+ *
+ * @param table The table name to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given table.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLiteException if there is an issue executing the sql or the SQL string is invalid
+ * @throws IllegalStateException if the database is not open
+ *
+ * @see Cursor
+ */
+ public Cursor query(String table, String[] columns, String selection,
+ String[] selectionArgs, String groupBy, String having,
+ String orderBy) {
+
+ return query(false, table, columns, selection, selectionArgs, groupBy,
+ having, orderBy, null /* limit */);
+ }
+
+ /**
+ * Query the given table, returning a {@link Cursor} over the result set.
+ *
+ * @param table The table name to compile the query against.
+ * @param columns A list of which columns to return. Passing null will
+ * return all columns, which is discouraged to prevent reading
+ * data from storage that isn't going to be used.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given table.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param groupBy A filter declaring how to group rows, formatted as an SQL
+ * GROUP BY clause (excluding the GROUP BY itself). Passing null
+ * will cause the rows to not be grouped.
+ * @param having A filter declare which row groups to include in the cursor,
+ * if row grouping is being used, formatted as an SQL HAVING
+ * clause (excluding the HAVING itself). Passing null will cause
+ * all row groups to be included, and is required when row
+ * grouping is not being used.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @param limit Limits the number of rows returned by the query,
+ * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLiteException if there is an issue executing the sql or the SQL string is invalid
+ * @throws IllegalStateException if the database is not open
+ *
+ * @see Cursor
+ */
+ public Cursor query(String table, String[] columns, String selection,
+ String[] selectionArgs, String groupBy, String having,
+ String orderBy, String limit) {
+
+ return query(false, table, columns, selection, selectionArgs, groupBy,
+ having, orderBy, limit);
+ }
+
+ /**
+ * Runs the provided SQL and returns a {@link Cursor} over the result set.
+ *
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param selectionArgs You may include ?s in where clause in the query,
+ * which will be replaced by the values from selectionArgs. The
+ * values will be bound as Strings.
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLiteException if there is an issue executing the sql or the SQL string is invalid
+ * @throws IllegalStateException if the database is not open
+ */
+ public Cursor rawQuery(String sql, String[] selectionArgs) {
+ return rawQueryWithFactory(null, sql, selectionArgs, null);
+ }
+
+ /**
+ * Determines the total size in bytes of the query results, and the largest
+ * single row in bytes for the query.
+ *
+ * @param sql the SQL query. The SQL string must a SELECT statement
+ * @param args the argments to bind to the query
+ *
+ * @return A {@link SQLiteQueryStats} based the provided SQL query.
+ */
+ public SQLiteQueryStats getQueryStats(String sql, Object[] args){
+ long totalPayload = 0L;
+ long largestIndividualPayload = 0L;
+ try {
+ String query = String.format("CREATE TABLE tempstat AS %s", sql);
+ execSQL(query, args);
+ Cursor cursor = rawQuery("SELECT sum(payload) FROM dbstat WHERE name = 'tempstat';", new Object[]{});
+ if(cursor == null) return new SQLiteQueryStats(totalPayload, largestIndividualPayload);
+ cursor.moveToFirst();
+ totalPayload = cursor.getLong(0);
+ cursor.close();
+ cursor = rawQuery("SELECT max(mx_payload) FROM dbstat WHERE name = 'tempstat';", new Object[]{});
+ if(cursor == null) return new SQLiteQueryStats(totalPayload, largestIndividualPayload);
+ cursor.moveToFirst();
+ largestIndividualPayload = cursor.getLong(0);
+ cursor.close();
+ execSQL("DROP TABLE tempstat;");
+ } catch(SQLiteException ex) {
+ execSQL("DROP TABLE IF EXISTS tempstat;");
+ throw ex;
+ }
+ return new SQLiteQueryStats(totalPayload, largestIndividualPayload);
+ }
+
+ /**
+ * Runs the provided SQL and returns a {@link Cursor} over the result set.
+ *
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param args You may include ?s in where clause in the query,
+ * which will be replaced by the values from args. The
+ * values will be bound by their type.
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLiteException if there is an issue executing the sql or the SQL string is invalid
+ * @throws IllegalStateException if the database is not open
+ */
+ public Cursor rawQuery(String sql, Object[] args) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ long timeStart = 0;
+ if (Config.LOGV || mSlowQueryThreshold != -1) {
+ timeStart = System.currentTimeMillis();
+ }
+ SQLiteDirectCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, null);
+ Cursor cursor = null;
+ try {
+ cursor = driver.query(mFactory, args);
+ } finally {
+ if (Config.LOGV || mSlowQueryThreshold != -1) {
+ // Force query execution
+ int count = -1;
+ if (cursor != null) {
+ count = cursor.getCount();
+ }
+
+ long duration = System.currentTimeMillis() - timeStart;
+
+ if (BuildConfig.DEBUG || duration >= mSlowQueryThreshold) {
+ Log.v(TAG,
+ "query (" + duration + " ms): " + driver.toString() +
+ ", args are , count is " + count);
+ }
+ }
+ }
+ return new CrossProcessCursorWrapper(cursor);
+ }
+
+ /**
+ * Runs the provided SQL and returns a cursor over the result set.
+ *
+ * @param cursorFactory the cursor factory to use, or null for the default factory
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param selectionArgs You may include ?s in where clause in the query,
+ * which will be replaced by the values from selectionArgs. The
+ * values will be bound as Strings.
+ * @param editTable the name of the first table, which is editable
+ *
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * @throws SQLiteException if there is an issue executing the sql or the SQL string is invalid
+ * @throws IllegalStateException if the database is not open
+ */
+ public Cursor rawQueryWithFactory(
+ CursorFactory cursorFactory, String sql, String[] selectionArgs,
+ String editTable) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ long timeStart = 0;
+
+ if (Config.LOGV || mSlowQueryThreshold != -1) {
+ timeStart = System.currentTimeMillis();
+ }
+
+ SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
+
+ Cursor cursor = null;
+ try {
+ cursor = driver.query(
+ cursorFactory != null ? cursorFactory : mFactory,
+ selectionArgs);
+ } finally {
+ if (Config.LOGV || mSlowQueryThreshold != -1) {
+
+ // Force query execution
+ int count = -1;
+ if (cursor != null) {
+ count = cursor.getCount();
+ }
+
+ long duration = System.currentTimeMillis() - timeStart;
+
+ if (BuildConfig.DEBUG || duration >= mSlowQueryThreshold) {
+ Log.v(TAG,
+ "query (" + duration + " ms): " + driver.toString() +
+ ", args are , count is " + count);
+ }
+ }
+ }
+ return new CrossProcessCursorWrapper(cursor);
+ }
+
+ /**
+ * Runs the provided SQL and returns a cursor over the result set.
+ * The cursor will read an initial set of rows and the return to the caller.
+ * It will continue to read in batches and send data changed notifications
+ * when the later batches are ready.
+ * @param sql the SQL query. The SQL string must not be ; terminated
+ * @param selectionArgs You may include ?s in where clause in the query,
+ * which will be replaced by the values from selectionArgs. The
+ * values will be bound as Strings.
+ * @param initialRead set the initial count of items to read from the cursor
+ * @param maxRead set the count of items to read on each iteration after the first
+ * @return A {@link Cursor} object, which is positioned before the first entry. Note that
+ * {@link Cursor}s are not synchronized, see the documentation for more details.
+ *
+ * This work is incomplete and not fully tested or reviewed, so currently
+ * hidden.
+ * @hide
+ */
+ public Cursor rawQuery(String sql, String[] selectionArgs,
+ int initialRead, int maxRead) {
+ net.sqlcipher.CursorWrapper cursorWrapper = (net.sqlcipher.CursorWrapper)rawQueryWithFactory(null, sql, selectionArgs, null);
+ ((SQLiteCursor)cursorWrapper.getWrappedCursor()).setLoadStyle(initialRead, maxRead);
+ return cursorWrapper;
+ }
+
+ /**
+ * Convenience method for inserting a row into the database.
+ *
+ * @param table the table to insert the row into
+ * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
+ * so if initialValues is empty this column will explicitly be
+ * assigned a NULL value
+ * @param values this map contains the initial column values for the
+ * row. The keys should be the column names and the values the
+ * column values
+ * @return the row ID of the newly inserted row, or -1 if an error occurred
+ */
+ public long insert(String table, String nullColumnHack, ContentValues values) {
+ try {
+ return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
+ } catch (SQLException e) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Error inserting into " + table, e);
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * Convenience method for inserting a row into the database.
+ *
+ * @param table the table to insert the row into
+ * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
+ * so if initialValues is empty this column will explicitly be
+ * assigned a NULL value
+ * @param values this map contains the initial column values for the
+ * row. The keys should be the column names and the values the
+ * column values
+ * @throws SQLException
+ * @return the row ID of the newly inserted row, or -1 if an error occurred
+ */
+ public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
+ throws SQLException {
+ return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
+ }
+
+ /**
+ * Convenience method for replacing a row in the database.
+ *
+ * @param table the table in which to replace the row
+ * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
+ * so if initialValues is empty this row will explicitly be
+ * assigned a NULL value
+ * @param initialValues this map contains the initial column values for
+ * the row. The key
+ * @return the row ID of the newly inserted row, or -1 if an error occurred
+ */
+ public long replace(String table, String nullColumnHack, ContentValues initialValues) {
+ try {
+ return insertWithOnConflict(table, nullColumnHack, initialValues,
+ CONFLICT_REPLACE);
+ } catch (SQLException e) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Error inserting into " + table, e);
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * Convenience method for replacing a row in the database.
+ *
+ * @param table the table in which to replace the row
+ * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
+ * so if initialValues is empty this row will explicitly be
+ * assigned a NULL value
+ * @param initialValues this map contains the initial column values for
+ * the row. The key
+ * @throws SQLException
+ * @return the row ID of the newly inserted row, or -1 if an error occurred
+ */
+ public long replaceOrThrow(String table, String nullColumnHack,
+ ContentValues initialValues) throws SQLException {
+ return insertWithOnConflict(table, nullColumnHack, initialValues,
+ CONFLICT_REPLACE);
+ }
+
+ /**
+ * General method for inserting a row into the database.
+ *
+ * @param table the table to insert the row into
+ * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
+ * so if initialValues is empty this column will explicitly be
+ * assigned a NULL value
+ * @param initialValues this map contains the initial column values for the
+ * row. The keys should be the column names and the values the
+ * column values
+ * @param conflictAlgorithm for insert conflict resolver
+ *
+ * @return the row ID of the newly inserted row
+ * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
+ * {@link #CONFLICT_IGNORE}
+ * OR -1 if any error
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public long insertWithOnConflict(String table, String nullColumnHack,
+ ContentValues initialValues, int conflictAlgorithm) {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+
+ // Measurements show most sql lengths <= 152
+ StringBuilder sql = new StringBuilder(152);
+ sql.append("INSERT");
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+ sql.append(" INTO ");
+ sql.append(table);
+ // Measurements show most values lengths < 40
+ StringBuilder values = new StringBuilder(40);
+
+ Set> entrySet = null;
+ if (initialValues != null && initialValues.size() > 0) {
+ entrySet = initialValues.valueSet();
+ Iterator> entriesIter = entrySet.iterator();
+ sql.append('(');
+
+ boolean needSeparator = false;
+ while (entriesIter.hasNext()) {
+ if (needSeparator) {
+ sql.append(", ");
+ values.append(", ");
+ }
+ needSeparator = true;
+ Map.Entry entry = entriesIter.next();
+ sql.append(entry.getKey());
+ values.append('?');
+ }
+
+ sql.append(')');
+ } else {
+ sql.append("(" + nullColumnHack + ") ");
+ values.append("NULL");
+ }
+
+ sql.append(" VALUES(");
+ sql.append(values);
+ sql.append(");");
+
+ lock();
+ SQLiteStatement statement = null;
+ try {
+ statement = compileStatement(sql.toString());
+
+ // Bind the values
+ if (entrySet != null) {
+ int size = entrySet.size();
+ Iterator> entriesIter = entrySet.iterator();
+ for (int i = 0; i < size; i++) {
+ Map.Entry entry = entriesIter.next();
+ DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
+
+ }
+ }
+
+ // Run the program and then cleanup
+ statement.execute();
+
+ long insertedRowId = lastChangeCount() > 0 ? lastInsertRow() : -1;
+ if (insertedRowId == -1) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Error inserting using into " + table);
+ }
+ } else {
+ if (BuildConfig.DEBUG && Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Inserting row " + insertedRowId +
+ " from using into " + table);
+ }
+ }
+ return insertedRowId;
+ } catch (SQLiteDatabaseCorruptException e) {
+ onCorruption();
+ throw e;
+ } finally {
+ if (statement != null) {
+ statement.close();
+ }
+ unlock();
+ }
+ }
+
+ /**
+ * Convenience method for deleting rows in the database.
+ *
+ * @param table the table to delete from
+ * @param whereClause the optional WHERE clause to apply when deleting.
+ * Passing null will delete all rows.
+ *
+ * @return the number of rows affected if a whereClause is passed in, 0
+ * otherwise. To remove all rows and get a count pass "1" as the
+ * whereClause.
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public int delete(String table, String whereClause, String[] whereArgs) {
+ return delete(table, whereClause, (Object[])whereArgs);
+ }
+
+ /**
+ * Convenience method for deleting rows in the database.
+ *
+ * @param table the table to delete from
+ * @param whereClause the optional WHERE clause to apply when deleting.
+ * Passing null will delete all rows.
+ *
+ * @return the number of rows affected if a whereClause is passed in, 0
+ * otherwise. To remove all rows and get a count pass "1" as the
+ * whereClause.
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public int delete(String table, String whereClause, Object[] whereArgs) {
+ SQLiteStatement statement = null;
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ statement = compileStatement("DELETE FROM " + table
+ + (!TextUtils.isEmpty(whereClause)
+ ? " WHERE " + whereClause : ""));
+ if (whereArgs != null) {
+ int numArgs = whereArgs.length;
+ for (int i = 0; i < numArgs; i++) {
+ DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
+ }
+ }
+ statement.execute();
+ return lastChangeCount();
+ } catch (SQLiteDatabaseCorruptException e) {
+ onCorruption();
+ throw e;
+ } finally {
+ if (statement != null) {
+ statement.close();
+ }
+ unlock();
+ }
+ }
+
+ /**
+ * Convenience method for updating rows in the database.
+ *
+ * @param table the table to update in
+ * @param values a map from column names to new column values. null is a
+ * valid value that will be translated to NULL.
+ * @param whereClause the optional WHERE clause to apply when updating.
+ * Passing null will update all rows.
+ *
+ * @return the number of rows affected
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
+ return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
+ }
+
+ /**
+ * Convenience method for updating rows in the database.
+ *
+ * @param table the table to update in
+ * @param values a map from column names to new column values. null is a
+ * valid value that will be translated to NULL.
+ * @param whereClause the optional WHERE clause to apply when updating.
+ * Passing null will update all rows.
+ * @param conflictAlgorithm for update conflict resolver
+ *
+ * @return the number of rows affected
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public int updateWithOnConflict(String table, ContentValues values,
+ String whereClause, String[] whereArgs, int conflictAlgorithm) {
+ if (values == null || values.size() == 0) {
+ throw new IllegalArgumentException("Empty values");
+ }
+
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("UPDATE ");
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+ sql.append(table);
+ sql.append(" SET ");
+
+ Set> entrySet = values.valueSet();
+ Iterator> entriesIter = entrySet.iterator();
+
+ while (entriesIter.hasNext()) {
+ Map.Entry entry = entriesIter.next();
+ sql.append(entry.getKey());
+ sql.append("=?");
+ if (entriesIter.hasNext()) {
+ sql.append(", ");
+ }
+ }
+
+ if (!TextUtils.isEmpty(whereClause)) {
+ sql.append(" WHERE ");
+ sql.append(whereClause);
+ }
+ SQLiteStatement statement = null;
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ statement = compileStatement(sql.toString());
+
+ // Bind the values
+ int size = entrySet.size();
+ entriesIter = entrySet.iterator();
+ int bindArg = 1;
+ for (int i = 0; i < size; i++) {
+ Map.Entry entry = entriesIter.next();
+ DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
+ bindArg++;
+ }
+
+ if (whereArgs != null) {
+ size = whereArgs.length;
+ for (int i = 0; i < size; i++) {
+ statement.bindString(bindArg, whereArgs[i]);
+ bindArg++;
+ }
+ }
+
+ // Run the program and then cleanup
+ statement.execute();
+ int numChangedRows = lastChangeCount();
+ if (BuildConfig.DEBUG && Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Updated " + numChangedRows +
+ " rows using and for " + table);
+ }
+ return numChangedRows;
+ } catch (SQLiteDatabaseCorruptException e) {
+ onCorruption();
+ throw e;
+ } catch (SQLException e) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "Error updating using for " + table);
+ }
+ throw e;
+ } finally {
+ if (statement != null) {
+ statement.close();
+ }
+ unlock();
+ }
+ }
+
+ /**
+ * Execute a single SQL statement that is not a query. For example, CREATE
+ * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
+ * supported. it takes a write lock
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public void execSQL(String sql) throws SQLException {
+ long timeStart = SystemClock.uptimeMillis();
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ native_execSQL(sql);
+ } catch (SQLiteDatabaseCorruptException e) {
+ onCorruption();
+ throw e;
+ } finally {
+ unlock();
+ }
+ }
+
+ public void rawExecSQL(String sql){
+ long timeStart = SystemClock.uptimeMillis();
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ native_rawExecSQL(sql);
+ } catch (SQLiteDatabaseCorruptException e) {
+ onCorruption();
+ throw e;
+ } finally {
+ unlock();
+ }
+ }
+
+ /**
+ * Execute a single SQL statement that is not a query. For example, CREATE
+ * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
+ * supported. it takes a write lock,
+ *
+ * @param sql
+ * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
+ *
+ * @throws SQLException If the SQL string is invalid for some reason
+ * @throws IllegalStateException if the database is not open
+ */
+ public void execSQL(String sql, Object[] bindArgs) throws SQLException {
+ SQLiteStatement statement = null;
+ if (bindArgs == null) {
+ throw new IllegalArgumentException("Empty bindArgs");
+ }
+ long timeStart = SystemClock.uptimeMillis();
+ lock();
+ try {
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ statement = compileStatement(sql);
+ if (bindArgs != null) {
+ int numArgs = bindArgs.length;
+ for (int i = 0; i < numArgs; i++) {
+ DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
+ }
+ }
+ statement.execute();
+ } catch (SQLiteDatabaseCorruptException e) {
+ onCorruption();
+ throw e;
+ } finally {
+ if (statement != null) {
+ statement.close();
+ }
+ unlock();
+ }
+ }
+
+ @Override
+ protected void finalize() {
+ if (isOpen()) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "close() was never explicitly called on database '" +
+ mPath + "' ", mStackTrace);
+ }
+ closeClosable();
+ onAllReferencesReleased();
+ }
+ }
+
+ /**
+ * Public constructor which attempts to open the database. See {@link #create} and {@link #openDatabase}.
+ *
+ * Sets the locale of the database to the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path The full path to the database
+ * @param password to use to open and/or create a database file (char array)
+ * @param factory The factory to use when creating cursors, may be NULL.
+ * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
+ * exists, mFlags will be updated appropriately.
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public SQLiteDatabase(String path, char[] password, CursorFactory factory, int flags) {
+ this(path, factory, flags, null);
+ this.openDatabaseInternal(password, null);
+ }
+
+ /**
+ * Public constructor which attempts to open the database. See {@link #create} and {@link #openDatabase}.
+ *
+ * Sets the locale of the database to the system's current locale.
+ * Call {@link #setLocale} if you would like something else.
+ *
+ * @param path The full path to the database
+ * @param password to use to open and/or create a database file (char array)
+ * @param factory The factory to use when creating cursors, may be NULL.
+ * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
+ * exists, mFlags will be updated appropriately.
+ * @param databaseHook to run on pre/post key events
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @throws IllegalArgumentException if the database path is null
+ */
+ public SQLiteDatabase(String path, char[] password, CursorFactory factory, int flags, SQLiteDatabaseHook databaseHook) {
+ this(path, factory, flags, null);
+ this.openDatabaseInternal(password, databaseHook);
+ }
+
+ public SQLiteDatabase(String path, byte[] password, CursorFactory factory, int flags, SQLiteDatabaseHook databaseHook) {
+ this(path, factory, flags, null);
+ this.openDatabaseInternal(password, databaseHook);
+ }
+
+ /**
+ * Private constructor (without database password) which DOES NOT attempt to open the database.
+ *
+ * @param path The full path to the database
+ * @param factory The factory to use when creating cursors, may be NULL.
+ * @param flags to control database access mode and other options
+ * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
+ * corruption (or null for default).
+ *
+ * @throws IllegalArgumentException if the database path is null
+ */
+ private SQLiteDatabase(String path, CursorFactory factory, int flags, DatabaseErrorHandler errorHandler) {
+ if (path == null) {
+ throw new IllegalArgumentException("path should not be null");
+ }
+
+ mFlags = flags;
+ mPath = path;
+
+ mSlowQueryThreshold = -1;//SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
+ mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
+ mFactory = factory;
+ mPrograms = new WeakHashMap();
+
+ mErrorHandler = errorHandler;
+ }
+
+ private void openDatabaseInternal(final char[] password, SQLiteDatabaseHook hook) {
+ final byte[] keyMaterial = getBytes(password);
+ openDatabaseInternal(keyMaterial, hook);
+ }
+
+ private void openDatabaseInternal(final byte[] password, SQLiteDatabaseHook hook) {
+ boolean shouldCloseConnection = true;
+ dbopen(mPath, mFlags);
+ try {
+ keyDatabase(hook, new Runnable() {
+ public void run() {
+ if(password != null && password.length > 0) {
+ key(password);
+ }
+ }
+ });
+ shouldCloseConnection = false;
+
+ } catch(RuntimeException ex) {
+
+ final char[] keyMaterial = getChars(password);
+ if(containsNull(keyMaterial)) {
+ keyDatabase(hook, new Runnable() {
+ public void run() {
+ if(password != null) {
+ key_mutf8(keyMaterial);
+ }
+ }
+ });
+ if(password != null && password.length > 0) {
+ rekey(password);
+ }
+ shouldCloseConnection = false;
+ } else {
+ throw ex;
+ }
+ if(keyMaterial != null && keyMaterial.length > 0) {
+ Arrays.fill(keyMaterial, (char)0);
+ }
+
+ } finally {
+ if(shouldCloseConnection) {
+ dbclose();
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ mTimeClosed = getTime();
+ }
+ }
+ }
+
+ }
+
+ private boolean containsNull(char[] data) {
+ char defaultValue = '\u0000';
+ boolean status = false;
+ if(data != null && data.length > 0) {
+ for(char datum : data) {
+ if(datum == defaultValue) {
+ status = true;
+ break;
+ }
+ }
+ }
+ return status;
+ }
+
+ private void keyDatabase(SQLiteDatabaseHook databaseHook, Runnable keyOperation) {
+ if(databaseHook != null) {
+ databaseHook.preKey(this);
+ }
+ if(keyOperation != null){
+ keyOperation.run();
+ }
+ if(databaseHook != null){
+ databaseHook.postKey(this);
+ }
+ if (SQLiteDebug.DEBUG_SQL_CACHE) {
+ mTimeOpened = getTime();
+ }
+ try {
+ Cursor cursor = rawQuery("select count(*) from sqlite_master;", new String[]{});
+ if(cursor != null){
+ cursor.moveToFirst();
+ int count = cursor.getInt(0);
+ cursor.close();
+ }
+ } catch (RuntimeException e) {
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, e.getMessage(), e);
+ }
+ throw e;
+ }
+ }
+
+ private String getTime() {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ", Locale.US).format(System.currentTimeMillis());
+ }
+
+ /**
+ * return whether the DB is opened as read only.
+ * @return true if DB is opened as read only
+ */
+ public boolean isReadOnly() {
+ return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
+ }
+
+ /**
+ * @return true if the DB is currently open (has not been closed)
+ */
+ public boolean isOpen() {
+ return mNativeHandle != 0;
+ }
+
+ public boolean needUpgrade(int newVersion) {
+ /* NOTE: getVersion() will throw if database is not open. */
+ return newVersion > getVersion();
+ }
+
+ /**
+ * Getter for the path to the database file.
+ *
+ * @return the path to our database file.
+ */
+ public final String getPath() {
+ return mPath;
+ }
+
+ /**
+ * Removes email addresses from database filenames before they're
+ * logged to the EventLog where otherwise apps could potentially
+ * read them.
+ */
+ private String getPathForLogs() {
+ if (mPathForLogs != null) {
+ return mPathForLogs;
+ }
+ if (mPath == null) {
+ return null;
+ }
+ if (mPath.indexOf('@') == -1) {
+ mPathForLogs = mPath;
+ } else {
+ mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
+ }
+ return mPathForLogs;
+ }
+
+ /**
+ * Sets the locale for this database. Does nothing if this database has
+ * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
+ *
+ * @throws SQLException if the locale could not be set. The most common reason
+ * for this is that there is no collator available for the locale you requested.
+ * In this case the database remains unchanged.
+ */
+ public void setLocale(Locale locale) {
+ lock();
+ try {
+ native_setLocale(locale.toString(), mFlags);
+ } finally {
+ unlock();
+ }
+ }
+
+ /*
+ * ============================================================================
+ *
+ * The following methods deal with compiled-sql cache
+ * ============================================================================
+ */
+ /**
+ * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
+ * cache of compiledQueries attached to 'this'.
+ *
+ * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
+ * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
+ * mapping is NOT replaced with the new mapping).
+ */
+ /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
+ if (mMaxSqlCacheSize == 0) {
+ // for this database, there is no cache of compiled sql.
+ if (SQLiteDebug.DEBUG_SQL_CACHE && BuildConfig.DEBUG) {
+ Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
+ }
+ return;
+ }
+
+ SQLiteCompiledSql compiledSql = null;
+ synchronized(mCompiledQueries) {
+ // don't insert the new mapping if a mapping already exists
+ compiledSql = mCompiledQueries.get(sql);
+ if (compiledSql != null) {
+ return;
+ }
+ // add this to the cache
+ if (mCompiledQueries.size() == mMaxSqlCacheSize) {
+ /*
+ * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
+ * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
+ * chances are it is NOT using ? for bindargs - so caching is useless.
+ * TODO: either let the callers set max cchesize for their app, or intelligently
+ * figure out what should be cached for a given app.
+ */
+ if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION && BuildConfig.DEBUG) {
+ Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
+ getPath() + "; i.e., NO space for this sql statement in cache: " +
+ sql + ". Please change your sql statements to use '?' for " +
+ "bindargs, instead of using actual values");
+ }
+ // don't add this entry to cache
+ } else {
+ // cache is NOT full. add this to cache.
+ mCompiledQueries.put(sql, compiledStatement);
+ if (SQLiteDebug.DEBUG_SQL_CACHE && BuildConfig.DEBUG) {
+ Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
+ mCompiledQueries.size() + "|" + sql);
+ }
+ }
+ }
+ return;
+ }
+
+
+ private void deallocCachedSqlStatements() {
+ synchronized (mCompiledQueries) {
+ for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
+ compiledSql.releaseSqlStatement();
+ }
+ mCompiledQueries.clear();
+ }
+ }
+
+ /**
+ * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
+ * returns null, if not found in the cache.
+ */
+ /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
+ SQLiteCompiledSql compiledStatement = null;
+ boolean cacheHit;
+ synchronized(mCompiledQueries) {
+ if (mMaxSqlCacheSize == 0) {
+ // for this database, there is no cache of compiled sql.
+ if (SQLiteDebug.DEBUG_SQL_CACHE && BuildConfig.DEBUG) {
+ Log.v(TAG, "|cache NOT found|" + getPath());
+ }
+ return null;
+ }
+ cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
+ }
+ if (cacheHit) {
+ mNumCacheHits++;
+ } else {
+ mNumCacheMisses++;
+ }
+
+ if (SQLiteDebug.DEBUG_SQL_CACHE && BuildConfig.DEBUG) {
+ Log.v(TAG, "|cache_stats|" +
+ getPath() + "|" + mCompiledQueries.size() +
+ "|" + mNumCacheHits + "|" + mNumCacheMisses +
+ "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
+ }
+ return compiledStatement;
+ }
+
+ /**
+ * returns true if the given sql is cached in compiled-sql cache.
+ * @hide
+ */
+ public boolean isInCompiledSqlCache(String sql) {
+ synchronized(mCompiledQueries) {
+ return mCompiledQueries.containsKey(sql);
+ }
+ }
+
+ /**
+ * purges the given sql from the compiled-sql cache.
+ * @hide
+ */
+ public void purgeFromCompiledSqlCache(String sql) {
+ synchronized(mCompiledQueries) {
+ mCompiledQueries.remove(sql);
+ }
+ }
+
+ /**
+ * remove everything from the compiled sql cache
+ * @hide
+ */
+ public void resetCompiledSqlCache() {
+ deallocCachedSqlStatements();
+ }
+
+ /**
+ * return the current maxCacheSqlCacheSize
+ * @hide
+ */
+ public synchronized int getMaxSqlCacheSize() {
+ return mMaxSqlCacheSize;
+ }
+
+ /**
+ * set the max size of the compiled sql cache for this database after purging the cache.
+ * (size of the cache = number of compiled-sql-statements stored in the cache).
+ *
+ * max cache size can ONLY be increased from its current size (default = 0).
+ * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
+ * then IllegalStateException is thrown
+ *
+ * synchronized because we don't want t threads to change cache size at the same time.
+ * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
+ * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
+ * < the value set with previous setMaxSqlCacheSize() call.
+ *
+ * @hide
+ */
+ public synchronized void setMaxSqlCacheSize(int cacheSize) {
+ if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
+ throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
+ } else if (cacheSize < mMaxSqlCacheSize) {
+ throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
+ "set with previous setMaxSqlCacheSize() call.");
+ }
+ mMaxSqlCacheSize = cacheSize;
+ }
+
+ public static byte[] getBytes(char[] data) {
+ if(data == null || data.length == 0) return null;
+ CharBuffer charBuffer = CharBuffer.wrap(data);
+ ByteBuffer byteBuffer = Charset.forName(KEY_ENCODING).encode(charBuffer);
+ byte[] result = new byte[byteBuffer.limit()];
+ byteBuffer.get(result);
+ return result;
+ }
+
+ public static char[] getChars(byte[] data){
+ if(data == null || data.length == 0) return null;
+ ByteBuffer byteBuffer = ByteBuffer.wrap(data);
+ CharBuffer charBuffer = Charset.forName(KEY_ENCODING).decode(byteBuffer);
+ char[] result = new char[charBuffer.limit()];
+ charBuffer.get(result);
+ return result;
+ }
+
+ /* begin SQLiteSupportDatabase methods */
+
+ @Override
+ public android.database.Cursor query(String query) {
+ return rawQuery(query, null);
+ }
+
+ @Override
+ public android.database.Cursor query(String query, Object[] bindArgs) {
+ return rawQuery(query, bindArgs);
+ }
+
+ @Override
+ public android.database.Cursor query(SupportSQLiteQuery query) {
+ return query(query, null);
+ }
+
+ @Override
+ public android.database.Cursor query(final SupportSQLiteQuery supportQuery,
+ CancellationSignal cancellationSignal) {
+ String sql = supportQuery.getSql();
+ int argumentCount = supportQuery.getArgCount();
+ Object[] args = new Object[argumentCount];
+ SQLiteDirectCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, null);
+ SQLiteQuery query = new SQLiteQuery(this, sql, 0, args);
+ supportQuery.bindTo(query);
+ return new CrossProcessCursorWrapper(new SQLiteCursor(this, driver, null, query));
+ }
+
+ @Override
+ public long insert(String table, int conflictAlgorithm,
+ ContentValues values)
+ throws android.database.SQLException {
+ return insertWithOnConflict(table, null, values, conflictAlgorithm);
+ }
+
+ @Override
+ public int update(String table, int conflictAlgorithm, ContentValues values,
+ String whereClause, Object[] whereArgs) {
+ int whereArgsLength = whereArgs == null
+ ? 0
+ : whereArgs.length;
+ String[] args = new String[whereArgsLength];
+ for (int i = 0; i < whereArgsLength; i++) {
+ args[i] = whereArgs[i].toString();
+ }
+ return updateWithOnConflict(table, values, whereClause, args, conflictAlgorithm);
+ }
+
+ @Override
+ public void beginTransactionWithListener(
+ final android.database.sqlite.SQLiteTransactionListener transactionListener) {
+ beginTransactionWithListener(new SQLiteTransactionListener() {
+ @Override
+ public void onBegin() {
+ transactionListener.onBegin();
+ }
+
+ @Override
+ public void onCommit() {
+ transactionListener.onCommit();
+ }
+
+ @Override
+ public void onRollback() {
+ transactionListener.onRollback();
+ }
+ });
+ }
+
+ @Override
+ public void beginTransactionWithListenerNonExclusive(
+ final android.database.sqlite.SQLiteTransactionListener transactionListener) {
+ beginTransactionWithListenerNonExclusive(
+ new SQLiteTransactionListener() {
+ @Override
+ public void onBegin() {
+ transactionListener.onBegin();
+ }
+
+ @Override
+ public void onCommit() {
+ transactionListener.onCommit();
+ }
+
+ @Override
+ public void onRollback() {
+ transactionListener.onRollback();
+ }
+ });
+ }
+
+ /* end SQLiteSupportDatabase methods */
+
+ private void beginTransactionWithListenerInternal(SQLiteTransactionListener transactionListener,
+ SQLiteDatabaseTransactionType transactionType) {
+ lockForced();
+ if (!isOpen()) {
+ throw new IllegalStateException("database not open");
+ }
+ boolean ok = false;
+ try {
+ // If this thread already had the lock then get out
+ if (mLock.getHoldCount() > 1) {
+ if (mInnerTransactionIsSuccessful) {
+ String msg = "Cannot call beginTransaction between "
+ + "calling setTransactionSuccessful and endTransaction";
+ IllegalStateException e = new IllegalStateException(msg);
+ if(BuildConfig.DEBUG){
+ Log.e(TAG, "beginTransaction() failed", e);
+ }
+ throw e;
+ }
+ ok = true;
+ return;
+ }
+ // This thread didn't already have the lock, so begin a database
+ // transaction now.
+ if(transactionType == SQLiteDatabaseTransactionType.Exclusive) {
+ execSQL("BEGIN EXCLUSIVE;");
+ } else if(transactionType == SQLiteDatabaseTransactionType.Immediate) {
+ execSQL("BEGIN IMMEDIATE;");
+ } else if(transactionType == SQLiteDatabaseTransactionType.Deferred) {
+ execSQL("BEGIN DEFERRED;");
+ } else {
+ String message = String.format("%s is an unsupported transaction type",
+ transactionType);
+ throw new IllegalArgumentException(message);
+ }
+ mTransactionListener = transactionListener;
+ mTransactionIsSuccessful = true;
+ mInnerTransactionIsSuccessful = false;
+ if (transactionListener != null) {
+ try {
+ transactionListener.onBegin();
+ } catch (RuntimeException e) {
+ execSQL("ROLLBACK;");
+ throw e;
+ }
+ }
+ ok = true;
+ } finally {
+ if (!ok) {
+ // beginTransaction is called before the try block so we must release the lock in
+ // the case of failure.
+ unlockForced();
+ }
+ }
+ }
+
+ /**
+ * this method is used to collect data about ALL open databases in the current process.
+ * bugreport is a user of this data.
+ */
+ /* package */ static ArrayList getDbStats() {
+ ArrayList dbStatsList = new ArrayList();
+
+ for (SQLiteDatabase db : getActiveDatabases()) {
+ if (db == null || !db.isOpen()) {
+ continue;
+ }
+
+ // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
+ int lookasideUsed = db.native_getDbLookaside();
+
+ // get the lastnode of the dbname
+ String path = db.getPath();
+ int indx = path.lastIndexOf("/");
+ String lastnode = path.substring((indx != -1) ? ++indx : 0);
+
+ // get list of attached dbs and for each db, get its size and pagesize
+ ArrayList> attachedDbs = getAttachedDbs(db);
+ if (attachedDbs == null) {
+ continue;
+ }
+ for (int i = 0; i < attachedDbs.size(); i++) {
+ Pair p = attachedDbs.get(i);
+ long pageCount = getPragmaVal(db, p.first + ".page_count;");
+
+ // first entry in the attached db list is always the main database
+ // don't worry about prefixing the dbname with "main"
+ String dbName;
+ if (i == 0) {
+ dbName = lastnode;
+ } else {
+ // lookaside is only relevant for the main db
+ lookasideUsed = 0;
+ dbName = " (attached) " + p.first;
+ // if the attached db has a path, attach the lastnode from the path to above
+ if (p.second.trim().length() > 0) {
+ int idx = p.second.lastIndexOf("/");
+ dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
+ }
+ }
+ if (pageCount > 0) {
+ dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
+ lookasideUsed));
+ }
+ }
+ }
+ return dbStatsList;
+ }
+
+ private static ArrayList getActiveDatabases() {
+ ArrayList databases = new ArrayList();
+ synchronized (sActiveDatabases) {
+ databases.addAll(sActiveDatabases.keySet());
+ }
+ return databases;
+ }
+
+ /**
+ * get the specified pragma value from sqlite for the specified database.
+ * only handles pragma's that return int/long.
+ * NO JAVA locks are held in this method.
+ * TODO: use this to do all pragma's in this class
+ */
+ private static long getPragmaVal(SQLiteDatabase db, String pragma) {
+ if (!db.isOpen()) {
+ return 0;
+ }
+ SQLiteStatement prog = null;
+ try {
+ prog = new SQLiteStatement(db, "PRAGMA " + pragma);
+ long val = prog.simpleQueryForLong();
+ return val;
+ } finally {
+ if (prog != null) prog.close();
+ }
+ }
+
+ /**
+ * returns list of full pathnames of all attached databases
+ * including the main database
+ * TODO: move this to {@link DatabaseUtils}
+ */
+ private static ArrayList> getAttachedDbs(SQLiteDatabase dbObj) {
+ if (!dbObj.isOpen()) {
+ return null;
+ }
+ ArrayList> attachedDbs = new ArrayList>();
+ Cursor c = dbObj.rawQuery("pragma database_list;", null);
+ while (c.moveToNext()) {
+ attachedDbs.add(new Pair(c.getString(1), c.getString(2)));
+ }
+ c.close();
+ return attachedDbs;
+ }
+
+ private Pair getResultFromPragma(String command) {
+ Pair result = new Pair(false, "");
+ Cursor cursor = rawQuery(command, new Object[]{});
+ if(cursor == null) return result;
+ if(cursor.moveToFirst()){
+ String value = cursor.getString(0);
+ result = new Pair(true, value);
+ }
+ cursor.close();
+ return result;
+ }
+
+
+ /**
+ * Sets the root directory to search for the ICU data file
+ */
+ public static native void setICURoot(String path);
+
+ /**
+ * Native call to open the database.
+ *
+ * @param path The full path to the database
+ */
+ private native void dbopen(String path, int flags);
+
+ /**
+ * Native call to setup tracing of all sql statements
+ *
+ * @param path the full path to the database
+ */
+ private native void enableSqlTracing(String path);
+
+ /**
+ * Native call to setup profiling of all sql statements.
+ * currently, sqlite's profiling = printing of execution-time
+ * (wall-clock time) of each of the sql statements, as they
+ * are executed.
+ *
+ * @param path the full path to the database
+ */
+ private native void enableSqlProfiling(String path);
+
+ /**
+ * Native call to execute a raw SQL statement. {@link #lock} must be held
+ * when calling this method.
+ *
+ * @param sql The raw SQL string
+ *
+ * @throws SQLException
+ */
+ /* package */ native void native_execSQL(String sql) throws SQLException;
+
+ /**
+ * Native call to set the locale. {@link #lock} must be held when calling
+ * this method.
+ *
+ * @throws SQLException
+ */
+ /* package */ native void native_setLocale(String loc, int flags);
+
+ /**
+ * Returns the row ID of the last row inserted into the database.
+ *
+ * @return the row ID of the last row inserted into the database.
+ */
+ /* package */ native long lastInsertRow();
+
+ /**
+ * Returns the number of changes made in the last statement executed.
+ *
+ * @return the number of changes made in the last statement executed.
+ */
+ /* package */ native int lastChangeCount();
+
+ /**
+ * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
+ * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
+ * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
+ */
+ private native int native_getDbLookaside();
+
+ private native void native_rawExecSQL(String sql);
+
+ private native int native_status(int operation, boolean reset);
+
+ private native void key(byte[] key) throws SQLException;
+ private native void key_mutf8(char[] key) throws SQLException;
+ private native void rekey(byte[] key) throws SQLException;
+}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabaseHook.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabaseHook.java
new file mode 100644
index 00000000..a5014b14
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabaseHook.java
@@ -0,0 +1,15 @@
+package net.sqlcipher.database;
+
+/**
+ * An interface to perform pre and post key operations against a database.
+ */
+public interface SQLiteDatabaseHook {
+ /**
+ * Called immediately before opening the database.
+ */
+ void preKey(SQLiteDatabase database);
+ /**
+ * Called immediately after opening the database.
+ */
+ void postKey(SQLiteDatabase database);
+}
diff --git a/src/net/sqlcipher/database/SQLiteDebug.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDebug.java
similarity index 100%
rename from src/net/sqlcipher/database/SQLiteDebug.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDebug.java
diff --git a/src/net/sqlcipher/database/SQLiteDirectCursorDriver.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDirectCursorDriver.java
similarity index 72%
rename from src/net/sqlcipher/database/SQLiteDirectCursorDriver.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDirectCursorDriver.java
index 2b6cffc7..36ae59a4 100644
--- a/src/net/sqlcipher/database/SQLiteDirectCursorDriver.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDirectCursorDriver.java
@@ -16,18 +16,18 @@
package net.sqlcipher.database;
+import net.sqlcipher.Cursor;
import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
-import android.database.Cursor;
/**
* A cursor driver that uses the given query directly.
- *
+ *
* @hide
*/
public class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
- private String mEditTable;
+ private String mEditTable;
private SQLiteDatabase mDatabase;
- private android.database.Cursor mCursor;
+ private Cursor mCursor;
private String mSql;
private SQLiteQuery mQuery;
@@ -37,7 +37,25 @@ public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable)
mSql = sql;
}
- public android.database.Cursor query(CursorFactory factory, String[] selectionArgs) {
+ public Cursor query(CursorFactory factory, Object[] args) {
+ SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, args);
+ try {
+ query.bindArguments(args);
+ if (factory == null) {
+ mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
+ } else {
+ mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
+ }
+ mQuery = query;
+ query = null;
+ return mCursor;
+ } finally {
+ // Make sure this object is cleaned up if something happens
+ if (query != null) query.close();
+ }
+ }
+
+ public Cursor query(CursorFactory factory, String[] selectionArgs) {
// Compile the query
SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
@@ -51,7 +69,7 @@ public android.database.Cursor query(CursorFactory factory, String[] selectionAr
// Create the cursor
if (factory == null) {
mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
-
+
} else {
mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
}
@@ -76,11 +94,13 @@ public void setBindArguments(String[] bindArgs) {
}
}
+ @Override
public void cursorDeactivated() {
// Do nothing
}
- public void cursorRequeried(Cursor cursor) {
+ @Override
+ public void cursorRequeried(android.database.Cursor cursor) {
// Do nothing
}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java
new file mode 100644
index 00000000..e3a24f43
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2007 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 net.sqlcipher.database;
+
+import java.io.File;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteException;
+import net.sqlcipher.DatabaseErrorHandler;
+import net.sqlcipher.DefaultDatabaseErrorHandler;
+import net.sqlcipher.database.SQLiteDatabaseHook;
+import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
+import android.util.Log;
+
+/**
+ * A helper class to manage database creation and version management.
+ * You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
+ * optionally {@link #onOpen}, and this class takes care of opening the database
+ * if it exists, creating it if it does not, and upgrading it as necessary.
+ * Transactions are used to make sure the database is always in a sensible state.
+ * For an example, see the NotePadProvider class in the NotePad sample application,
+ * in the samples/ directory of the SDK.
+ */
+public abstract class SQLiteOpenHelper {
+ private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
+
+ private final Context mContext;
+ private final String mName;
+ private final CursorFactory mFactory;
+ private final int mNewVersion;
+ private final SQLiteDatabaseHook mHook;
+ private final DatabaseErrorHandler mErrorHandler;
+ private boolean mEnableWriteAheadLogging;
+ private boolean mDeferSetWriteAheadLoggingEnabled;
+
+ private SQLiteDatabase mDatabase = null;
+ private boolean mIsInitializing = false;
+
+ /**
+ * Create a helper object to create, open, and/or manage a database.
+ * This method always returns very quickly. The database is not actually
+ * created or opened until one of {@link #getWritableDatabase} or
+ * {@link #getReadableDatabase} is called.
+ *
+ * @param context to use to open or create the database
+ * @param name of the database file, or null for an in-memory database
+ * @param factory to use for creating cursor objects, or null for the default
+ * @param version number of the database (starting at 1); if the database is older,
+ * {@link #onUpgrade} will be used to upgrade the database
+ */
+ public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
+ this(context, name, factory, version, null, new DefaultDatabaseErrorHandler());
+ }
+
+ /**
+ * Create a helper object to create, open, and/or manage a database.
+ * The database is not actually created or opened until one of
+ * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
+ *
+ * @param context to use to open or create the database
+ * @param name of the database file, or null for an in-memory database
+ * @param factory to use for creating cursor objects, or null for the default
+ * @param version number of the database (starting at 1); if the database is older,
+ * {@link #onUpgrade} will be used to upgrade the database
+ * @param hook to run on pre/post key events
+ */
+ public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
+ int version, SQLiteDatabaseHook hook) {
+ this(context, name, factory, version, hook, new DefaultDatabaseErrorHandler());
+ }
+
+ /**
+ * Create a helper object to create, open, and/or manage a database.
+ * The database is not actually created or opened until one of
+ * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
+ *
+ * Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
+ * used to handle corruption when sqlite reports database corruption.
+ *
+ * @param context to use to open or create the database
+ * @param name of the database file, or null for an in-memory database
+ * @param factory to use for creating cursor objects, or null for the default
+ * @param version number of the database (starting at 1); if the database is older,
+ * {@link #onUpgrade} will be used to upgrade the database
+ * @param hook to run on pre/post key events
+ * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
+ * corruption.
+ */
+ public SQLiteOpenHelper(Context context, String name, CursorFactory factory,
+ int version, SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
+ if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
+ if (errorHandler == null) {
+ throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
+ }
+
+ mContext = context;
+ mName = name;
+ mFactory = factory;
+ mNewVersion = version;
+ mHook = hook;
+ mErrorHandler = errorHandler;
+ }
+
+ /**
+ * Create and/or open a database that will be used for reading and writing.
+ * Once opened successfully, the database is cached, so you can call this
+ * method every time you need to write to the database. Make sure to call
+ * {@link #close} when you no longer need it.
+ *
+ * Errors such as bad permissions or a full disk may cause this operation
+ * to fail, but future attempts may succeed if the problem is fixed.
+ *
+ * @throws SQLiteException if the database cannot be opened for writing
+ * @return a read/write database object valid until {@link #close} is called
+ */
+
+ public synchronized SQLiteDatabase getWritableDatabase(String password) {
+ return getWritableDatabase(password == null ? null : password.toCharArray());
+ }
+
+ public synchronized SQLiteDatabase getWritableDatabase(char[] password) {
+ return getWritableDatabase(password == null ? null : SQLiteDatabase.getBytes(password));
+ }
+
+ public synchronized SQLiteDatabase getWritableDatabase(byte[] password) {
+ if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
+ return mDatabase; // The database is already open for business
+ }
+
+ if (mIsInitializing) {
+ throw new IllegalStateException("getWritableDatabase called recursively");
+ }
+
+ // If we have a read-only database open, someone could be using it
+ // (though they shouldn't), which would cause a lock to be held on
+ // the file, and our attempts to open the database read-write would
+ // fail waiting for the file lock. To prevent that, we acquire the
+ // lock on the read-only database, which shuts out other users.
+
+ boolean success = false;
+ SQLiteDatabase db = null;
+ if (mDatabase != null) mDatabase.lock();
+ try {
+ mIsInitializing = true;
+ if (mName == null) {
+ db = SQLiteDatabase.create(null, "");
+ } else {
+ String path = mContext.getDatabasePath(mName).getPath();
+ File dbPathFile = new File (path);
+ if (!dbPathFile.exists()) {
+ dbPathFile.getParentFile().mkdirs();
+ }
+ db = SQLiteDatabase.openOrCreateDatabase(path, password, mFactory, mHook, mErrorHandler);
+ }
+ if(mDeferSetWriteAheadLoggingEnabled) {
+ mEnableWriteAheadLogging = db.enableWriteAheadLogging();
+ }
+ onConfigure(db);
+ int version = db.getVersion();
+ if (version != mNewVersion) {
+ db.beginTransaction();
+ try {
+ if (version == 0) {
+ onCreate(db);
+ } else {
+ if(version > mNewVersion) {
+ onDowngrade(db, version, mNewVersion);
+ } else {
+ onUpgrade(db, version, mNewVersion);
+ }
+ }
+ db.setVersion(mNewVersion);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ onOpen(db);
+ success = true;
+ return db;
+ } finally {
+ mIsInitializing = false;
+ if (success) {
+ if (mDatabase != null) {
+ try { mDatabase.close(); } catch (Exception e) { }
+ mDatabase.unlock();
+ }
+ mDatabase = db;
+ } else {
+ if (mDatabase != null) mDatabase.unlock();
+ if (db != null) db.close();
+ }
+ }
+ }
+
+ /**
+ * Create and/or open a database. This will be the same object returned by
+ * {@link #getWritableDatabase} unless some problem, such as a full disk,
+ * requires the database to be opened read-only. In that case, a read-only
+ * database object will be returned. If the problem is fixed, a future call
+ * to {@link #getWritableDatabase} may succeed, in which case the read-only
+ * database object will be closed and the read/write object will be returned
+ * in the future.
+ *
+ * @throws SQLiteException if the database cannot be opened
+ * @return a database object valid until {@link #getWritableDatabase}
+ * or {@link #close} is called.
+ */
+ public synchronized SQLiteDatabase getReadableDatabase(String password) {
+ return getReadableDatabase(password == null ? null : password.toCharArray());
+ }
+
+ public synchronized SQLiteDatabase getReadableDatabase(char[] password) {
+ return getReadableDatabase(password == null ? null : SQLiteDatabase.getBytes(password));
+ }
+
+ public synchronized SQLiteDatabase getReadableDatabase(byte[] password) {
+ if (mDatabase != null && mDatabase.isOpen()) {
+ return mDatabase; // The database is already open for business
+ }
+
+ if (mIsInitializing) {
+ throw new IllegalStateException("getReadableDatabase called recursively");
+ }
+
+ try {
+ return getWritableDatabase(password);
+ } catch (SQLiteException e) {
+ if (mName == null) throw e; // Can't open a temp database read-only!
+ Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
+ }
+
+ SQLiteDatabase db = null;
+ try {
+ mIsInitializing = true;
+ String path = mContext.getDatabasePath(mName).getPath();
+ File databasePath = new File(path);
+ File databasesDirectory = new File(mContext.getDatabasePath(mName).getParent());
+
+ if(!databasesDirectory.exists()){
+ databasesDirectory.mkdirs();
+ }
+ if(!databasePath.exists()){
+ mIsInitializing = false;
+ db = getWritableDatabase(password);
+ mIsInitializing = true;
+ db.close();
+ }
+ db = SQLiteDatabase.openDatabase(path, password, mFactory, SQLiteDatabase.OPEN_READONLY, mHook, mErrorHandler);
+ if (db.getVersion() != mNewVersion) {
+ throw new SQLiteException("Can't upgrade read-only database from version " +
+ db.getVersion() + " to " + mNewVersion + ": " + path);
+ }
+
+ onOpen(db);
+ Log.w(TAG, "Opened " + mName + " in read-only mode");
+ mDatabase = db;
+ return mDatabase;
+ } finally {
+ mIsInitializing = false;
+ if (db != null && db != mDatabase) db.close();
+ }
+ }
+
+ /**
+ * Close any open database object.
+ */
+ public synchronized void close() {
+ if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
+
+ if (mDatabase != null && mDatabase.isOpen()) {
+ mDatabase.close();
+ mDatabase = null;
+ }
+ }
+
+ /**
+ * Return the name of the SQLite database being opened, as given to
+ * the constructor.
+ */
+ public String getDatabaseName() {
+ return mName;
+ }
+
+ /**
+ * Enables or disables the use of write-ahead logging for the database.
+ *
+ * Write-ahead logging cannot be used with read-only databases so the value of
+ * this flag is ignored if the database is opened read-only.
+ *
+ * @param enabled True if write-ahead logging should be enabled, false if it
+ * should be disabled.
+ *
+ * @see SQLiteDatabase#enableWriteAheadLogging()
+ */
+ public void setWriteAheadLoggingEnabled(boolean enabled) {
+ synchronized (this) {
+ if (mEnableWriteAheadLogging != enabled) {
+ if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
+ if (enabled) {
+ mDatabase.enableWriteAheadLogging();
+ } else {
+ mDatabase.disableWriteAheadLogging();
+ }
+ mEnableWriteAheadLogging = enabled;
+ } else {
+ mDeferSetWriteAheadLoggingEnabled = enabled;
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the database needs to be downgraded. This is strictly similar to
+ * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
+ * However, this method is not abstract, so it is not mandatory for a customer to
+ * implement it. If not overridden, default implementation will reject downgrade and
+ * throws SQLiteException
+ *
+ *
+ * This method executes within a transaction. If an exception is thrown, all changes
+ * will automatically be rolled back.
+ *
+ *
+ * @param db The database.
+ * @param oldVersion The old database version.
+ * @param newVersion The new database version.
+ */
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ throw new SQLiteException("Can't downgrade database from version " +
+ oldVersion + " to " + newVersion);
+ }
+
+ /**
+ * Called when the database connection is being configured, to enable features
+ * such as write-ahead logging or foreign key support.
+ *
+ * This method is called before {@link #onCreate}, {@link #onUpgrade},
+ * {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify
+ * the database except to configure the database connection as required.
+ *
+ * This method should only call methods that configure the parameters of the
+ * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
+ * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
+ * {@link SQLiteDatabase#setLocale}, or executing PRAGMA statements.
+ *
+ *
+ * @param db The database.
+ */
+ public void onConfigure(SQLiteDatabase db) {}
+
+ /**
+ * Called when the database is created for the first time. This is where the
+ * creation of tables and the initial population of the tables should happen.
+ *
+ * @param db The database.
+ */
+ public abstract void onCreate(SQLiteDatabase db);
+
+ /**
+ * Called when the database needs to be upgraded. The implementation
+ * should use this method to drop tables, add tables, or do anything else it
+ * needs to upgrade to the new schema version.
+ *
+ * The SQLite ALTER TABLE documentation can be found
+ * here. If you add new columns
+ * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
+ * you can use ALTER TABLE to rename the old table, then create the new table and then
+ * populate the new table with the contents of the old table.
+ *
+ * @param db The database.
+ * @param oldVersion The old database version.
+ * @param newVersion The new database version.
+ */
+ public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
+
+ /**
+ * Called when the database has been opened.
+ * Override method should check {@link SQLiteDatabase#isReadOnly} before
+ * updating the database.
+ *
+ * @param db The database.
+ */
+ public void onOpen(SQLiteDatabase db) {}
+}
diff --git a/src/net/sqlcipher/database/SQLiteProgram.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.java
similarity index 88%
rename from src/net/sqlcipher/database/SQLiteProgram.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.java
index 658b0b90..e43826d9 100644
--- a/src/net/sqlcipher/database/SQLiteProgram.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.java
@@ -17,6 +17,7 @@
package net.sqlcipher.database;
import android.util.Log;
+import androidx.sqlite.db.SupportSQLiteProgram;
/**
* A base class for compiled SQLite programs.
@@ -24,7 +25,8 @@
* SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple
* threads should perform its own synchronization when using the SQLiteProgram.
*/
-public abstract class SQLiteProgram extends SQLiteClosable {
+public abstract class SQLiteProgram extends SQLiteClosable implements
+ SupportSQLiteProgram {
private static final String TAG = "SQLiteProgram";
@@ -43,7 +45,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @deprecated do not use this
*/
@Deprecated
- protected int nHandle = 0;
+ protected long nHandle = 0;
/**
* the SQLiteCompiledSql object for the given sql statement.
@@ -56,7 +58,12 @@ public abstract class SQLiteProgram extends SQLiteClosable {
* @deprecated do not use this
*/
@Deprecated
- protected int nStatement = 0;
+ protected long nStatement = 0;
+
+ /**
+ * Indicates whether {@link #close()} has been called.
+ */
+ boolean mClosed = false;
/* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
mDatabase = db;
@@ -64,9 +71,10 @@ public abstract class SQLiteProgram extends SQLiteClosable {
db.acquireReference();
db.addSQLiteClosable(this);
this.nHandle = db.mNativeHandle;
+ int crudPrefixLength = 6;
// only cache CRUD statements
- String prefixSql = mSql.substring(0, 6);
+ String prefixSql = mSql.length() >= crudPrefixLength ? mSql.substring(0, crudPrefixLength) : mSql;
if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") &&
!prefixSql.equalsIgnoreCase("REPLAC") &&
!prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) {
@@ -95,7 +103,7 @@ public abstract class SQLiteProgram extends SQLiteClosable {
// it is already in compiled-sql cache.
// try to acquire the object.
if (!mCompiledSql.acquire()) {
- int last = mCompiledSql.nStatement;
+ long last = mCompiledSql.nStatement;
// the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
// we can't have two different SQLiteProgam objects can't share the same
// CompiledSql object. create a new one.
@@ -141,7 +149,7 @@ private void releaseCompiledSqlIfNotInCache() {
// it is in compiled-sql cache. reset its CompiledSql#mInUse flag
mCompiledSql.release();
}
- }
+ }
}
/**
@@ -149,7 +157,7 @@ private void releaseCompiledSqlIfNotInCache() {
*
* @return a unique identifier for this program
*/
- public final int getUniqueId() {
+ public final long getUniqueId() {
return nStatement;
}
@@ -175,7 +183,11 @@ protected void compile(String sql, boolean forceCompilation) {
*
* @param index The 1-based index to the parameter to bind null to
*/
+ @Override
public void bindNull(int index) {
+ if (mClosed) {
+ throw new IllegalStateException("program already closed");
+ }
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -194,7 +206,11 @@ public void bindNull(int index) {
* @param index The 1-based index to the parameter to bind
* @param value The value to bind
*/
+ @Override
public void bindLong(int index, long value) {
+ if (mClosed) {
+ throw new IllegalStateException("program already closed");
+ }
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -213,7 +229,11 @@ public void bindLong(int index, long value) {
* @param index The 1-based index to the parameter to bind
* @param value The value to bind
*/
+ @Override
public void bindDouble(int index, double value) {
+ if (mClosed) {
+ throw new IllegalStateException("program already closed");
+ }
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -232,10 +252,14 @@ public void bindDouble(int index, double value) {
* @param index The 1-based index to the parameter to bind
* @param value The value to bind
*/
+ @Override
public void bindString(int index, String value) {
if (value == null) {
throw new IllegalArgumentException("the bind value at index " + index + " is null");
}
+ if (mClosed) {
+ throw new IllegalStateException("program already closed");
+ }
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -254,10 +278,14 @@ public void bindString(int index, String value) {
* @param index The 1-based index to the parameter to bind
* @param value The value to bind
*/
+ @Override
public void bindBlob(int index, byte[] value) {
if (value == null) {
throw new IllegalArgumentException("the bind value at index " + index + " is null");
}
+ if (mClosed) {
+ throw new IllegalStateException("program already closed");
+ }
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -272,7 +300,11 @@ public void bindBlob(int index, byte[] value) {
/**
* Clears all existing bindings. Unset bindings are treated as NULL.
*/
+ @Override
public void clearBindings() {
+ if (mClosed) {
+ throw new IllegalStateException("program already closed");
+ }
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
}
@@ -288,6 +320,9 @@ public void clearBindings() {
* Release this program's resources, making it invalid.
*/
public void close() {
+ if (mClosed) {
+ return;
+ }
if (!mDatabase.isOpen()) {
return;
}
@@ -297,6 +332,7 @@ public void close() {
} finally {
mDatabase.unlock();
}
+ mClosed = true;
}
/**
diff --git a/src/net/sqlcipher/database/SQLiteQuery.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQuery.java
similarity index 67%
rename from src/net/sqlcipher/database/SQLiteQuery.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQuery.java
index e0bb0d47..c87bd590 100644
--- a/src/net/sqlcipher/database/SQLiteQuery.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQuery.java
@@ -17,6 +17,8 @@
package net.sqlcipher.database;
import net.sqlcipher.*;
+import android.database.sqlite.SQLiteDatabaseCorruptException;
+import android.database.sqlite.SQLiteMisuseException;
import android.os.SystemClock;
import android.util.Log;
@@ -32,18 +34,17 @@ public class SQLiteQuery extends SQLiteProgram {
/** The index of the unbound OFFSET parameter */
private int mOffsetIndex;
-
+
/** Args to bind on requery */
private String[] mBindArgs;
-
- private boolean mClosed = false;
+ private Object[] mObjectBindArgs;
/**
* Create a persistent query object.
- *
+ *
* @param db The database that this query object is associated with
- * @param query The SQL string for this query.
- * @param offsetIndex The 1-based index to the OFFSET parameter,
+ * @param query The SQL string for this query.
+ * @param offsetIndex The 1-based index to the OFFSET parameter,
*/
/* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) {
super(db, query);
@@ -52,17 +53,25 @@ public class SQLiteQuery extends SQLiteProgram {
mBindArgs = bindArgs;
}
+ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, Object[] bindArgs) {
+ super(db, query);
+ mOffsetIndex = offsetIndex;
+ mObjectBindArgs = bindArgs;
+ int length = mObjectBindArgs != null ? mObjectBindArgs.length : 0;
+ mBindArgs = new String[length];
+ }
+
/**
* Reads rows into a buffer. This method acquires the database lock.
*
* @param window The window to fill into
* @return number of total rows in the query
*/
- /* package */ int fillWindow(CursorWindow window,
- int maxRead, int lastPos) {
+ /* package */
+ int fillWindow(CursorWindow window,
+ int maxRead, int lastPos) {
long timeStart = SystemClock.uptimeMillis();
mDatabase.lock();
- mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
try {
acquireReference();
try {
@@ -70,14 +79,16 @@ public class SQLiteQuery extends SQLiteProgram {
// if the start pos is not equal to 0, then most likely window is
// too small for the data set, loading by another thread
// is not safe in this situation. the native code will ignore maxRead
- int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
- maxRead, lastPos);
+ int numRows = native_fill_window(window,
+ window.getStartPosition(),
+ window.getRequiredPosition(),
+ mOffsetIndex,
+ maxRead, lastPos);
// Logging
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
Log.d(TAG, "fillWindow(): " + mSql);
}
- mDatabase.logTimeStat(mSql, timeStart);
return numRows;
} catch (IllegalStateException e){
// simply ignore it
@@ -98,7 +109,7 @@ public class SQLiteQuery extends SQLiteProgram {
* Get the column count for the statement. Only valid on query based
* statements. The database must be locked
* when calling this method.
- *
+ *
* @return The number of column in the statement's result set.
*/
/* package */ int columnCountLocked() {
@@ -113,7 +124,7 @@ public class SQLiteQuery extends SQLiteProgram {
/**
* Retrieves the column name for the given column index. The database must be locked
* when calling this method.
- *
+ *
* @param columnIndex the index of the column to get the name for
* @return The requested column's name
*/
@@ -125,17 +136,11 @@ public class SQLiteQuery extends SQLiteProgram {
releaseReference();
}
}
-
+
@Override
public String toString() {
return "SQLiteQuery: " + mSql;
}
-
- @Override
- public void close() {
- super.close();
- mClosed = true;
- }
/**
* Called by SQLiteCursor when it is requeried.
@@ -144,8 +149,12 @@ public void close() {
if (mBindArgs != null) {
int len = mBindArgs.length;
try {
- for (int i = 0; i < len; i++) {
- super.bindString(i + 1, mBindArgs[i]);
+ if(mObjectBindArgs != null) {
+ bindArguments(mObjectBindArgs);
+ } else {
+ for (int i = 0; i < len; i++) {
+ super.bindString(i + 1, mBindArgs[i]);
+ }
}
} catch (SQLiteMisuseException e) {
StringBuilder errMsg = new StringBuilder("mSql " + mSql);
@@ -156,7 +165,7 @@ public void close() {
errMsg.append(" ");
IllegalStateException leakProgram = new IllegalStateException(
errMsg.toString(), e);
- throw leakProgram;
+ throw leakProgram;
}
}
}
@@ -185,8 +194,37 @@ public void bindString(int index, String value) {
if (!mClosed) super.bindString(index, value);
}
- private final native int native_fill_window(CursorWindow window,
- int startPos, int offsetParam, int maxRead, int lastPos);
+ public void bindArguments(Object[] args){
+ if(args != null && args.length > 0){
+ for(int i = 0; i < args.length; i++){
+ Object value = args[i];
+ if(value == null){
+ bindNull(i + 1);
+ } else if (value instanceof Double) {
+ bindDouble(i + 1, (Double)value);
+ } else if (value instanceof Float) {
+ float number = ((Number)value).floatValue();
+ bindDouble(i + 1, Double.valueOf(number));
+ } else if (value instanceof Long) {
+ bindLong(i + 1, (Long)value);
+ } else if(value instanceof Integer) {
+ int number = ((Number) value).intValue();
+ bindLong(i + 1, Long.valueOf(number));
+ } else if (value instanceof Boolean) {
+ bindLong(i + 1, (Boolean)value ? 1 : 0);
+ } else if (value instanceof byte[]) {
+ bindBlob(i + 1, (byte[])value);
+ } else {
+ bindString(i + 1, value.toString());
+ }
+ }
+ }
+ }
+
+ private final native int native_fill_window(CursorWindow window,
+ int startPos, int requiredPos,
+ int offsetParam, int maxRead,
+ int lastPos);
private final native int native_column_count();
diff --git a/src/net/sqlcipher/database/SQLiteQueryBuilder.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryBuilder.java
similarity index 99%
rename from src/net/sqlcipher/database/SQLiteQueryBuilder.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryBuilder.java
index 3191b626..d47f5593 100644
--- a/src/net/sqlcipher/database/SQLiteQueryBuilder.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryBuilder.java
@@ -15,6 +15,7 @@
*/
package net.sqlcipher.database;
+
import net.sqlcipher.*;
import android.provider.BaseColumns;
@@ -273,7 +274,7 @@ public static void appendColumns(StringBuilder s, String[] columns) {
* @see android.content.ContentResolver#query(android.net.Uri, String[],
* String, String[], String)
*/
- public android.database.Cursor query(SQLiteDatabase db, String[] projectionIn,
+ public Cursor query(SQLiteDatabase db, String[] projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder) {
return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
@@ -312,7 +313,7 @@ public android.database.Cursor query(SQLiteDatabase db, String[] projectionIn,
* @see android.content.ContentResolver#query(android.net.Uri, String[],
* String, String[], String)
*/
- public android.database.Cursor query(SQLiteDatabase db, String[] projectionIn,
+ public Cursor query(SQLiteDatabase db, String[] projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder, String limit) {
if (mTables == null) {
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryStats.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryStats.java
new file mode 100644
index 00000000..4b36c05f
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryStats.java
@@ -0,0 +1,20 @@
+package net.sqlcipher.database;
+
+public class SQLiteQueryStats {
+ long totalQueryResultSize = 0L;
+ long largestIndividualRowSize = 0L;
+
+ public SQLiteQueryStats(long totalQueryResultSize,
+ long largestIndividualRowSize) {
+ this.totalQueryResultSize = totalQueryResultSize;
+ this.largestIndividualRowSize = largestIndividualRowSize;
+ }
+
+ public long getTotalQueryResultSize(){
+ return totalQueryResultSize;
+ }
+
+ public long getLargestIndividualRowSize(){
+ return largestIndividualRowSize;
+ }
+}
diff --git a/src/net/sqlcipher/database/SQLiteStatement.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.java
similarity index 87%
rename from src/net/sqlcipher/database/SQLiteStatement.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.java
index cb8e17fe..84b7b4c2 100644
--- a/src/net/sqlcipher/database/SQLiteStatement.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.java
@@ -17,6 +17,7 @@
package net.sqlcipher.database;
import android.os.SystemClock;
+import androidx.sqlite.db.SupportSQLiteStatement;
/**
* A pre-compiled statement against a {@link SQLiteDatabase} that can be reused.
@@ -27,7 +28,8 @@
* SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple
* threads should perform its own synchronization when using the SQLiteStatement.
*/
-public class SQLiteStatement extends SQLiteProgram
+public class SQLiteStatement extends SQLiteProgram implements
+ SupportSQLiteStatement
{
/**
* Don't use SQLiteStatement constructor directly, please use
@@ -46,6 +48,7 @@ public class SQLiteStatement extends SQLiteProgram
* @throws android.database.SQLException If the SQL string is invalid for
* some reason
*/
+ @Override
public void execute() {
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
@@ -56,7 +59,6 @@ public void execute() {
acquireReference();
try {
native_execute();
- mDatabase.logTimeStat(mSql, timeStart);
} finally {
releaseReference();
mDatabase.unlock();
@@ -72,6 +74,7 @@ public void execute() {
* @throws android.database.SQLException If the SQL string is invalid for
* some reason
*/
+ @Override
public long executeInsert() {
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
@@ -82,7 +85,6 @@ public long executeInsert() {
acquireReference();
try {
native_execute();
- mDatabase.logTimeStat(mSql, timeStart);
return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1;
} finally {
releaseReference();
@@ -90,6 +92,24 @@ public long executeInsert() {
}
}
+ @Override
+ public int executeUpdateDelete() {
+ if (!mDatabase.isOpen()) {
+ throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
+ }
+ long timeStart = SystemClock.uptimeMillis();
+ mDatabase.lock();
+
+ acquireReference();
+ try {
+ native_execute();
+ return mDatabase.lastChangeCount();
+ } finally {
+ releaseReference();
+ mDatabase.unlock();
+ }
+ }
+
/**
* Execute a statement that returns a 1 by 1 table with a numeric value.
* For example, SELECT COUNT(*) FROM table;
@@ -98,6 +118,7 @@ public long executeInsert() {
*
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
*/
+ @Override
public long simpleQueryForLong() {
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
@@ -108,7 +129,6 @@ public long simpleQueryForLong() {
acquireReference();
try {
long retValue = native_1x1_long();
- mDatabase.logTimeStat(mSql, timeStart);
return retValue;
} finally {
releaseReference();
@@ -124,6 +144,7 @@ public long simpleQueryForLong() {
*
* @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows
*/
+ @Override
public String simpleQueryForString() {
if (!mDatabase.isOpen()) {
throw new IllegalStateException("database " + mDatabase.getPath() + " already closed");
@@ -134,7 +155,6 @@ public String simpleQueryForString() {
acquireReference();
try {
String retValue = native_1x1_string();
- mDatabase.logTimeStat(mSql, timeStart);
return retValue;
} finally {
releaseReference();
diff --git a/src/net/sqlcipher/database/SQLiteTransactionListener.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteTransactionListener.java
similarity index 100%
rename from src/net/sqlcipher/database/SQLiteTransactionListener.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteTransactionListener.java
diff --git a/src/net/sqlcipher/database/SqliteWrapper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java
similarity index 96%
rename from src/net/sqlcipher/database/SqliteWrapper.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java
index 9772b063..1d15f99e 100644
--- a/src/net/sqlcipher/database/SqliteWrapper.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java
@@ -20,8 +20,10 @@
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import android.database.Cursor;
+
import net.sqlcipher.*;
+
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
@@ -64,7 +66,7 @@ public static Cursor query(Context context, ContentResolver resolver, Uri uri,
}
}
- public static boolean requery(Context context, Cursor cursor) {
+ public static boolean requery(Context context, android.database.Cursor cursor) {
try {
return cursor.requery();
} catch (SQLiteException e) {
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportFactory.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportFactory.java
new file mode 100644
index 00000000..2be2c2b2
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportFactory.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 Mark L. Murphy
+ *
+ * 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 net.sqlcipher.database;
+
+import androidx.sqlite.db.SupportSQLiteOpenHelper;
+
+public class SupportFactory implements SupportSQLiteOpenHelper.Factory {
+ private final byte[] passphrase;
+ private final SQLiteDatabaseHook hook;
+ private final boolean clearPassphrase;
+
+ public SupportFactory(byte[] passphrase) {
+ this(passphrase, (SQLiteDatabaseHook)null);
+ }
+
+ public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook) {
+ this(passphrase, hook, true);
+ }
+
+ public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook,
+ boolean clearPassphrase) {
+ this.passphrase = passphrase;
+ this.hook = hook;
+ this.clearPassphrase = clearPassphrase;
+ }
+
+ @Override
+ public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
+ return new SupportHelper(configuration, passphrase, hook, clearPassphrase);
+ }
+}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportHelper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportHelper.java
new file mode 100644
index 00000000..26960617
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportHelper.java
@@ -0,0 +1,118 @@
+ /*
+ * Copyright (C) 2019 Mark L. Murphy
+ *
+ * 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 net.sqlcipher.database;
+
+import android.database.sqlite.SQLiteException;
+import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteOpenHelper;
+
+public class SupportHelper implements SupportSQLiteOpenHelper {
+ private SQLiteOpenHelper standardHelper;
+ private byte[] passphrase;
+ private final boolean clearPassphrase;
+
+ SupportHelper(final SupportSQLiteOpenHelper.Configuration configuration,
+ byte[] passphrase, final SQLiteDatabaseHook hook,
+ boolean clearPassphrase) {
+ SQLiteDatabase.loadLibs(configuration.context);
+ this.passphrase = passphrase;
+ this.clearPassphrase = clearPassphrase;
+
+ standardHelper =
+ new SQLiteOpenHelper(configuration.context, configuration.name,
+ null, configuration.callback.version, hook) {
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ configuration.callback.onCreate(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion,
+ int newVersion) {
+ configuration.callback.onUpgrade(db, oldVersion,
+ newVersion);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion,
+ int newVersion) {
+ configuration.callback.onDowngrade(db, oldVersion,
+ newVersion);
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ configuration.callback.onOpen(db);
+ }
+
+ @Override
+ public void onConfigure(SQLiteDatabase db) {
+ configuration.callback.onConfigure(db);
+ }
+ };
+ }
+
+ @Override
+ public String getDatabaseName() {
+ return standardHelper.getDatabaseName();
+ }
+
+ @Override
+ public void setWriteAheadLoggingEnabled(boolean enabled) {
+ standardHelper.setWriteAheadLoggingEnabled(enabled);
+ }
+
+ @Override
+ public SupportSQLiteDatabase getWritableDatabase() {
+ SQLiteDatabase result;
+ try {
+ result = standardHelper.getWritableDatabase(passphrase);
+ } catch (SQLiteException ex){
+ if(passphrase != null){
+ boolean isCleared = true;
+ for(byte b : passphrase){
+ isCleared = isCleared && (b == (byte)0);
+ }
+ if (isCleared) {
+ throw new IllegalStateException("The passphrase appears to be cleared. This happens by " +
+ "default the first time you use the factory to open a database, so we can remove the " +
+ "cleartext passphrase from memory. If you close the database yourself, please use a " +
+ "fresh SupportFactory to reopen it. If something else (e.g., Room) closed the " +
+ "database, and you cannot control that, use SupportFactory boolean constructor option " +
+ "to opt out of the automatic password clearing step. See the project README for more information.", ex);
+ }
+ }
+ throw ex;
+ }
+ if(clearPassphrase && passphrase != null) {
+ for (int i = 0; i < passphrase.length; i++) {
+ passphrase[i] = (byte)0;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public SupportSQLiteDatabase getReadableDatabase() {
+ return getWritableDatabase();
+ }
+
+ @Override
+ public void close() {
+ standardHelper.close();
+ }
+}
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/database/package-info.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/package-info.java
new file mode 100644
index 00000000..84c8b7be
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the SQLCipher database managements classes that an application would use to manage its own private database.
+ */
+package net.sqlcipher.database;
diff --git a/android-database-sqlcipher/src/main/java/net/sqlcipher/package-info.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/package-info.java
new file mode 100644
index 00000000..c21dbf16
--- /dev/null
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains classes to explore data returned from a SQLCipher database.
+ */
+package net.sqlcipher;
diff --git a/android-database-sqlcipher/src/main/res/values/android_database_sqlcipher_strings.xml b/android-database-sqlcipher/src/main/res/values/android_database_sqlcipher_strings.xml
new file mode 100644
index 00000000..ddedb587
--- /dev/null
+++ b/android-database-sqlcipher/src/main/res/values/android_database_sqlcipher_strings.xml
@@ -0,0 +1,12 @@
+
+
+ Zetetic, LLC
+ https://www.zetetic.net/sqlcipher/
+ SQLCipher for Android
+ Android SQLite API based on SQLCipher
+ https://www.zetetic.net/sqlcipher/
+ ${clientVersionNumber}
+ true
+ https://github.com/sqlcipher/android-database-sqlcipher
+ https://www.zetetic.net/sqlcipher/license/
+
diff --git a/assets/icudt46l.zip b/assets/icudt46l.zip
deleted file mode 100644
index 91dc7f71..00000000
Binary files a/assets/icudt46l.zip and /dev/null differ
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 00000000..0751426a
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,99 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven {
+ url "https://plugins.gradle.org/m2/"
+ }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.3.1'
+ classpath "gradle.plugin.org.ec4j.gradle:editorconfig-gradle-plugin:0.0.3"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+ext {
+ if(project.hasProperty('sqlcipherAndroidClientVersion')) {
+ clientVersionNumber = "${sqlcipherAndroidClientVersion}"
+ } else {
+ clientVersionNumber = "UndefinedBuildNumber"
+ }
+ mavenPackaging = "aar"
+ mavenGroup = "net.zetetic"
+ mavenArtifactId = "android-database-sqlcipher"
+ mavenLocalRepositoryPrefix = "file://"
+ if(project.hasProperty('publishLocal') && publishLocal.toBoolean()){
+ mavenSnapshotRepositoryUrl = "outputs/snapshot"
+ mavenReleaseRepositoryUrl = "outputs/release"
+ } else {
+ mavenLocalRepositoryPrefix = ""
+ mavenSnapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots"
+ mavenReleaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
+ }
+ if(project.hasProperty('publishSnapshot') && publishSnapshot.toBoolean()){
+ mavenVersionName = "${clientVersionNumber}-SNAPSHOT"
+ } else {
+ mavenVersionName = "${clientVersionNumber}"
+ }
+ if(project.hasProperty('nexusUsername')){
+ nexusUsername = "${nexusUsername}"
+ }
+ if(project.hasProperty('nexusPassword')){
+ nexusPassword = "${nexusPassword}"
+ }
+ mavenPomDescription = "SQLCipher for Android is a plugin to SQLite that provides full database encryption."
+ mavenPomUrl = "https://www.zetetic.net/sqlcipher"
+ mavenScmUrl = "https://github.com/sqlcipher/android-database-sqlcipher.git"
+ mavenScmConnection = "scm:git:https://github.com/sqlcipher/android-database-sqlcipher.git"
+ mavenScmDeveloperConnection = "scm:git:https://github.com/sqlcipher/android-database-sqlcipher.git"
+ mavenLicenseUrl = "https://www.zetetic.net/sqlcipher/license/"
+ mavenDeveloperName = "Zetetic Support"
+ mavenDeveloperEmail = "support@zetetic.net"
+ mavenDeveloperOrganization = "Zetetic LLC"
+ mavenDeveloperUrl = "https://www.zetetic.net"
+ minimumAndroidSdkVersion = 21
+ minimumAndroid64BitSdkVersion = 21
+ targetAndroidSdkVersion = 26
+ compileAndroidSdkVersion = 26
+ mainProjectName = "android-database-sqlcipher"
+ nativeRootOutputDir = "${projectDir}/${mainProjectName}/src/main"
+ if(project.hasProperty('sqlcipherRoot')) {
+ sqlcipherDir = "${sqlcipherRoot}"
+ }
+ if(project.hasProperty('opensslAndroidNativeRoot') && "${opensslAndroidNativeRoot}") {
+ androidNativeRootDir = "${opensslAndroidNativeRoot}"
+ } else {
+ androidNativeRootDir = "${nativeRootOutputDir}/external/android-libs"
+ }
+ if(project.hasProperty('opensslRoot')) {
+ opensslDir = "${opensslRoot}"
+ }
+ if(project.hasProperty('debugBuild') && debugBuild.toBoolean()) {
+ otherSqlcipherCFlags = "-fstack-protector-all"
+ ndkBuildType="NDK_DEBUG=1"
+ } else {
+ otherSqlcipherCFlags = "-DLOG_NDEBUG -fstack-protector-all"
+ ndkBuildType="NDK_DEBUG=0"
+ }
+ if(project.hasProperty('sqlcipherCFlags')
+ && project.sqlcipherCFlags?.trim()
+ && project.sqlcipherCFlags?.contains('SQLITE_HAS_CODEC')
+ && project.sqlcipherCFlags?.contains('SQLITE_TEMP_STORE')) {
+ sqlcipherCFlags = "${sqlcipherCFlags}"
+ } else {
+ if(!project.gradle.startParameter.taskNames.toString().contains('clean')){
+ throw new InvalidUserDataException("SQLCIPHER_CFLAGS environment variable must be specified and include at least '-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2'")
+ }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/dist/SQLCipherForAndroid-SDK/LICENSE b/dist/SQLCipherForAndroid-SDK/LICENSE
deleted file mode 100644
index d6456956..00000000
--- a/dist/SQLCipherForAndroid-SDK/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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.
diff --git a/dist/SQLCipherForAndroid-SDK/README b/dist/SQLCipherForAndroid-SDK/README
deleted file mode 100644
index 8a46bd8d..00000000
--- a/dist/SQLCipherForAndroid-SDK/README
+++ /dev/null
@@ -1,63 +0,0 @@
-
-SQLCipher for Android v1 (0.0.6-FINAL)
-2011/11/29
-
-CHANGELOG
-- includes icu44 data file for devices without it built-in ( < 2.2)
-- complete support for original Android SQLite/database package APIs
-- improved platform support through unified library (less lib .so files)
-- added support for CrossProcess Cursors
-- now support Observers/callbacks
-
-CURRENT TESTED PLATFORM SUPPORT
-
-Android 2.1 (SDK Level 7)
-Android 2.2 (SDK Level 8)
-Android 2.3 (SDK Level 9)
-Android 2.3.3 (SDK Level 10)
-Android 3.0 (SDK Level 11)
-Android 3.1 (SDK Level 12)
-Android 3.2 (SDK Level 13)
-Android 4.0 (Not Tested)
-
-HOW TO
-
-1) copy the libs folder into your project. If you are targeting 2.2 and below, include the 'icudt44l.zip' in the assets folder as well.
-
-2) Update your import path from android.database to info.guardianproject.database and you are almost there!
-
-3) then when you open your database, just pass a variable argument to the open database method with your password:
-
- SQLiteDatabase.loadLibs(this); //first init the db libraries with the context
- SQLiteOpenHelper.getWritableDatabase("thisismysecret"):
-
-4) run your app to ensure all the libraries are being loaded correctly, and that your app runs.
-
-5) You will need to implement a passcode entry user interface to your application, as well.
-The longer the passcode, the better.
-
-ABOUT
-
-More info and source code at:
-https://guardianproject.info/code/sqlcipher/
-http://sqlcipher.net
-
-Or contact us:
-root@guardianproject.info
-#guardianproject on freenode
-
-EXPORT CONTROL
-
-This distribution includes cryptographic software. The country in which you currently reside may have restrictions
-on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any
-encryption software, please check your country's laws, regulations and policies concerning the import, possession,
-or use, and re-export of encryption software, to see if this is permitted. See for more
-information.
-
-The U.S. Government Department of Commerce, Bureau of Industry and Security (BIS), has classified this software as
-Export Commodity Control Number (ECCN) 5D002.C.1, which includes information security software using or performing
-cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution
-makes it eligible for export under the License Exception ENC Technology Software Unrestricted (TSU) exception
-(see the BIS Export Administration Regulations, Section 740.13) for both object code and source code.
-
-More information is available here: https://guardianproject.info/home/export-information/
diff --git a/dist/SQLCipherForAndroid-SDK/SQLCIPHER_LICENSE b/dist/SQLCipherForAndroid-SDK/SQLCIPHER_LICENSE
deleted file mode 100644
index 21566c58..00000000
--- a/dist/SQLCipherForAndroid-SDK/SQLCIPHER_LICENSE
+++ /dev/null
@@ -1,26 +0,0 @@
-http://sqlcipher.net
-
- Copyright (c) 2010 Zetetic LLC
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the ZETETIC LLC nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/dist/SQLCipherForAndroid-SDK/assets/icudt44l.zip b/dist/SQLCipherForAndroid-SDK/assets/icudt44l.zip
deleted file mode 100644
index 3b6372e5..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/assets/icudt44l.zip and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/libs/armeabi/libdatabase_sqlcipher.so b/dist/SQLCipherForAndroid-SDK/libs/armeabi/libdatabase_sqlcipher.so
deleted file mode 100755
index 1cc874d5..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/libs/armeabi/libdatabase_sqlcipher.so and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/libs/armeabi/libsqlcipher_android.so b/dist/SQLCipherForAndroid-SDK/libs/armeabi/libsqlcipher_android.so
deleted file mode 100755
index cbdff2ff..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/libs/armeabi/libsqlcipher_android.so and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/libs/armeabi/libstlport_shared.so b/dist/SQLCipherForAndroid-SDK/libs/armeabi/libstlport_shared.so
deleted file mode 100755
index 165ca68f..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/libs/armeabi/libstlport_shared.so and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/libs/commons-codec.jar b/dist/SQLCipherForAndroid-SDK/libs/commons-codec.jar
deleted file mode 100644
index 957b6752..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/libs/commons-codec.jar and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/libs/guava-r09.jar b/dist/SQLCipherForAndroid-SDK/libs/guava-r09.jar
deleted file mode 100644
index f8da8b1c..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/libs/guava-r09.jar and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/libs/sqlcipher.jar b/dist/SQLCipherForAndroid-SDK/libs/sqlcipher.jar
deleted file mode 100644
index b90c98ba..00000000
Binary files a/dist/SQLCipherForAndroid-SDK/libs/sqlcipher.jar and /dev/null differ
diff --git a/dist/SQLCipherForAndroid-SDK/samples/basic/example/EventDataSQLHelper.java b/dist/SQLCipherForAndroid-SDK/samples/basic/example/EventDataSQLHelper.java
deleted file mode 100644
index 9580615a..00000000
--- a/dist/SQLCipherForAndroid-SDK/samples/basic/example/EventDataSQLHelper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package example;
-
-import info.guardianproject.database.sqlcipher.SQLiteDatabase;
-import info.guardianproject.database.sqlcipher.SQLiteOpenHelper;
-import android.content.Context;
-import android.provider.BaseColumns;
-import android.util.Log;
-
-/** Helper to the database, manages versions and creation */
-public class EventDataSQLHelper extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "events.db";
- private static final int DATABASE_VERSION = 1;
-
- // Table name
- public static final String TABLE = "events";
-
- // Columns
- public static final String TIME = "time";
- public static final String TITLE = "title";
-
- public EventDataSQLHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- String sql = "create table " + TABLE + "( " + BaseColumns._ID
- + " integer primary key autoincrement, " + TIME + " integer, "
- + TITLE + " text not null);";
- Log.d("EventsData", "onCreate: " + sql);
- db.execSQL(sql);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion >= newVersion)
- return;
-
- String sql = null;
- if (oldVersion == 1)
- sql = "alter table " + TABLE + " add note text;";
- if (oldVersion == 2)
- sql = "";
-
- Log.d("EventsData", "onUpgrade : " + sql);
- if (sql != null)
- db.execSQL(sql);
- }
-
-}
diff --git a/dist/SQLCipherForAndroid-SDK/samples/basic/example/SQLDemoActivity.java b/dist/SQLCipherForAndroid-SDK/samples/basic/example/SQLDemoActivity.java
deleted file mode 100644
index 245f271b..00000000
--- a/dist/SQLCipherForAndroid-SDK/samples/basic/example/SQLDemoActivity.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package example;
-
-import android.database.Cursor;
-import info.guardianproject.database.sqlcipher.SQLiteDatabase;
-import android.app.Activity;
-import android.content.ContentValues;
-import android.os.Bundle;
-import android.util.Log;
-
-public class SQLDemoActivity extends Activity {
- EventDataSQLHelper eventsData;
-
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- //you must set Context on SQLiteDatabase first
- SQLiteDatabase.loadLibs(this);
-
- String password = "foo123";
-
- eventsData = new EventDataSQLHelper(this);
-
- //then you can open the database using a password
- SQLiteDatabase db = eventsData.getWritableDatabase(password);
-
- for (int i = 1; i < 100; i++)
- addEvent("Hello Android Event: " + i, db);
-
- db.close();
-
- db = eventsData.getReadableDatabase(password);
-
- Cursor cursor = getEvents(db);
- showEvents(cursor);
-
- db.close();
-
- }
-
- @Override
- public void onDestroy() {
- eventsData.close();
- }
-
- private void addEvent(String title, SQLiteDatabase db) {
-
- ContentValues values = new ContentValues();
- values.put(EventDataSQLHelper.TIME, System.currentTimeMillis());
- values.put(EventDataSQLHelper.TITLE, title);
- db.insert(EventDataSQLHelper.TABLE, null, values);
- }
-
- private Cursor getEvents(SQLiteDatabase db) {
-
- Cursor cursor = db.query(EventDataSQLHelper.TABLE, null, null, null, null,
- null, null);
-
- startManagingCursor(cursor);
- return cursor;
- }
-
- private void showEvents(Cursor cursor) {
- StringBuilder ret = new StringBuilder("Saved Events:\n\n");
- while (cursor.moveToNext()) {
- long id = cursor.getLong(0);
- long time = cursor.getLong(1);
- String title = cursor.getString(2);
- ret.append(id + ": " + time + ": " + title + "\n");
- }
-
- Log.i("sqldemo",ret.toString());
- }
-}
\ No newline at end of file
diff --git a/dist/SQLCipherForAndroid-SDK/samples/notepadbot b/dist/SQLCipherForAndroid-SDK/samples/notepadbot
deleted file mode 160000
index 28eee1b8..00000000
--- a/dist/SQLCipherForAndroid-SDK/samples/notepadbot
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 28eee1b847359ce432a81ac5a675802927b953a7
diff --git a/external/Android.mk b/external/Android.mk
deleted file mode 100644
index d7d25724..00000000
--- a/external/Android.mk
+++ /dev/null
@@ -1,388 +0,0 @@
-#
-# Before building using this do:
-# make -f Android.mk build-local-hack
-# ndk-build
-# ndk-build
-# make -f Android.mk copy-libs-hack
-
-PROJECT_ROOT_PATH := $(call my-dir)
-LOCAL_PATH := $(PROJECT_ROOT_PATH)
-LOCAL_PRELINK_MODULE := false
-
-# how on earth to you make this damn Android build system run cmd line progs?!?!
-build-local-hack: sqlcipher/sqlite3.c ../obj/local/armeabi/libcrypto.so
-
-sqlcipher/sqlite3.c:
- cd sqlcipher && ./configure --enable-tempstore=yes CFLAGS="-DSQL_HAS_CODEC" LDFLAGS="-lcrypto"
- make -C sqlcipher sqlite3.c
-
-# TODO include this Android.mk to integrate this into the build
-../obj/local/armeabi/libcrypto.so:
- cd openssl && ndk-build -j4
- mkdir -p ../obj/local/armeabi
- install -p openssl/libs/armeabi/libcrypto.so openssl/libs/armeabi/libssl.so \
- ../obj/local/armeabi/
-
-copy-libs-hack: build-local-hack
- install -p -m644 openssl/libs/armeabi/*.so ../obj/local/armeabi/
- install -p -m644 libs/armeabi/*.so ../obj/local/armeabi/
-
-project_ldflags:= -Llibs/armeabi/ -Landroid-libs/
-
-#------------------------------------------------------------------------------#
-# libsqlite3
-
-# NOTE the following flags,
-# SQLITE_TEMP_STORE=3 causes all TEMP files to go into RAM. and thats the behavior we want
-# SQLITE_ENABLE_FTS3 enables usage of FTS3 - NOT FTS1 or 2.
-# SQLITE_DEFAULT_AUTOVACUUM=1 causes the databases to be subject to auto-vacuum
-android_sqlite_cflags := -DHAVE_USLEEP=1 -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 -DSQLITE_THREADSAFE=1 -DNDEBUG=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_BACKWARDS -DSQLITE_ENABLE_LOAD_EXTENSION
-
-sqlcipher_files := \
- sqlcipher/sqlite3.c
-
-sqlcipher_cflags := -DSQLITE_HAS_CODEC -DHAVE_FDATASYNC=0 -Dfdatasync=fsync
-
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlcipher_cflags)
-LOCAL_C_INCLUDES := includes openssl/include sqlcipher
-LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDLIBS += -lcrypto
-LOCAL_MODULE := libsqlcipher
-LOCAL_SRC_FILES := $(sqlcipher_files)
-
-include $(BUILD_STATIC_LIBRARY)
-
-#------------------------------------------------------------------------------#
-# libsqlcipher_android (our version of Android's libsqlite_android)
-
-# these are all files from various external git repos
-libsqlite3_android_local_src_files := \
- android-sqlite/android/sqlite3_android.cpp \
- android-sqlite/android/PhonebookIndex.cpp \
- android-sqlite/android/PhoneNumberUtils.cpp \
- android-sqlite/android/OldPhoneNumberUtils.cpp \
- android-sqlite/android/PhoneticStringUtils.cpp \
- String16.cpp \
- String8.cpp
-# android-sqlite/android/PhoneNumberUtilsTest.cpp \
-# android-sqlite/android/PhoneticStringUtilsTest.cpp \
-
-include $(CLEAR_VARS)
-
-## this might save us linking against the private android shared libraries like
-## libnativehelper.so, libutils.so, libcutils.so, libicuuc, libicui18n.so
-LOCAL_ALLOW_UNDEFINED_SYMBOLS := false
-
-# TODO this needs to depend on libsqlcipher being built, how to do that?
-#LOCAL_REQUIRED_MODULES += libsqlcipher libicui18n libicuuc
-LOCAL_STATIC_LIBRARIES := libsqlcipher libicui18n libicuuc
-
-LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlite_cflags) -DOS_PATH_SEPARATOR="'/'"
-
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/includes \
- $(LOCAL_PATH)/sqlcipher \
- $(LOCAL_PATH)/icu4c/i18n \
- $(LOCAL_PATH)/icu4c/common \
- $(LOCAL_PATH)/platform-system-core/include \
- $(LOCAL_PATH)/platform-frameworks-base/include
-
-LOCAL_LDFLAGS += -L$(LOCAL_PATH)/android-libs/ -L$(LOCAL_PATH)/libs/armeabi/
-LOCAL_LDLIBS := -llog -lutils -lcutils -lcrypto
-LOCAL_MODULE := libsqlcipher_android
-LOCAL_MODULE_FILENAME := libsqlcipher_android
-LOCAL_SRC_FILES := $(libsqlite3_android_local_src_files)
-
-include $(BUILD_SHARED_LIBRARY)
-
-#-------------------------
-# start icu project import
-#-------------------------
-
-#include $(LOCAL_PATH)/icu4c/Android.mk
-
-#LOCAL_PATH := $(call my-dir)
-#include $(CLEAR_VARS)
-
-#include $(CLEAR_VARS)
-
-ICU_COMMON_PATH := icu4c/common
-
-# new icu common build begin
-
-icu_src_files := \
- $(ICU_COMMON_PATH)/cmemory.c $(ICU_COMMON_PATH)/cstring.c \
- $(ICU_COMMON_PATH)/cwchar.c $(ICU_COMMON_PATH)/locmap.c \
- $(ICU_COMMON_PATH)/punycode.c $(ICU_COMMON_PATH)/putil.c \
- $(ICU_COMMON_PATH)/uarrsort.c $(ICU_COMMON_PATH)/ubidi.c \
- $(ICU_COMMON_PATH)/ubidiln.c $(ICU_COMMON_PATH)/ubidi_props.c \
- $(ICU_COMMON_PATH)/ubidiwrt.c $(ICU_COMMON_PATH)/ucase.c \
- $(ICU_COMMON_PATH)/ucasemap.c $(ICU_COMMON_PATH)/ucat.c \
- $(ICU_COMMON_PATH)/uchar.c $(ICU_COMMON_PATH)/ucln_cmn.c \
- $(ICU_COMMON_PATH)/ucmndata.c \
- $(ICU_COMMON_PATH)/ucnv2022.c $(ICU_COMMON_PATH)/ucnv_bld.c \
- $(ICU_COMMON_PATH)/ucnvbocu.c $(ICU_COMMON_PATH)/ucnv.c \
- $(ICU_COMMON_PATH)/ucnv_cb.c $(ICU_COMMON_PATH)/ucnv_cnv.c \
- $(ICU_COMMON_PATH)/ucnvdisp.c $(ICU_COMMON_PATH)/ucnv_err.c \
- $(ICU_COMMON_PATH)/ucnv_ext.c $(ICU_COMMON_PATH)/ucnvhz.c \
- $(ICU_COMMON_PATH)/ucnv_io.c $(ICU_COMMON_PATH)/ucnvisci.c \
- $(ICU_COMMON_PATH)/ucnvlat1.c $(ICU_COMMON_PATH)/ucnv_lmb.c \
- $(ICU_COMMON_PATH)/ucnvmbcs.c $(ICU_COMMON_PATH)/ucnvscsu.c \
- $(ICU_COMMON_PATH)/ucnv_set.c $(ICU_COMMON_PATH)/ucnv_u16.c \
- $(ICU_COMMON_PATH)/ucnv_u32.c $(ICU_COMMON_PATH)/ucnv_u7.c \
- $(ICU_COMMON_PATH)/ucnv_u8.c \
- $(ICU_COMMON_PATH)/udatamem.c \
- $(ICU_COMMON_PATH)/udataswp.c $(ICU_COMMON_PATH)/uenum.c \
- $(ICU_COMMON_PATH)/uhash.c $(ICU_COMMON_PATH)/uinit.c \
- $(ICU_COMMON_PATH)/uinvchar.c $(ICU_COMMON_PATH)/uloc.c \
- $(ICU_COMMON_PATH)/umapfile.c $(ICU_COMMON_PATH)/umath.c \
- $(ICU_COMMON_PATH)/umutex.c $(ICU_COMMON_PATH)/unames.c \
- $(ICU_COMMON_PATH)/unorm_it.c $(ICU_COMMON_PATH)/uresbund.c \
- $(ICU_COMMON_PATH)/ures_cnv.c $(ICU_COMMON_PATH)/uresdata.c \
- $(ICU_COMMON_PATH)/usc_impl.c $(ICU_COMMON_PATH)/uscript.c \
- $(ICU_COMMON_PATH)/ushape.c $(ICU_COMMON_PATH)/ustrcase.c \
- $(ICU_COMMON_PATH)/ustr_cnv.c $(ICU_COMMON_PATH)/ustrfmt.c \
- $(ICU_COMMON_PATH)/ustring.c $(ICU_COMMON_PATH)/ustrtrns.c \
- $(ICU_COMMON_PATH)/ustr_wcs.c $(ICU_COMMON_PATH)/utf_impl.c \
- $(ICU_COMMON_PATH)/utrace.c $(ICU_COMMON_PATH)/utrie.c \
- $(ICU_COMMON_PATH)/utypes.c $(ICU_COMMON_PATH)/wintz.c \
- $(ICU_COMMON_PATH)/utrie2_builder.c $(ICU_COMMON_PATH)/icuplug.c \
- $(ICU_COMMON_PATH)/propsvec.c $(ICU_COMMON_PATH)/ulist.c \
- $(ICU_COMMON_PATH)/uloc_tag.c
-
-icu_src_files += \
- $(ICU_COMMON_PATH)/bmpset.cpp $(ICU_COMMON_PATH)/unisetspan.cpp \
- $(ICU_COMMON_PATH)/brkeng.cpp $(ICU_COMMON_PATH)/brkiter.cpp \
- $(ICU_COMMON_PATH)/caniter.cpp $(ICU_COMMON_PATH)/chariter.cpp \
- $(ICU_COMMON_PATH)/dictbe.cpp $(ICU_COMMON_PATH)/locbased.cpp \
- $(ICU_COMMON_PATH)/locid.cpp $(ICU_COMMON_PATH)/locutil.cpp \
- $(ICU_COMMON_PATH)/normlzr.cpp $(ICU_COMMON_PATH)/parsepos.cpp \
- $(ICU_COMMON_PATH)/propname.cpp $(ICU_COMMON_PATH)/rbbi.cpp \
- $(ICU_COMMON_PATH)/rbbidata.cpp $(ICU_COMMON_PATH)/rbbinode.cpp \
- $(ICU_COMMON_PATH)/rbbirb.cpp $(ICU_COMMON_PATH)/rbbiscan.cpp \
- $(ICU_COMMON_PATH)/rbbisetb.cpp $(ICU_COMMON_PATH)/rbbistbl.cpp \
- $(ICU_COMMON_PATH)/rbbitblb.cpp $(ICU_COMMON_PATH)/resbund_cnv.cpp \
- $(ICU_COMMON_PATH)/resbund.cpp $(ICU_COMMON_PATH)/ruleiter.cpp \
- $(ICU_COMMON_PATH)/schriter.cpp $(ICU_COMMON_PATH)/serv.cpp \
- $(ICU_COMMON_PATH)/servlk.cpp $(ICU_COMMON_PATH)/servlkf.cpp \
- $(ICU_COMMON_PATH)/servls.cpp $(ICU_COMMON_PATH)/servnotf.cpp \
- $(ICU_COMMON_PATH)/servrbf.cpp $(ICU_COMMON_PATH)/servslkf.cpp \
- $(ICU_COMMON_PATH)/triedict.cpp $(ICU_COMMON_PATH)/ubrk.cpp \
- $(ICU_COMMON_PATH)/uchriter.cpp $(ICU_COMMON_PATH)/uhash_us.cpp \
- $(ICU_COMMON_PATH)/uidna.cpp $(ICU_COMMON_PATH)/uiter.cpp \
- $(ICU_COMMON_PATH)/unifilt.cpp $(ICU_COMMON_PATH)/unifunct.cpp \
- $(ICU_COMMON_PATH)/uniset.cpp $(ICU_COMMON_PATH)/uniset_props.cpp \
- $(ICU_COMMON_PATH)/unistr_case.cpp $(ICU_COMMON_PATH)/unistr_cnv.cpp \
- $(ICU_COMMON_PATH)/unistr.cpp $(ICU_COMMON_PATH)/unistr_props.cpp \
- $(ICU_COMMON_PATH)/unormcmp.cpp $(ICU_COMMON_PATH)/unorm.cpp \
- $(ICU_COMMON_PATH)/uobject.cpp $(ICU_COMMON_PATH)/uset.cpp \
- $(ICU_COMMON_PATH)/usetiter.cpp $(ICU_COMMON_PATH)/uset_props.cpp \
- $(ICU_COMMON_PATH)/usprep.cpp $(ICU_COMMON_PATH)/ustack.cpp \
- $(ICU_COMMON_PATH)/ustrenum.cpp $(ICU_COMMON_PATH)/utext.cpp \
- $(ICU_COMMON_PATH)/util.cpp $(ICU_COMMON_PATH)/util_props.cpp \
- $(ICU_COMMON_PATH)/uvector.cpp $(ICU_COMMON_PATH)/uvectr32.cpp \
- $(ICU_COMMON_PATH)/errorcode.cpp \
- $(ICU_COMMON_PATH)/bytestream.cpp $(ICU_COMMON_PATH)/stringpiece.cpp \
- $(ICU_COMMON_PATH)/mutex.cpp $(ICU_COMMON_PATH)/dtintrv.cpp \
- $(ICU_COMMON_PATH)/ucnvsel.cpp $(ICU_COMMON_PATH)/uvectr64.cpp \
- $(ICU_COMMON_PATH)/locavailable.cpp $(ICU_COMMON_PATH)/locdispnames.cpp \
- $(ICU_COMMON_PATH)/loclikely.cpp $(ICU_COMMON_PATH)/locresdata.cpp \
- $(ICU_COMMON_PATH)/normalizer2impl.cpp $(ICU_COMMON_PATH)/normalizer2.cpp \
- $(ICU_COMMON_PATH)/filterednormalizer2.cpp $(ICU_COMMON_PATH)/ucol_swp.cpp \
- $(ICU_COMMON_PATH)/uprops.cpp $(ICU_COMMON_PATH)/utrie2.cpp \
- $(ICU_COMMON_PATH)/charstr.cpp $(ICU_COMMON_PATH)/uts46.cpp \
- $(ICU_COMMON_PATH)/udata.cpp
-
-# This is the empty compiled-in icu data structure
-# that we need to satisfy the linker.
-icu_src_files += $(ICU_COMMON_PATH)/../stubdata/stubdata.c
-
-# new icu common build end
-
-icu_c_includes := \
- $(ICU_COMMON_PATH)/ \
- $(ICU_COMMON_PATH)//../i18n
-
-# We make the ICU data directory relative to $ANDROID_ROOT on Android, so both
-# device and sim builds can use the same codepath, and it's hard to break one
-# without noticing because the other still works.
-
-icu_local_cflags += -D_REENTRANT -DU_COMMON_IMPLEMENTATION -O3 -DHAVE_ANDROID_OS=1 -fvisibility=hidden
-icu_local_cflags += '-DICU_DATA_DIR_PREFIX_ENV_VAR="SQLCIPHER_ICU_PREFIX"'
-icu_local_cflags += '-DICU_DATA_DIR="/icu"'
-icu_local_ldlibs := -lc -lpthread -lm
-
-#
-# Build for the target (device).
-#
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(icu_src_files)
-LOCAL_C_INCLUDES := $(icu_c_includes)
-LOCAL_CFLAGS := $(icu_local_cflags) -DPIC -fPIC
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES += libgabi++
-LOCAL_LDLIBS += $(icu_local_ldlibs)
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := libicuuc
-include $(BUILD_STATIC_LIBRARY)
-
-#----------
-# end icuuc
-#----------
-
-#--------------
-# start icui18n
-#--------------
-include $(CLEAR_VARS)
-LOCAL_PATH := $(PROJECT_ROOT_PATH)
-#ICU_I18N_PATH := $(LOCAL_PATH)/icu4c/i18n
-ICU_I18N_PATH := icu4c/i18n
-
-# start new icu18n
-
-src_files := \
- $(ICU_I18N_PATH)/bocsu.c $(ICU_I18N_PATH)/ucln_in.c $(ICU_I18N_PATH)/decContext.c \
- $(ICU_I18N_PATH)/ulocdata.c $(ICU_I18N_PATH)/utmscale.c $(ICU_I18N_PATH)/decNumber.c
-
-src_files += \
- $(ICU_I18N_PATH)/indiancal.cpp $(ICU_I18N_PATH)/dtptngen.cpp $(ICU_I18N_PATH)/dtrule.cpp \
- $(ICU_I18N_PATH)/persncal.cpp $(ICU_I18N_PATH)/rbtz.cpp $(ICU_I18N_PATH)/reldtfmt.cpp \
- $(ICU_I18N_PATH)/taiwncal.cpp $(ICU_I18N_PATH)/tzrule.cpp $(ICU_I18N_PATH)/tztrans.cpp \
- $(ICU_I18N_PATH)/udatpg.cpp $(ICU_I18N_PATH)/vtzone.cpp \
- $(ICU_I18N_PATH)/anytrans.cpp $(ICU_I18N_PATH)/astro.cpp $(ICU_I18N_PATH)/buddhcal.cpp \
- $(ICU_I18N_PATH)/basictz.cpp $(ICU_I18N_PATH)/calendar.cpp $(ICU_I18N_PATH)/casetrn.cpp \
- $(ICU_I18N_PATH)/choicfmt.cpp $(ICU_I18N_PATH)/coleitr.cpp $(ICU_I18N_PATH)/coll.cpp \
- $(ICU_I18N_PATH)/cpdtrans.cpp $(ICU_I18N_PATH)/csdetect.cpp $(ICU_I18N_PATH)/csmatch.cpp \
- $(ICU_I18N_PATH)/csr2022.cpp $(ICU_I18N_PATH)/csrecog.cpp $(ICU_I18N_PATH)/csrmbcs.cpp \
- $(ICU_I18N_PATH)/csrsbcs.cpp $(ICU_I18N_PATH)/csrucode.cpp $(ICU_I18N_PATH)/csrutf8.cpp \
- $(ICU_I18N_PATH)/curramt.cpp $(ICU_I18N_PATH)/currfmt.cpp $(ICU_I18N_PATH)/currunit.cpp \
- $(ICU_I18N_PATH)/datefmt.cpp $(ICU_I18N_PATH)/dcfmtsym.cpp $(ICU_I18N_PATH)/decimfmt.cpp \
- $(ICU_I18N_PATH)/digitlst.cpp $(ICU_I18N_PATH)/dtfmtsym.cpp $(ICU_I18N_PATH)/esctrn.cpp \
- $(ICU_I18N_PATH)/fmtable_cnv.cpp $(ICU_I18N_PATH)/fmtable.cpp $(ICU_I18N_PATH)/format.cpp \
- $(ICU_I18N_PATH)/funcrepl.cpp $(ICU_I18N_PATH)/gregocal.cpp $(ICU_I18N_PATH)/gregoimp.cpp \
- $(ICU_I18N_PATH)/hebrwcal.cpp $(ICU_I18N_PATH)/inputext.cpp $(ICU_I18N_PATH)/islamcal.cpp \
- $(ICU_I18N_PATH)/japancal.cpp $(ICU_I18N_PATH)/measfmt.cpp $(ICU_I18N_PATH)/measure.cpp \
- $(ICU_I18N_PATH)/msgfmt.cpp $(ICU_I18N_PATH)/name2uni.cpp $(ICU_I18N_PATH)/nfrs.cpp \
- $(ICU_I18N_PATH)/nfrule.cpp $(ICU_I18N_PATH)/nfsubs.cpp $(ICU_I18N_PATH)/nortrans.cpp \
- $(ICU_I18N_PATH)/nultrans.cpp $(ICU_I18N_PATH)/numfmt.cpp $(ICU_I18N_PATH)/olsontz.cpp \
- $(ICU_I18N_PATH)/quant.cpp $(ICU_I18N_PATH)/rbnf.cpp $(ICU_I18N_PATH)/rbt.cpp \
- $(ICU_I18N_PATH)/rbt_data.cpp $(ICU_I18N_PATH)/rbt_pars.cpp $(ICU_I18N_PATH)/rbt_rule.cpp \
- $(ICU_I18N_PATH)/rbt_set.cpp $(ICU_I18N_PATH)/regexcmp.cpp $(ICU_I18N_PATH)/regexst.cpp \
- $(ICU_I18N_PATH)/rematch.cpp $(ICU_I18N_PATH)/remtrans.cpp $(ICU_I18N_PATH)/repattrn.cpp \
- $(ICU_I18N_PATH)/search.cpp $(ICU_I18N_PATH)/simpletz.cpp $(ICU_I18N_PATH)/smpdtfmt.cpp \
- $(ICU_I18N_PATH)/sortkey.cpp $(ICU_I18N_PATH)/strmatch.cpp $(ICU_I18N_PATH)/strrepl.cpp \
- $(ICU_I18N_PATH)/stsearch.cpp $(ICU_I18N_PATH)/tblcoll.cpp $(ICU_I18N_PATH)/timezone.cpp \
- $(ICU_I18N_PATH)/titletrn.cpp $(ICU_I18N_PATH)/tolowtrn.cpp $(ICU_I18N_PATH)/toupptrn.cpp \
- $(ICU_I18N_PATH)/translit.cpp $(ICU_I18N_PATH)/transreg.cpp $(ICU_I18N_PATH)/tridpars.cpp \
- $(ICU_I18N_PATH)/ucal.cpp $(ICU_I18N_PATH)/ucol_bld.cpp $(ICU_I18N_PATH)/ucol_cnt.cpp \
- $(ICU_I18N_PATH)/ucol.cpp $(ICU_I18N_PATH)/ucoleitr.cpp $(ICU_I18N_PATH)/ucol_elm.cpp \
- $(ICU_I18N_PATH)/ucol_res.cpp $(ICU_I18N_PATH)/ucol_sit.cpp $(ICU_I18N_PATH)/ucol_tok.cpp \
- $(ICU_I18N_PATH)/ucsdet.cpp $(ICU_I18N_PATH)/ucurr.cpp $(ICU_I18N_PATH)/udat.cpp \
- $(ICU_I18N_PATH)/umsg.cpp $(ICU_I18N_PATH)/unesctrn.cpp $(ICU_I18N_PATH)/uni2name.cpp \
- $(ICU_I18N_PATH)/unum.cpp $(ICU_I18N_PATH)/uregexc.cpp $(ICU_I18N_PATH)/uregex.cpp \
- $(ICU_I18N_PATH)/usearch.cpp $(ICU_I18N_PATH)/utrans.cpp $(ICU_I18N_PATH)/windtfmt.cpp \
- $(ICU_I18N_PATH)/winnmfmt.cpp $(ICU_I18N_PATH)/zonemeta.cpp $(ICU_I18N_PATH)/zstrfmt.cpp \
- $(ICU_I18N_PATH)/numsys.cpp $(ICU_I18N_PATH)/chnsecal.cpp \
- $(ICU_I18N_PATH)/cecal.cpp $(ICU_I18N_PATH)/coptccal.cpp $(ICU_I18N_PATH)/ethpccal.cpp \
- $(ICU_I18N_PATH)/brktrans.cpp $(ICU_I18N_PATH)/wintzimpl.cpp $(ICU_I18N_PATH)/plurrule.cpp \
- $(ICU_I18N_PATH)/plurfmt.cpp $(ICU_I18N_PATH)/dtitvfmt.cpp $(ICU_I18N_PATH)/dtitvinf.cpp \
- $(ICU_I18N_PATH)/tmunit.cpp $(ICU_I18N_PATH)/tmutamt.cpp $(ICU_I18N_PATH)/tmutfmt.cpp \
- $(ICU_I18N_PATH)/colldata.cpp $(ICU_I18N_PATH)/bmsearch.cpp $(ICU_I18N_PATH)/bms.cpp \
- $(ICU_I18N_PATH)/currpinf.cpp $(ICU_I18N_PATH)/uspoof.cpp $(ICU_I18N_PATH)/uspoof_impl.cpp \
- $(ICU_I18N_PATH)/uspoof_build.cpp \
- $(ICU_I18N_PATH)/regextxt.cpp $(ICU_I18N_PATH)/selfmt.cpp $(ICU_I18N_PATH)/uspoof_conf.cpp \
- $(ICU_I18N_PATH)/uspoof_wsconf.cpp $(ICU_I18N_PATH)/ztrans.cpp $(ICU_I18N_PATH)/zrule.cpp \
- $(ICU_I18N_PATH)/vzone.cpp $(ICU_I18N_PATH)/fphdlimp.cpp $(ICU_I18N_PATH)/fpositer.cpp\
- $(ICU_I18N_PATH)/locdspnm.cpp $(ICU_I18N_PATH)/decnumstr.cpp $(ICU_I18N_PATH)/ucol_wgt.cpp
-
-# end new icu18n
-
-c_includes = \
- $(ICU_I18N_PATH)/ \
- $(ICU_I18N_PATH)/../common
-
-#
-# Build for the target (device).
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(src_files)
-LOCAL_C_INCLUDES := $(c_includes) \
- abi/cpp/include
-LOCAL_CFLAGS += -D_REENTRANT -DPIC -DU_I18N_IMPLEMENTATION -fPIC -fvisibility=hidden
-LOCAL_CFLAGS += -O3
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_SHARED_LIBRARIES += libgabi++
-LOCAL_STATIC_LIBRARIES += libicuuc
-LOCAL_LDLIBS += -lc -lpthread -lm
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := libicui18n
-
-include $(BUILD_STATIC_LIBRARY)
-
-#------------
-# end icui18n
-#------------
-
-#---------------
-# start stubdata
-#---------------
-
-# Build configuration:
-#
-# "Large" includes all the supported locales.
-# Japanese includes US and Japan.
-# US-Euro is needed for IT or PL builds
-# Default is suitable for CS, DE, EN, ES, FR, NL
-# US has only EN and ES
-
-config := $(word 1, \
- $(if $(findstring ar,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring da,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring el,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring fi,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring he,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring hr,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring hu,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring id,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring ko,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring nb,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring pt,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring ro,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring ru,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring sk,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring sr,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring sv,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring th,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring tr,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring uk,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring zh,$(PRODUCT_LOCALES)),large) \
- $(if $(findstring ja,$(PRODUCT_LOCALES)),us-japan) \
- $(if $(findstring it,$(PRODUCT_LOCALES)),us-euro) \
- $(if $(findstring pl,$(PRODUCT_LOCALES)),us-euro) \
- $(if $(findstring cs,$(PRODUCT_LOCALES)),default) \
- $(if $(findstring de,$(PRODUCT_LOCALES)),default) \
- $(if $(findstring fr,$(PRODUCT_LOCALES)),default) \
- $(if $(findstring nl,$(PRODUCT_LOCALES)),default) \
- us)
-
-#include $(LOCAL_PATH)/root.mk
-# derive a string like 'icudt44l' from a local file like 'external/icu4c/stubdata/icudt44l-all.dat'
-stubdata_path:= $(PROJECT_ROOT_PATH)/icu4c/stubdata
-root_dat_path := $(wildcard $(stubdata_path)/*-all.dat)
-root := $(patsubst $(stubdata_path)/%,%,$(patsubst %-all.dat,%,$(root_dat_path)))
-
-
-PRODUCT_COPY_FILES += $(LOCAL_PATH)/$(root)-$(config).dat:/system/usr/icu/$(root).dat
-
-ifeq ($(WITH_HOST_DALVIK),true)
- $(eval $(call copy-one-file,$(LOCAL_PATH)/$(root)-$(config).dat,$(HOST_OUT)/usr/icu/$(root).dat))
-endif
-
-#-------------
-# end stubdata
-#-------------
diff --git a/external/String16.cpp b/external/String16.cpp
deleted file mode 100644
index 38baf531..00000000
--- a/external/String16.cpp
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#ifdef HAVE_WINSOCK
-# undef nhtol
-# undef htonl
-# undef nhtos
-# undef htons
-
-# ifdef HAVE_LITTLE_ENDIAN
-# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) )
-# define htonl(x) ntohl(x)
-# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) )
-# define htons(x) ntohs(x)
-# else
-# define ntohl(x) (x)
-# define htonl(x) (x)
-# define ntohs(x) (x)
-# define htons(x) (x)
-# endif
-#else
-//# include
-# include
-#endif
-
-#include
-#include
-#include
-
-// ---------------------------------------------------------------------------
-
-int strcmp16(const char16_t *s1, const char16_t *s2)
-{
- char16_t ch;
- int d = 0;
-
- while ( 1 ) {
- d = (int)(ch = *s1++) - (int)*s2++;
- if ( d || !ch )
- break;
- }
-
- return d;
-}
-
-int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
-{
- char16_t ch;
- int d = 0;
-
- while ( n-- ) {
- d = (int)(ch = *s1++) - (int)*s2++;
- if ( d || !ch )
- break;
- }
-
- return d;
-}
-
-char16_t *strcpy16(char16_t *dst, const char16_t *src)
-{
- char16_t *q = dst;
- const char16_t *p = src;
- char16_t ch;
-
- do {
- *q++ = ch = *p++;
- } while ( ch );
-
- return dst;
-}
-
-size_t strlen16(const char16_t *s)
-{
- const char16_t *ss = s;
- while ( *ss )
- ss++;
- return ss-s;
-}
-
-
-char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n)
-{
- char16_t *q = dst;
- const char16_t *p = src;
- char ch;
-
- while (n) {
- n--;
- *q++ = ch = *p++;
- if ( !ch )
- break;
- }
-
- *q = 0;
-
- return dst;
-}
-
-size_t strnlen16(const char16_t *s, size_t maxlen)
-{
- const char16_t *ss = s;
-
- /* Important: the maxlen test must precede the reference through ss;
- since the byte beyond the maximum may segfault */
- while ((maxlen > 0) && *ss) {
- ss++;
- maxlen--;
- }
- return ss-s;
-}
-
-int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
-{
- const char16_t* e1 = s1+n1;
- const char16_t* e2 = s2+n2;
-
- while (s1 < e1 && s2 < e2) {
- const int d = (int)*s1++ - (int)*s2++;
- if (d) {
- return d;
- }
- }
-
- return n1 < n2
- ? (0 - (int)*s2)
- : (n1 > n2
- ? ((int)*s1 - 0)
- : 0);
-}
-
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2)
-{
- const char16_t* e1 = s1H+n1;
- const char16_t* e2 = s2N+n2;
-
- while (s1H < e1 && s2N < e2) {
- const char16_t c2 = ntohs(*s2N);
- const int d = (int)*s1H++ - (int)c2;
- s2N++;
- if (d) {
- return d;
- }
- }
-
- return n1 < n2
- ? (0 - (int)ntohs(*s2N))
- : (n1 > n2
- ? ((int)*s1H - 0)
- : 0);
-}
-
-static inline size_t
-utf8_char_len(uint8_t ch)
-{
- return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
-}
-
-#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte));
-
-static inline uint32_t
-utf8_to_utf32(const uint8_t *src, size_t length)
-{
- uint32_t unicode;
-
- switch (length)
- {
- case 1:
- return src[0];
- case 2:
- unicode = src[0] & 0x1f;
- UTF8_SHIFT_AND_MASK(unicode, src[1])
- return unicode;
- case 3:
- unicode = src[0] & 0x0f;
- UTF8_SHIFT_AND_MASK(unicode, src[1])
- UTF8_SHIFT_AND_MASK(unicode, src[2])
- return unicode;
- case 4:
- unicode = src[0] & 0x07;
- UTF8_SHIFT_AND_MASK(unicode, src[1])
- UTF8_SHIFT_AND_MASK(unicode, src[2])
- UTF8_SHIFT_AND_MASK(unicode, src[3])
- return unicode;
- default:
- return 0xffff;
- }
-
- //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
-}
-
-void
-utf8_to_utf16(const uint8_t *src, size_t srcLen,
- char16_t* dst, const size_t dstLen)
-{
- const uint8_t* const end = src + srcLen;
- const char16_t* const dstEnd = dst + dstLen;
- while (src < end && dst < dstEnd) {
- size_t len = utf8_char_len(*src);
- uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len);
-
- // Convert the UTF32 codepoint to one or more UTF16 codepoints
- if (codepoint <= 0xFFFF) {
- // Single UTF16 character
- *dst++ = (char16_t) codepoint;
- } else {
- // Multiple UTF16 characters with surrogates
- codepoint = codepoint - 0x10000;
- *dst++ = (char16_t) ((codepoint >> 10) + 0xD800);
- *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
- }
-
- src += len;
- }
- if (dst < dstEnd) {
- *dst = 0;
- }
-}
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char16_t* gEmptyString = NULL;
-
-static inline char16_t* getEmptyString()
-{
- gEmptyStringBuf->acquire();
- return gEmptyString;
-}
-
-void initialize_string16()
-{
- SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
- char16_t* str = (char16_t*)buf->data();
- *str = 0;
- gEmptyStringBuf = buf;
- gEmptyString = str;
-}
-
-void terminate_string16()
-{
- SharedBuffer::bufferFromData(gEmptyString)->release();
- gEmptyStringBuf = NULL;
- gEmptyString = NULL;
-}
-
-// ---------------------------------------------------------------------------
-
-static char16_t* allocFromUTF8(const char* in, size_t len)
-{
- if (len == 0) return getEmptyString();
-
- size_t chars = 0;
- const char* end = in+len;
- const char* p = in;
-
- while (p < end) {
- chars++;
- int utf8len = utf8_char_len(*p);
- uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len);
- if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16
- p += utf8len;
- }
-
- size_t bufSize = (chars+1)*sizeof(char16_t);
- SharedBuffer* buf = SharedBuffer::alloc(bufSize);
- if (buf) {
- p = in;
- char16_t* str = (char16_t*)buf->data();
-
- utf8_to_utf16((const uint8_t*)p, len, str, bufSize);
-
- //printf("Created UTF-16 string from UTF-8 \"%s\":", in);
- //printHexData(1, str, buf->size(), 16, 1);
- //printf("\n");
-
- return str;
- }
-
- return getEmptyString();
-}
-
-// ---------------------------------------------------------------------------
-
-String16::String16()
- : mString(getEmptyString())
-{
-}
-
-String16::String16(const String16& o)
- : mString(o.mString)
-{
- SharedBuffer::bufferFromData(mString)->acquire();
-}
-
-String16::String16(const String16& o, size_t len, size_t begin)
- : mString(getEmptyString())
-{
- setTo(o, len, begin);
-}
-
-String16::String16(const char16_t* o)
-{
- size_t len = strlen16(o);
- SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
- LOG_ASSERT(buf, "Unable to allocate shared buffer");
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- strcpy16(str, o);
- mString = str;
- return;
- }
-
- mString = getEmptyString();
-}
-
-String16::String16(const char16_t* o, size_t len)
-{
- SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t));
- LOG_ASSERT(buf, "Unable to allocate shared buffer");
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- memcpy(str, o, len*sizeof(char16_t));
- str[len] = 0;
- mString = str;
- return;
- }
-
- mString = getEmptyString();
-}
-
-String16::String16(const String8& o)
- : mString(allocFromUTF8(o.string(), o.size()))
-{
-}
-
-String16::String16(const char* o)
- : mString(allocFromUTF8(o, strlen(o)))
-{
-}
-
-String16::String16(const char* o, size_t len)
- : mString(allocFromUTF8(o, len))
-{
-}
-
-String16::~String16()
-{
- SharedBuffer::bufferFromData(mString)->release();
-}
-
-void String16::setTo(const String16& other)
-{
- SharedBuffer::bufferFromData(other.mString)->acquire();
- SharedBuffer::bufferFromData(mString)->release();
- mString = other.mString;
-}
-
-status_t String16::setTo(const String16& other, size_t len, size_t begin)
-{
- const size_t N = other.size();
- if (begin >= N) {
- SharedBuffer::bufferFromData(mString)->release();
- mString = getEmptyString();
- return NO_ERROR;
- }
- if ((begin+len) > N) len = N-begin;
- if (begin == 0 && len == N) {
- setTo(other);
- return NO_ERROR;
- }
-
- if (&other == this) {
- LOG_ALWAYS_FATAL("Not implemented");
- }
-
- return setTo(other.string()+begin, len);
-}
-
-status_t String16::setTo(const char16_t* other)
-{
- return setTo(other, strlen16(other));
-}
-
-status_t String16::setTo(const char16_t* other, size_t len)
-{
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((len+1)*sizeof(char16_t));
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- memmove(str, other, len*sizeof(char16_t));
- str[len] = 0;
- mString = str;
- return NO_ERROR;
- }
- return NO_MEMORY;
-}
-
-status_t String16::append(const String16& other)
-{
- const size_t myLen = size();
- const size_t otherLen = other.size();
- if (myLen == 0) {
- setTo(other);
- return NO_ERROR;
- } else if (otherLen == 0) {
- return NO_ERROR;
- }
-
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+otherLen+1)*sizeof(char16_t));
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t));
- mString = str;
- return NO_ERROR;
- }
- return NO_MEMORY;
-}
-
-status_t String16::append(const char16_t* chrs, size_t otherLen)
-{
- const size_t myLen = size();
- if (myLen == 0) {
- setTo(chrs, otherLen);
- return NO_ERROR;
- } else if (otherLen == 0) {
- return NO_ERROR;
- }
-
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+otherLen+1)*sizeof(char16_t));
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- memcpy(str+myLen, chrs, otherLen*sizeof(char16_t));
- str[myLen+otherLen] = 0;
- mString = str;
- return NO_ERROR;
- }
- return NO_MEMORY;
-}
-
-status_t String16::insert(size_t pos, const char16_t* chrs)
-{
- return insert(pos, chrs, strlen16(chrs));
-}
-
-status_t String16::insert(size_t pos, const char16_t* chrs, size_t len)
-{
- const size_t myLen = size();
- if (myLen == 0) {
- return setTo(chrs, len);
- return NO_ERROR;
- } else if (len == 0) {
- return NO_ERROR;
- }
-
- if (pos > myLen) pos = myLen;
-
- #if 0
- printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n",
- String8(*this).string(), pos,
- len, myLen, String8(chrs, len).string());
- #endif
-
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((myLen+len+1)*sizeof(char16_t));
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- if (pos < myLen) {
- memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t));
- }
- memcpy(str+pos, chrs, len*sizeof(char16_t));
- str[myLen+len] = 0;
- mString = str;
- #if 0
- printf("Result (%d chrs): %s\n", size(), String8(*this).string());
- #endif
- return NO_ERROR;
- }
- return NO_MEMORY;
-}
-
-ssize_t String16::findFirst(char16_t c) const
-{
- const char16_t* str = string();
- const char16_t* p = str;
- const char16_t* e = p + size();
- while (p < e) {
- if (*p == c) {
- return p-str;
- }
- p++;
- }
- return -1;
-}
-
-ssize_t String16::findLast(char16_t c) const
-{
- const char16_t* str = string();
- const char16_t* p = str;
- const char16_t* e = p + size();
- while (p < e) {
- e--;
- if (*e == c) {
- return e-str;
- }
- }
- return -1;
-}
-
-bool String16::startsWith(const String16& prefix) const
-{
- const size_t ps = prefix.size();
- if (ps > size()) return false;
- return strzcmp16(mString, ps, prefix.string(), ps) == 0;
-}
-
-bool String16::startsWith(const char16_t* prefix) const
-{
- const size_t ps = strlen16(prefix);
- if (ps > size()) return false;
- return strncmp16(mString, prefix, ps) == 0;
-}
-
-status_t String16::makeLower()
-{
- const size_t N = size();
- const char16_t* str = string();
- char16_t* edit = NULL;
- for (size_t i=0; i= 'A' && v <= 'Z') {
- if (!edit) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit();
- if (!buf) {
- return NO_MEMORY;
- }
- edit = (char16_t*)buf->data();
- mString = str = edit;
- }
- edit[i] = tolower((char)v);
- }
- }
- return NO_ERROR;
-}
-
-status_t String16::replaceAll(char16_t replaceThis, char16_t withThis)
-{
- const size_t N = size();
- const char16_t* str = string();
- char16_t* edit = NULL;
- for (size_t i=0; iedit();
- if (!buf) {
- return NO_MEMORY;
- }
- edit = (char16_t*)buf->data();
- mString = str = edit;
- }
- edit[i] = withThis;
- }
- }
- return NO_ERROR;
-}
-
-status_t String16::remove(size_t len, size_t begin)
-{
- const size_t N = size();
- if (begin >= N) {
- SharedBuffer::bufferFromData(mString)->release();
- mString = getEmptyString();
- return NO_ERROR;
- }
- if ((begin+len) > N) len = N-begin;
- if (begin == 0 && len == N) {
- return NO_ERROR;
- }
-
- if (begin > 0) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((N+1)*sizeof(char16_t));
- if (!buf) {
- return NO_MEMORY;
- }
- char16_t* str = (char16_t*)buf->data();
- memmove(str, str+begin, (N-begin+1)*sizeof(char16_t));
- mString = str;
- }
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize((len+1)*sizeof(char16_t));
- if (buf) {
- char16_t* str = (char16_t*)buf->data();
- str[len] = 0;
- mString = str;
- return NO_ERROR;
- }
- return NO_MEMORY;
-}
-
-TextOutput& operator<<(TextOutput& to, const String16& val)
-{
- to << String8(val).string();
- return to;
-}
-
-}; // namespace android
diff --git a/external/String8.cpp b/external/String8.cpp
deleted file mode 100644
index 1c4f80c1..00000000
--- a/external/String8.cpp
+++ /dev/null
@@ -1,940 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include
-
-#include
-
-/*
- * Functions outside android is below the namespace android, since they use
- * functions and constants in android namespace.
- */
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-static const char32_t kByteMask = 0x000000BF;
-static const char32_t kByteMark = 0x00000080;
-
-// Surrogates aren't valid for UTF-32 characters, so define some
-// constants that will let us screen them out.
-static const char32_t kUnicodeSurrogateHighStart = 0x0000D800;
-static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;
-static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;
-static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF;
-static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart;
-static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd;
-static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF;
-
-// Mask used to set appropriate bits in first byte of UTF-8 sequence,
-// indexed by number of bytes in the sequence.
-// 0xxxxxxx
-// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
-// 110yyyyx 10xxxxxx
-// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
-// 1110yyyy 10yxxxxx 10xxxxxx
-// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
-// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
-// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
-static const char32_t kFirstByteMark[] = {
- 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
-};
-
-// Separator used by resource paths. This is not platform dependent contrary
-// to OS_PATH_SEPARATOR.
-#define RES_PATH_SEPARATOR '/'
-
-// Return number of utf8 bytes required for the character.
-static size_t utf32_to_utf8_bytes(char32_t srcChar)
-{
- size_t bytesToWrite;
-
- // Figure out how many bytes the result will require.
- if (srcChar < 0x00000080)
- {
- bytesToWrite = 1;
- }
- else if (srcChar < 0x00000800)
- {
- bytesToWrite = 2;
- }
- else if (srcChar < 0x00010000)
- {
- if ((srcChar < kUnicodeSurrogateStart)
- || (srcChar > kUnicodeSurrogateEnd))
- {
- bytesToWrite = 3;
- }
- else
- {
- // Surrogates are invalid UTF-32 characters.
- return 0;
- }
- }
- // Max code point for Unicode is 0x0010FFFF.
- else if (srcChar <= kUnicodeMaxCodepoint)
- {
- bytesToWrite = 4;
- }
- else
- {
- // Invalid UTF-32 character.
- return 0;
- }
-
- return bytesToWrite;
-}
-
-// Write out the source character to .
-
-static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
-{
- dstP += bytes;
- switch (bytes)
- { /* note: everything falls through. */
- case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
- case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
- case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
- case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
- }
-}
-
-// ---------------------------------------------------------------------------
-
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char* gEmptyString = NULL;
-
-extern int gDarwinCantLoadAllObjects;
-int gDarwinIsReallyAnnoying;
-
-static inline char* getEmptyString()
-{
- gEmptyStringBuf->acquire();
- return gEmptyString;
-}
-
-void initialize_string8()
-{
- // HACK: This dummy dependency forces linking libutils Static.cpp,
- // which is needed to initialize String8/String16 classes.
- // These variables are named for Darwin, but are needed elsewhere too,
- // including static linking on any platform.
- gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
-
- SharedBuffer* buf = SharedBuffer::alloc(1);
- char* str = (char*)buf->data();
- *str = 0;
- gEmptyStringBuf = buf;
- gEmptyString = str;
-}
-
-void terminate_string8()
-{
- SharedBuffer::bufferFromData(gEmptyString)->release();
- gEmptyStringBuf = NULL;
- gEmptyString = NULL;
-}
-
-// ---------------------------------------------------------------------------
-
-static char* allocFromUTF8(const char* in, size_t len)
-{
- if (len > 0) {
- SharedBuffer* buf = SharedBuffer::alloc(len+1);
- LOG_ASSERT(buf, "Unable to allocate shared buffer");
- if (buf) {
- char* str = (char*)buf->data();
- memcpy(str, in, len);
- str[len] = 0;
- return str;
- }
- return NULL;
- }
-
- return getEmptyString();
-}
-
-template
-static char* allocFromUTF16OrUTF32(const T* in, L len)
-{
- if (len == 0) return getEmptyString();
-
- size_t bytes = 0;
- const T* end = in+len;
- const T* p = in;
-
- while (p < end) {
- bytes += utf32_to_utf8_bytes(*p);
- p++;
- }
-
- SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
- LOG_ASSERT(buf, "Unable to allocate shared buffer");
- if (buf) {
- p = in;
- char* str = (char*)buf->data();
- char* d = str;
- while (p < end) {
- const T c = *p++;
- size_t len = utf32_to_utf8_bytes(c);
- utf32_to_utf8((uint8_t*)d, c, len);
- d += len;
- }
- *d = 0;
-
- return str;
- }
-
- return getEmptyString();
-}
-
-static char* allocFromUTF16(const char16_t* in, size_t len)
-{
- if (len == 0) return getEmptyString();
-
- const size_t bytes = utf8_length_from_utf16(in, len);
-
- SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
- LOG_ASSERT(buf, "Unable to allocate shared buffer");
- if (buf) {
- char* str = (char*)buf->data();
-
- utf16_to_utf8(in, len, str, bytes+1);
-
- return str;
- }
-
- return getEmptyString();
-}
-
-static char* allocFromUTF32(const char32_t* in, size_t len)
-{
- return allocFromUTF16OrUTF32(in, len);
-}
-
-// ---------------------------------------------------------------------------
-
-String8::String8()
- : mString(getEmptyString())
-{
-}
-
-String8::String8(const String8& o)
- : mString(o.mString)
-{
- SharedBuffer::bufferFromData(mString)->acquire();
-}
-
-String8::String8(const char* o)
- : mString(allocFromUTF8(o, strlen(o)))
-{
- if (mString == NULL) {
- mString = getEmptyString();
- }
-}
-
-String8::String8(const char* o, size_t len)
- : mString(allocFromUTF8(o, len))
-{
- if (mString == NULL) {
- mString = getEmptyString();
- }
-}
-
-String8::String8(const String16& o)
- : mString(allocFromUTF16(o.string(), o.size()))
-{
-}
-
-String8::String8(const char16_t* o)
- : mString(allocFromUTF16(o, strlen16(o)))
-{
-}
-
-String8::String8(const char16_t* o, size_t len)
- : mString(allocFromUTF16(o, len))
-{
-}
-
-String8::String8(const char32_t* o)
- : mString(allocFromUTF32(o, strlen32(o)))
-{
-}
-
-String8::String8(const char32_t* o, size_t len)
- : mString(allocFromUTF32(o, len))
-{
-}
-
-String8::~String8()
-{
- SharedBuffer::bufferFromData(mString)->release();
-}
-
-void String8::setTo(const String8& other)
-{
- SharedBuffer::bufferFromData(other.mString)->acquire();
- SharedBuffer::bufferFromData(mString)->release();
- mString = other.mString;
-}
-
-status_t String8::setTo(const char* other)
-{
- const char *newString = allocFromUTF8(other, strlen(other));
- SharedBuffer::bufferFromData(mString)->release();
- mString = newString;
- if (mString) return NO_ERROR;
-
- mString = getEmptyString();
- return NO_MEMORY;
-}
-
-status_t String8::setTo(const char* other, size_t len)
-{
- const char *newString = allocFromUTF8(other, len);
- SharedBuffer::bufferFromData(mString)->release();
- mString = newString;
- if (mString) return NO_ERROR;
-
- mString = getEmptyString();
- return NO_MEMORY;
-}
-
-status_t String8::setTo(const char16_t* other, size_t len)
-{
- const char *newString = allocFromUTF16(other, len);
- SharedBuffer::bufferFromData(mString)->release();
- mString = newString;
- if (mString) return NO_ERROR;
-
- mString = getEmptyString();
- return NO_MEMORY;
-}
-
-status_t String8::setTo(const char32_t* other, size_t len)
-{
- const char *newString = allocFromUTF32(other, len);
- SharedBuffer::bufferFromData(mString)->release();
- mString = newString;
- if (mString) return NO_ERROR;
-
- mString = getEmptyString();
- return NO_MEMORY;
-}
-
-status_t String8::append(const String8& other)
-{
- const size_t otherLen = other.bytes();
- if (bytes() == 0) {
- setTo(other);
- return NO_ERROR;
- } else if (otherLen == 0) {
- return NO_ERROR;
- }
-
- return real_append(other.string(), otherLen);
-}
-
-status_t String8::append(const char* other)
-{
- return append(other, strlen(other));
-}
-
-status_t String8::append(const char* other, size_t otherLen)
-{
- if (bytes() == 0) {
- return setTo(other, otherLen);
- } else if (otherLen == 0) {
- return NO_ERROR;
- }
-
- return real_append(other, otherLen);
-}
-
-status_t String8::appendFormat(const char* fmt, ...)
-{
- va_list ap;
- va_start(ap, fmt);
-
- int result = NO_ERROR;
- int n = vsnprintf(NULL, 0, fmt, ap);
- if (n != 0) {
- size_t oldLength = length();
- char* buf = lockBuffer(oldLength + n);
- if (buf) {
- vsnprintf(buf + oldLength, n + 1, fmt, ap);
- } else {
- result = NO_MEMORY;
- }
- }
-
- va_end(ap);
- return result;
-}
-
-status_t String8::real_append(const char* other, size_t otherLen)
-{
- const size_t myLen = bytes();
-
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize(myLen+otherLen+1);
- if (buf) {
- char* str = (char*)buf->data();
- mString = str;
- str += myLen;
- memcpy(str, other, otherLen);
- str[otherLen] = '\0';
- return NO_ERROR;
- }
- return NO_MEMORY;
-}
-
-char* String8::lockBuffer(size_t size)
-{
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize(size+1);
- if (buf) {
- char* str = (char*)buf->data();
- mString = str;
- return str;
- }
- return NULL;
-}
-
-void String8::unlockBuffer()
-{
- unlockBuffer(strlen(mString));
-}
-
-status_t String8::unlockBuffer(size_t size)
-{
- if (size != this->size()) {
- SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
- ->editResize(size+1);
- if (! buf) {
- return NO_MEMORY;
- }
-
- char* str = (char*)buf->data();
- str[size] = 0;
- mString = str;
- }
-
- return NO_ERROR;
-}
-
-ssize_t String8::find(const char* other, size_t start) const
-{
- size_t len = size();
- if (start >= len) {
- return -1;
- }
- const char* s = mString+start;
- const char* p = strstr(s, other);
- return p ? p-mString : -1;
-}
-
-void String8::toLower()
-{
- toLower(0, size());
-}
-
-void String8::toLower(size_t start, size_t length)
-{
- const size_t len = size();
- if (start >= len) {
- return;
- }
- if (start+length > len) {
- length = len-start;
- }
- char* buf = lockBuffer(len);
- buf += start;
- while (length > 0) {
- *buf = tolower(*buf);
- buf++;
- length--;
- }
- unlockBuffer(len);
-}
-
-void String8::toUpper()
-{
- toUpper(0, size());
-}
-
-void String8::toUpper(size_t start, size_t length)
-{
- const size_t len = size();
- if (start >= len) {
- return;
- }
- if (start+length > len) {
- length = len-start;
- }
- char* buf = lockBuffer(len);
- buf += start;
- while (length > 0) {
- *buf = toupper(*buf);
- buf++;
- length--;
- }
- unlockBuffer(len);
-}
-
-size_t String8::getUtf32Length() const
-{
- return utf32_length(mString, length());
-}
-
-int32_t String8::getUtf32At(size_t index, size_t *next_index) const
-{
- return utf32_at(mString, length(), index, next_index);
-}
-
-size_t String8::getUtf32(char32_t* dst, size_t dst_len) const
-{
- return utf8_to_utf32(mString, length(), dst, dst_len);
-}
-
-TextOutput& operator<<(TextOutput& to, const String8& val)
-{
- to << val.string();
- return to;
-}
-
-// ---------------------------------------------------------------------------
-// Path functions
-
-void String8::setPathName(const char* name)
-{
- setPathName(name, strlen(name));
-}
-
-void String8::setPathName(const char* name, size_t len)
-{
- char* buf = lockBuffer(len);
-
- memcpy(buf, name, len);
-
- // remove trailing path separator, if present
- if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
- len--;
-
- buf[len] = '\0';
-
- unlockBuffer(len);
-}
-
-String8 String8::getPathLeaf(void) const
-{
- const char* cp;
- const char*const buf = mString;
-
- cp = strrchr(buf, OS_PATH_SEPARATOR);
- if (cp == NULL)
- return String8(*this);
- else
- return String8(cp+1);
-}
-
-String8 String8::getPathDir(void) const
-{
- const char* cp;
- const char*const str = mString;
-
- cp = strrchr(str, OS_PATH_SEPARATOR);
- if (cp == NULL)
- return String8("");
- else
- return String8(str, cp - str);
-}
-
-String8 String8::walkPath(String8* outRemains) const
-{
- const char* cp;
- const char*const str = mString;
- const char* buf = str;
-
- cp = strchr(buf, OS_PATH_SEPARATOR);
- if (cp == buf) {
- // don't include a leading '/'.
- buf = buf+1;
- cp = strchr(buf, OS_PATH_SEPARATOR);
- }
-
- if (cp == NULL) {
- String8 res = buf != str ? String8(buf) : *this;
- if (outRemains) *outRemains = String8("");
- return res;
- }
-
- String8 res(buf, cp-buf);
- if (outRemains) *outRemains = String8(cp+1);
- return res;
-}
-
-/*
- * Helper function for finding the start of an extension in a pathname.
- *
- * Returns a pointer inside mString, or NULL if no extension was found.
- */
-char* String8::find_extension(void) const
-{
- const char* lastSlash;
- const char* lastDot;
- int extLen;
- const char* const str = mString;
-
- // only look at the filename
- lastSlash = strrchr(str, OS_PATH_SEPARATOR);
- if (lastSlash == NULL)
- lastSlash = str;
- else
- lastSlash++;
-
- // find the last dot
- lastDot = strrchr(lastSlash, '.');
- if (lastDot == NULL)
- return NULL;
-
- // looks good, ship it
- return const_cast(lastDot);
-}
-
-String8 String8::getPathExtension(void) const
-{
- char* ext;
-
- ext = find_extension();
- if (ext != NULL)
- return String8(ext);
- else
- return String8("");
-}
-
-String8 String8::getBasePath(void) const
-{
- char* ext;
- const char* const str = mString;
-
- ext = find_extension();
- if (ext == NULL)
- return String8(*this);
- else
- return String8(str, ext - str);
-}
-
-String8& String8::appendPath(const char* name)
-{
- // TODO: The test below will fail for Win32 paths. Fix later or ignore.
- if (name[0] != OS_PATH_SEPARATOR) {
- if (*name == '\0') {
- // nothing to do
- return *this;
- }
-
- size_t len = length();
- if (len == 0) {
- // no existing filename, just use the new one
- setPathName(name);
- return *this;
- }
-
- // make room for oldPath + '/' + newPath
- int newlen = strlen(name);
-
- char* buf = lockBuffer(len+1+newlen);
-
- // insert a '/' if needed
- if (buf[len-1] != OS_PATH_SEPARATOR)
- buf[len++] = OS_PATH_SEPARATOR;
-
- memcpy(buf+len, name, newlen+1);
- len += newlen;
-
- unlockBuffer(len);
-
- return *this;
- } else {
- setPathName(name);
- return *this;
- }
-}
-
-String8& String8::convertToResPath()
-{
-#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
- size_t len = length();
- if (len > 0) {
- char * buf = lockBuffer(len);
- for (char * end = buf + len; buf < end; ++buf) {
- if (*buf == OS_PATH_SEPARATOR)
- *buf = RES_PATH_SEPARATOR;
- }
- unlockBuffer(len);
- }
-#endif
- return *this;
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-size_t strlen32(const char32_t *s)
-{
- const char32_t *ss = s;
- while ( *ss )
- ss++;
- return ss-s;
-}
-
-size_t strnlen32(const char32_t *s, size_t maxlen)
-{
- const char32_t *ss = s;
- while ((maxlen > 0) && *ss) {
- ss++;
- maxlen--;
- }
- return ss-s;
-}
-
-size_t utf8_length(const char *src)
-{
- const char *cur = src;
- size_t ret = 0;
- while (*cur != '\0') {
- const char first_char = *cur++;
- if ((first_char & 0x80) == 0) { // ASCII
- ret += 1;
- continue;
- }
- // (UTF-8's character must not be like 10xxxxxx,
- // but 110xxxxx, 1110xxxx, ... or 1111110x)
- if ((first_char & 0x40) == 0) {
- return 0;
- }
-
- int32_t mask, to_ignore_mask;
- size_t num_to_read = 0;
- char32_t utf32 = 0;
- for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
- num_to_read < 5 && (first_char & mask);
- num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
- if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
- return 0;
- }
- // 0x3F == 00111111
- utf32 = (utf32 << 6) + (*cur++ & 0x3F);
- }
- // "first_char" must be (110xxxxx - 11110xxx)
- if (num_to_read == 5) {
- return 0;
- }
- to_ignore_mask |= mask;
- utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
- if (utf32 > android::kUnicodeMaxCodepoint) {
- return 0;
- }
-
- ret += num_to_read;
- }
- return ret;
-}
-
-size_t utf32_length(const char *src, size_t src_len)
-{
- if (src == NULL || src_len == 0) {
- return 0;
- }
- size_t ret = 0;
- const char* cur;
- const char* end;
- size_t num_to_skip;
- for (cur = src, end = src + src_len, num_to_skip = 1;
- cur < end;
- cur += num_to_skip, ret++) {
- const char first_char = *cur;
- num_to_skip = 1;
- if ((first_char & 0x80) == 0) { // ASCII
- continue;
- }
- int32_t mask;
-
- for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) {
- }
- }
- return ret;
-}
-
-size_t utf8_length_from_utf32(const char32_t *src, size_t src_len)
-{
- if (src == NULL || src_len == 0) {
- return 0;
- }
- size_t ret = 0;
- const char32_t *end = src + src_len;
- while (src < end) {
- ret += android::utf32_to_utf8_bytes(*src++);
- }
- return ret;
-}
-
-size_t utf8_length_from_utf16(const char16_t *src, size_t src_len)
-{
- if (src == NULL || src_len == 0) {
- return 0;
- }
- size_t ret = 0;
- const char16_t* const end = src + src_len;
- while (src < end) {
- if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
- && (*++src & 0xFC00) == 0xDC00) {
- // surrogate pairs are always 4 bytes.
- ret += 4;
- src++;
- } else {
- ret += android::utf32_to_utf8_bytes((char32_t) *src++);
- }
- }
- return ret;
-}
-
-static int32_t utf32_at_internal(const char* cur, size_t *num_read)
-{
- const char first_char = *cur;
- if ((first_char & 0x80) == 0) { // ASCII
- *num_read = 1;
- return *cur;
- }
- cur++;
- char32_t mask, to_ignore_mask;
- size_t num_to_read = 0;
- char32_t utf32 = first_char;
- for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
- (first_char & mask);
- num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
- // 0x3F == 00111111
- utf32 = (utf32 << 6) + (*cur++ & 0x3F);
- }
- to_ignore_mask |= mask;
- utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
-
- *num_read = num_to_read;
- return static_cast(utf32);
-}
-
-int32_t utf32_at(const char *src, size_t src_len,
- size_t index, size_t *next_index)
-{
- if (index >= src_len) {
- return -1;
- }
- size_t dummy_index;
- if (next_index == NULL) {
- next_index = &dummy_index;
- }
- size_t num_read;
- int32_t ret = utf32_at_internal(src + index, &num_read);
- if (ret >= 0) {
- *next_index = index + num_read;
- }
-
- return ret;
-}
-
-size_t utf8_to_utf32(const char* src, size_t src_len,
- char32_t* dst, size_t dst_len)
-{
- if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
- return 0;
- }
-
- const char* cur = src;
- const char* end = src + src_len;
- char32_t* cur_utf32 = dst;
- const char32_t* end_utf32 = dst + dst_len;
- while (cur_utf32 < end_utf32 && cur < end) {
- size_t num_read;
- *cur_utf32++ =
- static_cast(utf32_at_internal(cur, &num_read));
- cur += num_read;
- }
- if (cur_utf32 < end_utf32) {
- *cur_utf32 = 0;
- }
- return static_cast(cur_utf32 - dst);
-}
-
-size_t utf32_to_utf8(const char32_t* src, size_t src_len,
- char* dst, size_t dst_len)
-{
- if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
- return 0;
- }
- const char32_t *cur_utf32 = src;
- const char32_t *end_utf32 = src + src_len;
- char *cur = dst;
- const char *end = dst + dst_len;
- while (cur_utf32 < end_utf32 && cur < end) {
- size_t len = android::utf32_to_utf8_bytes(*cur_utf32);
- android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len);
- cur += len;
- }
- if (cur < end) {
- *cur = '\0';
- }
- return cur - dst;
-}
-
-size_t utf16_to_utf8(const char16_t* src, size_t src_len,
- char* dst, size_t dst_len)
-{
- if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
- return 0;
- }
- const char16_t* cur_utf16 = src;
- const char16_t* const end_utf16 = src + src_len;
- char *cur = dst;
- const char* const end = dst + dst_len;
- while (cur_utf16 < end_utf16 && cur < end) {
- char32_t utf32;
- // surrogate pairs
- if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) {
- utf32 = (*cur_utf16++ - 0xD800) << 10;
- utf32 |= *cur_utf16++ - 0xDC00;
- utf32 += 0x10000;
- } else {
- utf32 = (char32_t) *cur_utf16++;
- }
- size_t len = android::utf32_to_utf8_bytes(utf32);
- android::utf32_to_utf8((uint8_t*)cur, utf32, len);
- cur += len;
- }
- if (cur < end) {
- *cur = '\0';
- }
- return cur - dst;
-}
diff --git a/external/android-libs/libandroid_runtime.so b/external/android-libs/libandroid_runtime.so
deleted file mode 100644
index fa370f30..00000000
Binary files a/external/android-libs/libandroid_runtime.so and /dev/null differ
diff --git a/external/android-libs/libbinder.so b/external/android-libs/libbinder.so
deleted file mode 100644
index 5cbc35f2..00000000
Binary files a/external/android-libs/libbinder.so and /dev/null differ
diff --git a/external/android-libs/libcrypto.so b/external/android-libs/libcrypto.so
deleted file mode 100644
index 859bdac9..00000000
Binary files a/external/android-libs/libcrypto.so and /dev/null differ
diff --git a/external/android-libs/libcutils.so b/external/android-libs/libcutils.so
deleted file mode 100644
index d506bfaa..00000000
Binary files a/external/android-libs/libcutils.so and /dev/null differ
diff --git a/external/android-libs/liblog.so b/external/android-libs/liblog.so
deleted file mode 100644
index c7cc6d83..00000000
Binary files a/external/android-libs/liblog.so and /dev/null differ
diff --git a/external/android-libs/libnativehelper.so b/external/android-libs/libnativehelper.so
deleted file mode 100644
index 9cc76e49..00000000
Binary files a/external/android-libs/libnativehelper.so and /dev/null differ
diff --git a/external/android-libs/libstlport_shared.so b/external/android-libs/libstlport_shared.so
deleted file mode 100755
index 165ca68f..00000000
Binary files a/external/android-libs/libstlport_shared.so and /dev/null differ
diff --git a/external/android-libs/libutils.so b/external/android-libs/libutils.so
deleted file mode 100644
index 29837275..00000000
Binary files a/external/android-libs/libutils.so and /dev/null differ
diff --git a/external/android-sqlite b/external/android-sqlite
deleted file mode 160000
index 1c376985..00000000
--- a/external/android-sqlite
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1c376985dd7602cd3542714b7eb46a5198270658
diff --git a/external/dalvik b/external/dalvik
deleted file mode 160000
index ef4b0613..00000000
--- a/external/dalvik
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ef4b0613d6952770aefac07d503955eb7b962d2b
diff --git a/external/icu4c b/external/icu4c
deleted file mode 160000
index 0fa67b93..00000000
--- a/external/icu4c
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 0fa67b93b831c6636ca18b152a1b1b14cc99b034
diff --git a/external/includes/utils/String16.h b/external/includes/utils/String16.h
deleted file mode 100644
index 07a0c118..00000000
--- a/external/includes/utils/String16.h
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_STRING16_H
-#define ANDROID_STRING16_H
-
-#include
-#include
-
-#include
-#include
-
-// ---------------------------------------------------------------------------
-
-extern "C" {
-
-typedef uint16_t char16_t;
-
-// Standard string functions on char16 strings.
-int strcmp16(const char16_t *, const char16_t *);
-int strncmp16(const char16_t *s1, const char16_t *s2, size_t n);
-size_t strlen16(const char16_t *);
-size_t strnlen16(const char16_t *, size_t);
-char16_t *strcpy16(char16_t *, const char16_t *);
-char16_t *strncpy16(char16_t *, const char16_t *, size_t);
-
-// Version of comparison that supports embedded nulls.
-// This is different than strncmp() because we don't stop
-// at a nul character and consider the strings to be different
-// if the lengths are different (thus we need to supply the
-// lengths of both strings). This can also be used when
-// your string is not nul-terminated as it will have the
-// equivalent result as strcmp16 (unlike strncmp16).
-int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
-
-// Version of strzcmp16 for comparing strings in different endianness.
-int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
-
-// Convert UTF-8 to UTF-16 including surrogate pairs
-void utf8_to_utf16(const uint8_t *src, size_t srcLen, char16_t* dst, const size_t dstLen);
-
-}
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class String8;
-class TextOutput;
-
-//! This is a string holding UTF-16 characters.
-class String16
-{
-public:
- String16();
- String16(const String16& o);
- String16(const String16& o,
- size_t len,
- size_t begin=0);
- explicit String16(const char16_t* o);
- explicit String16(const char16_t* o, size_t len);
- explicit String16(const String8& o);
- explicit String16(const char* o);
- explicit String16(const char* o, size_t len);
-
- ~String16();
-
- inline const char16_t* string() const;
- inline size_t size() const;
-
- inline const SharedBuffer* sharedBuffer() const;
-
- void setTo(const String16& other);
- status_t setTo(const char16_t* other);
- status_t setTo(const char16_t* other, size_t len);
- status_t setTo(const String16& other,
- size_t len,
- size_t begin=0);
-
- status_t append(const String16& other);
- status_t append(const char16_t* other, size_t len);
-
- inline String16& operator=(const String16& other);
-
- inline String16& operator+=(const String16& other);
- inline String16 operator+(const String16& other) const;
-
- status_t insert(size_t pos, const char16_t* chrs);
- status_t insert(size_t pos,
- const char16_t* chrs, size_t len);
-
- ssize_t findFirst(char16_t c) const;
- ssize_t findLast(char16_t c) const;
-
- bool startsWith(const String16& prefix) const;
- bool startsWith(const char16_t* prefix) const;
-
- status_t makeLower();
-
- status_t replaceAll(char16_t replaceThis,
- char16_t withThis);
-
- status_t remove(size_t len, size_t begin=0);
-
- inline int compare(const String16& other) const;
-
- inline bool operator<(const String16& other) const;
- inline bool operator<=(const String16& other) const;
- inline bool operator==(const String16& other) const;
- inline bool operator!=(const String16& other) const;
- inline bool operator>=(const String16& other) const;
- inline bool operator>(const String16& other) const;
-
- inline bool operator<(const char16_t* other) const;
- inline bool operator<=(const char16_t* other) const;
- inline bool operator==(const char16_t* other) const;
- inline bool operator!=(const char16_t* other) const;
- inline bool operator>=(const char16_t* other) const;
- inline bool operator>(const char16_t* other) const;
-
- inline operator const char16_t*() const;
-
-private:
- const char16_t* mString;
-};
-
-TextOutput& operator<<(TextOutput& to, const String16& val);
-
-// ---------------------------------------------------------------------------
-// No user servicable parts below.
-
-inline int compare_type(const String16& lhs, const String16& rhs)
-{
- return lhs.compare(rhs);
-}
-
-inline int strictly_order_type(const String16& lhs, const String16& rhs)
-{
- return compare_type(lhs, rhs) < 0;
-}
-
-inline const char16_t* String16::string() const
-{
- return mString;
-}
-
-inline size_t String16::size() const
-{
- return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
-}
-
-inline const SharedBuffer* String16::sharedBuffer() const
-{
- return SharedBuffer::bufferFromData(mString);
-}
-
-inline String16& String16::operator=(const String16& other)
-{
- setTo(other);
- return *this;
-}
-
-inline String16& String16::operator+=(const String16& other)
-{
- append(other);
- return *this;
-}
-
-inline String16 String16::operator+(const String16& other) const
-{
- String16 tmp;
- tmp += other;
- return tmp;
-}
-
-inline int String16::compare(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size());
-}
-
-inline bool String16::operator<(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) < 0;
-}
-
-inline bool String16::operator<=(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) <= 0;
-}
-
-inline bool String16::operator==(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) == 0;
-}
-
-inline bool String16::operator!=(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) != 0;
-}
-
-inline bool String16::operator>=(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) >= 0;
-}
-
-inline bool String16::operator>(const String16& other) const
-{
- return strzcmp16(mString, size(), other.mString, other.size()) > 0;
-}
-
-inline bool String16::operator<(const char16_t* other) const
-{
- return strcmp16(mString, other) < 0;
-}
-
-inline bool String16::operator<=(const char16_t* other) const
-{
- return strcmp16(mString, other) <= 0;
-}
-
-inline bool String16::operator==(const char16_t* other) const
-{
- return strcmp16(mString, other) == 0;
-}
-
-inline bool String16::operator!=(const char16_t* other) const
-{
- return strcmp16(mString, other) != 0;
-}
-
-inline bool String16::operator>=(const char16_t* other) const
-{
- return strcmp16(mString, other) >= 0;
-}
-
-inline bool String16::operator>(const char16_t* other) const
-{
- return strcmp16(mString, other) > 0;
-}
-
-inline String16::operator const char16_t*() const
-{
- return mString;
-}
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_STRING16_H
diff --git a/external/includes/utils/String8.h b/external/includes/utils/String8.h
deleted file mode 100644
index ef0b51a4..00000000
--- a/external/includes/utils/String8.h
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_STRING8_H
-#define ANDROID_STRING8_H
-
-#include
-
-// Need this for the char16_t type; String8.h should not
-// be depedent on the String16 class.
-#include
-
-#include
-#include
-#include
-
-// ---------------------------------------------------------------------------
-
-extern "C" {
-
-typedef uint32_t char32_t;
-
-size_t strlen32(const char32_t *);
-size_t strnlen32(const char32_t *, size_t);
-
-/*
- * Returns the length of "src" when "src" is valid UTF-8 string.
- * Returns 0 if src is NULL, 0-length string or non UTF-8 string.
- * This function should be used to determine whether "src" is valid UTF-8
- * characters with valid unicode codepoints. "src" must be null-terminated.
- *
- * If you are going to use other GetUtf... functions defined in this header
- * with string which may not be valid UTF-8 with valid codepoint (form 0 to
- * 0x10FFFF), you should use this function before calling others, since the
- * other functions do not check whether the string is valid UTF-8 or not.
- *
- * If you do not care whether "src" is valid UTF-8 or not, you should use
- * strlen() as usual, which should be much faster.
- */
-size_t utf8_length(const char *src);
-
-/*
- * Returns the UTF-32 length of "src".
- */
-size_t utf32_length(const char *src, size_t src_len);
-
-/*
- * Returns the UTF-8 length of "src".
- */
-size_t utf8_length_from_utf16(const char16_t *src, size_t src_len);
-
-/*
- * Returns the UTF-8 length of "src".
- */
-size_t utf8_length_from_utf32(const char32_t *src, size_t src_len);
-
-/*
- * Returns the unicode value at "index".
- * Returns -1 when the index is invalid (equals to or more than "src_len").
- * If returned value is positive, it is able to be converted to char32_t, which
- * is unsigned. Then, if "next_index" is not NULL, the next index to be used is
- * stored in "next_index". "next_index" can be NULL.
- */
-int32_t utf32_at(const char *src, size_t src_len,
- size_t index, size_t *next_index);
-
-/*
- * Stores a UTF-32 string converted from "src" in "dst", if "dst_length" is not
- * large enough to store the string, the part of the "src" string is stored
- * into "dst".
- * Returns the size actually used for storing the string.
- * "dst" is not null-terminated when dst_len is fully used (like strncpy).
- */
-size_t utf8_to_utf32(const char* src, size_t src_len,
- char32_t* dst, size_t dst_len);
-
-/*
- * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not
- * large enough to store the string, the part of the "src" string is stored
- * into "dst" as much as possible. See the examples for more detail.
- * Returns the size actually used for storing the string.
- * dst" is not null-terminated when dst_len is fully used (like strncpy).
- *
- * Example 1
- * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
- * "src_len" == 2
- * "dst_len" >= 7
- * ->
- * Returned value == 6
- * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0
- * (note that "dst" is null-terminated)
- *
- * Example 2
- * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
- * "src_len" == 2
- * "dst_len" == 5
- * ->
- * Returned value == 3
- * "dst" becomes \xE3\x81\x82\0
- * (note that "dst" is null-terminated, but \u3044 is not stored in "dst"
- * since "dst" does not have enough size to store the character)
- *
- * Example 3
- * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84)
- * "src_len" == 2
- * "dst_len" == 6
- * ->
- * Returned value == 6
- * "dst" becomes \xE3\x81\x82\xE3\x81\x84
- * (note that "dst" is NOT null-terminated, like strncpy)
- */
-size_t utf32_to_utf8(const char32_t* src, size_t src_len,
- char* dst, size_t dst_len);
-
-size_t utf16_to_utf8(const char16_t* src, size_t src_len,
- char* dst, size_t dst_len);
-
-}
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-class TextOutput;
-
-//! This is a string holding UTF-8 characters. Does not allow the value more
-// than 0x10FFFF, which is not valid unicode codepoint.
-class String8
-{
-public:
- String8();
- String8(const String8& o);
- explicit String8(const char* o);
- explicit String8(const char* o, size_t numChars);
-
- explicit String8(const String16& o);
- explicit String8(const char16_t* o);
- explicit String8(const char16_t* o, size_t numChars);
- explicit String8(const char32_t* o);
- explicit String8(const char32_t* o, size_t numChars);
- ~String8();
-
- inline const char* string() const;
- inline size_t size() const;
- inline size_t length() const;
- inline size_t bytes() const;
-
- inline const SharedBuffer* sharedBuffer() const;
-
- void setTo(const String8& other);
- status_t setTo(const char* other);
- status_t setTo(const char* other, size_t numChars);
- status_t setTo(const char16_t* other, size_t numChars);
- status_t setTo(const char32_t* other,
- size_t length);
-
- status_t append(const String8& other);
- status_t append(const char* other);
- status_t append(const char* other, size_t numChars);
-
- status_t appendFormat(const char* fmt, ...)
- __attribute__((format (printf, 2, 3)));
-
- // Note that this function takes O(N) time to calculate the value.
- // No cache value is stored.
- size_t getUtf32Length() const;
- int32_t getUtf32At(size_t index,
- size_t *next_index) const;
- size_t getUtf32(char32_t* dst, size_t dst_len) const;
-
- inline String8& operator=(const String8& other);
- inline String8& operator=(const char* other);
-
- inline String8& operator+=(const String8& other);
- inline String8 operator+(const String8& other) const;
-
- inline String8& operator+=(const char* other);
- inline String8 operator+(const char* other) const;
-
- inline int compare(const String8& other) const;
-
- inline bool operator<(const String8& other) const;
- inline bool operator<=(const String8& other) const;
- inline bool operator==(const String8& other) const;
- inline bool operator!=(const String8& other) const;
- inline bool operator>=(const String8& other) const;
- inline bool operator>(const String8& other) const;
-
- inline bool operator<(const char* other) const;
- inline bool operator<=(const char* other) const;
- inline bool operator==(const char* other) const;
- inline bool operator!=(const char* other) const;
- inline bool operator>=(const char* other) const;
- inline bool operator>(const char* other) const;
-
- inline operator const char*() const;
-
- char* lockBuffer(size_t size);
- void unlockBuffer();
- status_t unlockBuffer(size_t size);
-
- // return the index of the first byte of other in this at or after
- // start, or -1 if not found
- ssize_t find(const char* other, size_t start = 0) const;
-
- void toLower();
- void toLower(size_t start, size_t numChars);
- void toUpper();
- void toUpper(size_t start, size_t numChars);
-
- /*
- * These methods operate on the string as if it were a path name.
- */
-
- /*
- * Set the filename field to a specific value.
- *
- * Normalizes the filename, removing a trailing '/' if present.
- */
- void setPathName(const char* name);
- void setPathName(const char* name, size_t numChars);
-
- /*
- * Get just the filename component.
- *
- * "/tmp/foo/bar.c" --> "bar.c"
- */
- String8 getPathLeaf(void) const;
-
- /*
- * Remove the last (file name) component, leaving just the directory
- * name.
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo"
- * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
- * "bar.c" --> ""
- */
- String8 getPathDir(void) const;
-
- /*
- * Retrieve the front (root dir) component. Optionally also return the
- * remaining components.
- *
- * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
- * "/tmp" --> "tmp" (remain = "")
- * "bar.c" --> "bar.c" (remain = "")
- */
- String8 walkPath(String8* outRemains = NULL) const;
-
- /*
- * Return the filename extension. This is the last '.' and up to
- * four characters that follow it. The '.' is included in case we
- * decide to expand our definition of what constitutes an extension.
- *
- * "/tmp/foo/bar.c" --> ".c"
- * "/tmp" --> ""
- * "/tmp/foo.bar/baz" --> ""
- * "foo.jpeg" --> ".jpeg"
- * "foo." --> ""
- */
- String8 getPathExtension(void) const;
-
- /*
- * Return the path without the extension. Rules for what constitutes
- * an extension are described in the comment for getPathExtension().
- *
- * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
- */
- String8 getBasePath(void) const;
-
- /*
- * Add a component to the pathname. We guarantee that there is
- * exactly one path separator between the old path and the new.
- * If there is no existing name, we just copy the new name in.
- *
- * If leaf is a fully qualified path (i.e. starts with '/', it
- * replaces whatever was there before.
- */
- String8& appendPath(const char* leaf);
- String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); }
-
- /*
- * Like appendPath(), but does not affect this string. Returns a new one instead.
- */
- String8 appendPathCopy(const char* leaf) const
- { String8 p(*this); p.appendPath(leaf); return p; }
- String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
-
- /*
- * Converts all separators in this string to /, the default path separator.
- *
- * If the default OS separator is backslash, this converts all
- * backslashes to slashes, in-place. Otherwise it does nothing.
- * Returns self.
- */
- String8& convertToResPath();
-
-private:
- status_t real_append(const char* other, size_t numChars);
- char* find_extension(void) const;
-
- const char* mString;
-};
-
-TextOutput& operator<<(TextOutput& to, const String16& val);
-
-// ---------------------------------------------------------------------------
-// No user servicable parts below.
-
-inline int compare_type(const String8& lhs, const String8& rhs)
-{
- return lhs.compare(rhs);
-}
-
-inline int strictly_order_type(const String8& lhs, const String8& rhs)
-{
- return compare_type(lhs, rhs) < 0;
-}
-
-inline const char* String8::string() const
-{
- return mString;
-}
-
-inline size_t String8::length() const
-{
- return SharedBuffer::sizeFromData(mString)-1;
-}
-
-inline size_t String8::size() const
-{
- return length();
-}
-
-inline size_t String8::bytes() const
-{
- return SharedBuffer::sizeFromData(mString)-1;
-}
-
-inline const SharedBuffer* String8::sharedBuffer() const
-{
- return SharedBuffer::bufferFromData(mString);
-}
-
-inline String8& String8::operator=(const String8& other)
-{
- setTo(other);
- return *this;
-}
-
-inline String8& String8::operator=(const char* other)
-{
- setTo(other);
- return *this;
-}
-
-inline String8& String8::operator+=(const String8& other)
-{
- append(other);
- return *this;
-}
-
-inline String8 String8::operator+(const String8& other) const
-{
- String8 tmp(*this);
- tmp += other;
- return tmp;
-}
-
-inline String8& String8::operator+=(const char* other)
-{
- append(other);
- return *this;
-}
-
-inline String8 String8::operator+(const char* other) const
-{
- String8 tmp(*this);
- tmp += other;
- return tmp;
-}
-
-inline int String8::compare(const String8& other) const
-{
- return strcmp(mString, other.mString);
-}
-
-inline bool String8::operator<(const String8& other) const
-{
- return strcmp(mString, other.mString) < 0;
-}
-
-inline bool String8::operator<=(const String8& other) const
-{
- return strcmp(mString, other.mString) <= 0;
-}
-
-inline bool String8::operator==(const String8& other) const
-{
- return strcmp(mString, other.mString) == 0;
-}
-
-inline bool String8::operator!=(const String8& other) const
-{
- return strcmp(mString, other.mString) != 0;
-}
-
-inline bool String8::operator>=(const String8& other) const
-{
- return strcmp(mString, other.mString) >= 0;
-}
-
-inline bool String8::operator>(const String8& other) const
-{
- return strcmp(mString, other.mString) > 0;
-}
-
-inline bool String8::operator<(const char* other) const
-{
- return strcmp(mString, other) < 0;
-}
-
-inline bool String8::operator<=(const char* other) const
-{
- return strcmp(mString, other) <= 0;
-}
-
-inline bool String8::operator==(const char* other) const
-{
- return strcmp(mString, other) == 0;
-}
-
-inline bool String8::operator!=(const char* other) const
-{
- return strcmp(mString, other) != 0;
-}
-
-inline bool String8::operator>=(const char* other) const
-{
- return strcmp(mString, other) >= 0;
-}
-
-inline bool String8::operator>(const char* other) const
-{
- return strcmp(mString, other) > 0;
-}
-
-inline String8::operator const char*() const
-{
- return mString;
-}
-
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_STRING8_H
diff --git a/external/openssl b/external/openssl
deleted file mode 160000
index 1a3c5799..00000000
--- a/external/openssl
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1a3c5799337b90ddc56376ace7284a9e7f8cc988
diff --git a/external/platform-frameworks-base b/external/platform-frameworks-base
deleted file mode 160000
index 94180377..00000000
--- a/external/platform-frameworks-base
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 94180377e709ed0faff6ea94c75af0f0a1183b36
diff --git a/external/platform-system-core b/external/platform-system-core
deleted file mode 160000
index 31b2f441..00000000
--- a/external/platform-system-core
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 31b2f4414b291d87adddf13ad62d317d3ba382fa
diff --git a/external/sqlcipher b/external/sqlcipher
deleted file mode 160000
index ff9a3400..00000000
--- a/external/sqlcipher
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ff9a34003964900e2ee792169c544cd5d7696bdb
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..2d8d1e4d
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..41d9927a
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..070cb702
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000..1b6c7873
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000..ac1b06f9
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/jni/Android.mk b/jni/Android.mk
deleted file mode 100644
index e8e008b5..00000000
--- a/jni/Android.mk
+++ /dev/null
@@ -1,71 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-EXTERNAL_PATH := ../external
-
-ifeq ($(TARGET_ARCH), arm)
- LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))"
-else
- LOCAL_CFLAGS += -DPACKED=""
-endif
-
-#TARGET_PLATFORM := android-8
-
-ifeq ($(WITH_JIT),true)
- LOCAL_CFLAGS += -DWITH_JIT
-endif
-
-ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),)
- LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX)
-endif
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- net_sqlcipher_database_SQLiteCompiledSql.cpp \
- net_sqlcipher_database_SQLiteDatabase.cpp \
- net_sqlcipher_database_SQLiteProgram.cpp \
- net_sqlcipher_database_SQLiteQuery.cpp \
- net_sqlcipher_database_SQLiteStatement.cpp \
- net_sqlcipher_CursorWindow.cpp \
- CursorWindow.cpp
-# net_sqlcipher_database_sqlcipher_SQLiteDebug.cpp
-
-LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- $(EXTERNAL_PATH)/sqlcipher \
- $(EXTERNAL_PATH)/openssl/include \
- $(EXTERNAL_PATH)/platform-frameworks-base/core/jni \
- $(EXTERNAL_PATH)/android-sqlite/android \
- $(EXTERNAL_PATH)/dalvik/libnativehelper/include \
- $(EXTERNAL_PATH)/dalvik/libnativehelper/include/nativehelper \
- $(EXTERNAL_PATH)/platform-system-core/include \
- $(LOCAL_PATH)/include \
- $(EXTERNAL_PATH)/platform-frameworks-base/include \
-
-LOCAL_SHARED_LIBRARIES := \
- libcrypto \
- libssl \
- libsqlcipher \
- libsqlite3_android
-
-LOCAL_CFLAGS += -U__APPLE__
-LOCAL_LDFLAGS += -L../external/android-libs/ -L../external/libs/armeabi/
-
-# libs from the NDK
-LOCAL_LDLIBS += -ldl -llog
-# libnativehelper and libandroid_runtime are included with Android but not the NDK
-LOCAL_LDLIBS += -lnativehelper -landroid_runtime -lutils -lbinder
-# these are build in the ../external section
-
-#LOCAL_REQUIRED_MODULES += libsqlcipher libicuuc libicui18n
-LOCAL_LDLIBS += -lsqlcipher_android
-
-ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
- LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
-endif
-
-LOCAL_MODULE:= libdatabase_sqlcipher
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/jni/Application.mk b/jni/Application.mk
deleted file mode 100644
index ccc3b137..00000000
--- a/jni/Application.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-APP_PROJECT_PATH := $(shell pwd)
-APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk
-# fixes this error when building external/android-sqlite/android/sqlite3_android.cpp
-# icu4c/common/unicode/std_string.h:39:18: error: string: No such file or directory
-APP_STL := stlport_shared
diff --git a/jni/android_util_Binder.cpp b/jni/android_util_Binder.cpp
deleted file mode 100644
index 7a53874c..00000000
--- a/jni/android_util_Binder.cpp
+++ /dev/null
@@ -1,1707 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-#define LOG_TAG "JavaBinder"
-//#define LOG_NDEBUG 0
-
-#include "android_util_Binder.h"
-#include "JNIHelp.h"
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-//#undef LOGV
-//#define LOGV(...) fprintf(stderr, __VA_ARGS__)
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-
-static struct bindernative_offsets_t
-{
- // Class state.
- jclass mClass;
- jmethodID mExecTransact;
-
- // Object state.
- jfieldID mObject;
-
-} gBinderOffsets;
-
-// ----------------------------------------------------------------------------
-
-static struct binderinternal_offsets_t
-{
- // Class state.
- jclass mClass;
- jmethodID mForceGc;
-
-} gBinderInternalOffsets;
-
-// ----------------------------------------------------------------------------
-
-static struct debug_offsets_t
-{
- // Class state.
- jclass mClass;
-
-} gDebugOffsets;
-
-// ----------------------------------------------------------------------------
-
-static struct weakreference_offsets_t
-{
- // Class state.
- jclass mClass;
- jmethodID mGet;
-
-} gWeakReferenceOffsets;
-
-static struct error_offsets_t
-{
- jclass mClass;
-} gErrorOffsets;
-
-// ----------------------------------------------------------------------------
-
-static struct binderproxy_offsets_t
-{
- // Class state.
- jclass mClass;
- jmethodID mConstructor;
- jmethodID mSendDeathNotice;
-
- // Object state.
- jfieldID mObject;
- jfieldID mSelf;
-
-} gBinderProxyOffsets;
-
-// ----------------------------------------------------------------------------
-
-static struct parcel_offsets_t
-{
- jfieldID mObject;
- jfieldID mOwnObject;
-} gParcelOffsets;
-
-static struct log_offsets_t
-{
- // Class state.
- jclass mClass;
- jmethodID mLogE;
-} gLogOffsets;
-
-static struct file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
- jfieldID mDescriptor;
-} gFileDescriptorOffsets;
-
-static struct parcel_file_descriptor_offsets_t
-{
- jclass mClass;
- jmethodID mConstructor;
-} gParcelFileDescriptorOffsets;
-
-static struct strict_mode_callback_offsets_t
-{
- jclass mClass;
- jmethodID mCallback;
-} gStrictModeCallbackOffsets;
-
-// ****************************************************************************
-// ****************************************************************************
-// ****************************************************************************
-
-static volatile int32_t gNumRefsCreated = 0;
-static volatile int32_t gNumProxyRefs = 0;
-static volatile int32_t gNumLocalRefs = 0;
-static volatile int32_t gNumDeathRefs = 0;
-
-static void incRefsCreated(JNIEnv* env)
-{
- int old = android_atomic_inc(&gNumRefsCreated);
- if (old == 200) {
- android_atomic_and(0, &gNumRefsCreated);
- env->CallStaticVoidMethod(gBinderInternalOffsets.mClass,
- gBinderInternalOffsets.mForceGc);
- } else {
- LOGV("Now have %d binder ops", old);
- }
-}
-
-static JavaVM* jnienv_to_javavm(JNIEnv* env)
-{
- JavaVM* vm;
- return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
-}
-
-static JNIEnv* javavm_to_jnienv(JavaVM* vm)
-{
- JNIEnv* env;
- return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
-}
-
-static void report_exception(JNIEnv* env, jthrowable excep, const char* msg)
-{
- env->ExceptionClear();
-
- jstring tagstr = env->NewStringUTF(LOG_TAG);
- jstring msgstr = env->NewStringUTF(msg);
-
- if ((tagstr == NULL) || (msgstr == NULL)) {
- env->ExceptionClear(); /* assume exception (OOM?) was thrown */
- LOGE("Unable to call Log.e()\n");
- LOGE("%s", msg);
- goto bail;
- }
-
- env->CallStaticIntMethod(
- gLogOffsets.mClass, gLogOffsets.mLogE, tagstr, msgstr, excep);
- if (env->ExceptionCheck()) {
- /* attempting to log the failure has failed */
- LOGW("Failed trying to log exception, msg='%s'\n", msg);
- env->ExceptionClear();
- }
-
- if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) {
- /*
- * It's an Error: Reraise the exception, detach this thread, and
- * wait for the fireworks. Die even more blatantly after a minute
- * if the gentler attempt doesn't do the trick.
- *
- * The GetJavaVM function isn't on the "approved" list of JNI calls
- * that can be made while an exception is pending, so we want to
- * get the VM ptr, throw the exception, and then detach the thread.
- */
- JavaVM* vm = jnienv_to_javavm(env);
- env->Throw(excep);
- vm->DetachCurrentThread();
- sleep(60);
- LOGE("Forcefully exiting");
- exit(1);
- *((int *) 1) = 1;
- }
-
-bail:
- /* discard local refs created for us by VM */
- env->DeleteLocalRef(tagstr);
- env->DeleteLocalRef(msgstr);
-}
-
-static void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy)
-{
- // Call back into android.os.StrictMode#onBinderStrictModePolicyChange
- // to sync our state back to it. See the comments in StrictMode.java.
- env->CallStaticVoidMethod(gStrictModeCallbackOffsets.mClass,
- gStrictModeCallbackOffsets.mCallback,
- strict_policy);
-}
-
-class JavaBBinderHolder;
-
-class JavaBBinder : public BBinder
-{
-public:
- JavaBBinder(JNIEnv* env, jobject object)
- : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
- {
- LOGV("Creating JavaBBinder %p\n", this);
- android_atomic_inc(&gNumLocalRefs);
- incRefsCreated(env);
- }
-
- bool checkSubclass(const void* subclassID) const
- {
- return subclassID == &gBinderOffsets;
- }
-
- jobject object() const
- {
- return mObject;
- }
-
-protected:
- virtual ~JavaBBinder()
- {
- LOGV("Destroying JavaBBinder %p\n", this);
- android_atomic_dec(&gNumLocalRefs);
- JNIEnv* env = javavm_to_jnienv(mVM);
- env->DeleteGlobalRef(mObject);
- }
-
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
- {
- JNIEnv* env = javavm_to_jnienv(mVM);
-
- LOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
-
- IPCThreadState* thread_state = IPCThreadState::self();
- const int strict_policy_before = thread_state->getStrictModePolicy();
- thread_state->setLastTransactionBinderFlags(flags);
-
- //printf("Transact from %p to Java code sending: ", this);
- //data.print();
- //printf("\n");
- jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
- code, (int32_t)&data, (int32_t)reply, flags);
- jthrowable excep = env->ExceptionOccurred();
-
- // Restore the Java binder thread's state if it changed while
- // processing a call (as it would if the Parcel's header had a
- // new policy mask and Parcel.enforceInterface() changed
- // it...)
- const int strict_policy_after = thread_state->getStrictModePolicy();
- if (strict_policy_after != strict_policy_before) {
- // Our thread-local...
- thread_state->setStrictModePolicy(strict_policy_before);
- // And the Java-level thread-local...
- set_dalvik_blockguard_policy(env, strict_policy_before);
- }
-
- if (excep) {
- report_exception(env, excep,
- "*** Uncaught remote exception! "
- "(Exceptions are not yet supported across processes.)");
- res = JNI_FALSE;
-
- /* clean up JNI local ref -- we don't return to Java code */
- env->DeleteLocalRef(excep);
- }
-
- //aout << "onTransact to Java code; result=" << res << endl
- // << "Transact from " << this << " to Java code returning "
- // << reply << ": " << *reply << endl;
- return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
- }
-
- virtual status_t dump(int fd, const Vector& args)
- {
- return 0;
- }
-
-private:
- JavaVM* const mVM;
- jobject const mObject;
-};
-
-// ----------------------------------------------------------------------------
-
-class JavaBBinderHolder : public RefBase
-{
-public:
- JavaBBinderHolder(JNIEnv* env, jobject object)
- : mObject(object)
- {
- LOGV("Creating JavaBBinderHolder for Object %p\n", object);
- }
- ~JavaBBinderHolder()
- {
- LOGV("Destroying JavaBBinderHolder for Object %p\n", mObject);
- }
-
- sp get(JNIEnv* env)
- {
- AutoMutex _l(mLock);
- sp b = mBinder.promote();
- if (b == NULL) {
- b = new JavaBBinder(env, mObject);
- mBinder = b;
- LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n",
- b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount());
- }
-
- return b;
- }
-
- sp getExisting()
- {
- AutoMutex _l(mLock);
- return mBinder.promote();
- }
-
-private:
- Mutex mLock;
- jobject mObject;
- wp mBinder;
-};
-
-// ----------------------------------------------------------------------------
-
-class JavaDeathRecipient : public IBinder::DeathRecipient
-{
-public:
- JavaDeathRecipient(JNIEnv* env, jobject object)
- : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
- mHoldsRef(true)
- {
- incStrong(this);
- android_atomic_inc(&gNumDeathRefs);
- incRefsCreated(env);
- }
-
- void binderDied(const wp& who)
- {
- JNIEnv* env = javavm_to_jnienv(mVM);
-
- LOGV("Receiving binderDied() on JavaDeathRecipient %p\n", this);
-
- env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
- gBinderProxyOffsets.mSendDeathNotice, mObject);
- jthrowable excep = env->ExceptionOccurred();
- if (excep) {
- report_exception(env, excep,
- "*** Uncaught exception returned from death notification!");
- }
-
- clearReference();
- }
-
- void clearReference()
- {
- bool release = false;
- mLock.lock();
- if (mHoldsRef) {
- mHoldsRef = false;
- release = true;
- }
- mLock.unlock();
- if (release) {
- decStrong(this);
- }
- }
-
-protected:
- virtual ~JavaDeathRecipient()
- {
- //LOGI("Removing death ref: recipient=%p\n", mObject);
- android_atomic_dec(&gNumDeathRefs);
- JNIEnv* env = javavm_to_jnienv(mVM);
- env->DeleteGlobalRef(mObject);
- }
-
-private:
- JavaVM* const mVM;
- jobject const mObject;
- Mutex mLock;
- bool mHoldsRef;
-};
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-static void proxy_cleanup(const void* id, void* obj, void* cleanupCookie)
-{
- android_atomic_dec(&gNumProxyRefs);
- JNIEnv* env = javavm_to_jnienv((JavaVM*)cleanupCookie);
- env->DeleteGlobalRef((jobject)obj);
-}
-
-static Mutex mProxyLock;
-
-jobject javaObjectForIBinder(JNIEnv* env, const sp& val)
-{
- if (val == NULL) return NULL;
-
- if (val->checkSubclass(&gBinderOffsets)) {
- // One of our own!
- jobject object = static_cast(val.get())->object();
- //printf("objectForBinder %p: it's our own %p!\n", val.get(), object);
- return object;
- }
-
- // For the rest of the function we will hold this lock, to serialize
- // looking/creation of Java proxies for native Binder proxies.
- AutoMutex _l(mProxyLock);
-
- // Someone else's... do we know about it?
- jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
- if (object != NULL) {
- jobject res = env->CallObjectMethod(object, gWeakReferenceOffsets.mGet);
- if (res != NULL) {
- LOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
- return res;
- }
- LOGV("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
- android_atomic_dec(&gNumProxyRefs);
- val->detachObject(&gBinderProxyOffsets);
- env->DeleteGlobalRef(object);
- }
-
- object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
- if (object != NULL) {
- LOGV("objectForBinder %p: created new %p!\n", val.get(), object);
- // The proxy holds a reference to the native object.
- env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
- val->incStrong(object);
-
- // The native object needs to hold a weak reference back to the
- // proxy, so we can retrieve the same proxy if it is still active.
- jobject refObject = env->NewGlobalRef(
- env->GetObjectField(object, gBinderProxyOffsets.mSelf));
- val->attachObject(&gBinderProxyOffsets, refObject,
- jnienv_to_javavm(env), proxy_cleanup);
-
- // Note that a new object reference has been created.
- android_atomic_inc(&gNumProxyRefs);
- incRefsCreated(env);
- }
-
- return object;
-}
-
-sp ibinderForJavaObject(JNIEnv* env, jobject obj)
-{
- if (obj == NULL) return NULL;
-
- if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
- JavaBBinderHolder* jbh = (JavaBBinderHolder*)
- env->GetIntField(obj, gBinderOffsets.mObject);
- return jbh != NULL ? jbh->get(env) : NULL;
- }
-
- if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
- return (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- }
-
- LOGW("ibinderForJavaObject: %p is not a Binder object", obj);
- return NULL;
-}
-
-Parcel* parcelForJavaObject(JNIEnv* env, jobject obj)
-{
- if (obj) {
- Parcel* p = (Parcel*)env->GetIntField(obj, gParcelOffsets.mObject);
- if (p != NULL) {
- return p;
- }
- jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!");
- }
- return NULL;
-}
-
-jobject newFileDescriptor(JNIEnv* env, int fd)
-{
- jobject object = env->NewObject(
- gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
- if (object != NULL) {
- //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
- }
- return object;
-}
-
-jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc)
-{
- return env->NewObject(
- gParcelFileDescriptorOffsets.mClass, gParcelFileDescriptorOffsets.mConstructor, fileDesc);
-}
-
-void signalExceptionForError(JNIEnv* env, jobject obj, status_t err)
-{
- switch (err) {
- case UNKNOWN_ERROR:
- jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
- break;
- case NO_MEMORY:
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- break;
- case INVALID_OPERATION:
- jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
- break;
- case BAD_VALUE:
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- break;
- case BAD_INDEX:
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL);
- break;
- case BAD_TYPE:
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- break;
- case NAME_NOT_FOUND:
- jniThrowException(env, "java/util/NoSuchElementException", NULL);
- break;
- case PERMISSION_DENIED:
- jniThrowException(env, "java/lang/SecurityException", NULL);
- break;
- case NOT_ENOUGH_DATA:
- jniThrowException(env, "android/os/ParcelFormatException", "Not enough data");
- break;
- case NO_INIT:
- jniThrowException(env, "java/lang/RuntimeException", "Not initialized");
- break;
- case ALREADY_EXISTS:
- jniThrowException(env, "java/lang/RuntimeException", "Item already exists");
- break;
- case DEAD_OBJECT:
- jniThrowException(env, "android/os/DeadObjectException", NULL);
- break;
- case UNKNOWN_TRANSACTION:
- jniThrowException(env, "java/lang/RuntimeException", "Unknown transaction code");
- break;
- case FAILED_TRANSACTION:
- LOGE("!!! FAILED BINDER TRANSACTION !!!");
- //jniThrowException(env, "java/lang/OutOfMemoryError", "Binder transaction too large");
- break;
- default:
- LOGE("Unknown binder error code. 0x%x", err);
- }
-}
-
-}
-
-// ----------------------------------------------------------------------------
-
-static jint android_os_Binder_getCallingPid(JNIEnv* env, jobject clazz)
-{
- return IPCThreadState::self()->getCallingPid();
-}
-
-static jint android_os_Binder_getCallingUid(JNIEnv* env, jobject clazz)
-{
- return IPCThreadState::self()->getCallingUid();
-}
-
-static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
-{
- return IPCThreadState::self()->clearCallingIdentity();
-}
-
-static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
-{
- IPCThreadState::self()->restoreCallingIdentity(token);
-}
-
-static void android_os_Binder_setThreadStrictModePolicy(JNIEnv* env, jobject clazz, jint policyMask)
-{
- IPCThreadState::self()->setStrictModePolicy(policyMask);
-}
-
-static jint android_os_Binder_getThreadStrictModePolicy(JNIEnv* env, jobject clazz)
-{
- return IPCThreadState::self()->getStrictModePolicy();
-}
-
-static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
-{
- IPCThreadState::self()->flushCommands();
-}
-
-static void android_os_Binder_init(JNIEnv* env, jobject clazz)
-{
- JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz);
- if (jbh == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- return;
- }
- LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh);
- jbh->incStrong(clazz);
- env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh);
-}
-
-static void android_os_Binder_destroy(JNIEnv* env, jobject clazz)
-{
- JavaBBinderHolder* jbh = (JavaBBinderHolder*)
- env->GetIntField(clazz, gBinderOffsets.mObject);
- if (jbh != NULL) {
- env->SetIntField(clazz, gBinderOffsets.mObject, 0);
- LOGV("Java Binder %p: removing ref on holder %p", clazz, jbh);
- jbh->decStrong(clazz);
- } else {
- // Encountering an uninitialized binder is harmless. All it means is that
- // the Binder was only partially initialized when its finalizer ran and called
- // destroy(). The Binder could be partially initialized for several reasons.
- // For example, a Binder subclass constructor might have thrown an exception before
- // it could delegate to its superclass's constructor. Consequently init() would
- // not have been called and the holder pointer would remain NULL.
- LOGV("Java Binder %p: ignoring uninitialized binder", clazz);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gBinderMethods[] = {
- /* name, signature, funcPtr */
- { "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
- { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
- { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
- { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
- { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
- { "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
- { "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
- { "init", "()V", (void*)android_os_Binder_init },
- { "destroy", "()V", (void*)android_os_Binder_destroy }
-};
-
-const char* const kBinderPathName = "android/os/Binder";
-
-static int int_register_android_os_Binder(JNIEnv* env)
-{
- jclass clazz;
-
- clazz = env->FindClass(kBinderPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Binder");
-
- gBinderOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gBinderOffsets.mExecTransact
- = env->GetMethodID(clazz, "execTransact", "(IIII)Z");
- assert(gBinderOffsets.mExecTransact);
-
- gBinderOffsets.mObject
- = env->GetFieldID(clazz, "mObject", "I");
- assert(gBinderOffsets.mObject);
-
- return AndroidRuntime::registerNativeMethods(
- env, kBinderPathName,
- gBinderMethods, NELEM(gBinderMethods));
-}
-
-// ****************************************************************************
-// ****************************************************************************
-// ****************************************************************************
-
-namespace android {
-
-jint android_os_Debug_getLocalObjectCount(JNIEnv* env, jobject clazz)
-{
- return gNumLocalRefs;
-}
-
-jint android_os_Debug_getProxyObjectCount(JNIEnv* env, jobject clazz)
-{
- return gNumProxyRefs;
-}
-
-jint android_os_Debug_getDeathObjectCount(JNIEnv* env, jobject clazz)
-{
- return gNumDeathRefs;
-}
-
-}
-
-// ****************************************************************************
-// ****************************************************************************
-// ****************************************************************************
-
-static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
-{
- sp b = ProcessState::self()->getContextObject(NULL);
- return javaObjectForIBinder(env, b);
-}
-
-static void android_os_BinderInternal_joinThreadPool(JNIEnv* env, jobject clazz)
-{
- sp b = ProcessState::self()->getContextObject(NULL);
- android::IPCThreadState::self()->joinThreadPool();
-}
-
-static void android_os_BinderInternal_disableBackgroundScheduling(JNIEnv* env,
- jobject clazz, jboolean disable)
-{
- IPCThreadState::disableBackgroundScheduling(disable ? true : false);
-}
-
-static void android_os_BinderInternal_handleGc(JNIEnv* env, jobject clazz)
-{
- LOGV("Gc has executed, clearing binder ops");
- android_atomic_and(0, &gNumRefsCreated);
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gBinderInternalMethods[] = {
- /* name, signature, funcPtr */
- { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
- { "joinThreadPool", "()V", (void*)android_os_BinderInternal_joinThreadPool },
- { "disableBackgroundScheduling", "(Z)V", (void*)android_os_BinderInternal_disableBackgroundScheduling },
- { "handleGc", "()V", (void*)android_os_BinderInternal_handleGc }
-};
-
-const char* const kBinderInternalPathName = "com/android/internal/os/BinderInternal";
-
-static int int_register_android_os_BinderInternal(JNIEnv* env)
-{
- jclass clazz;
-
- clazz = env->FindClass(kBinderInternalPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class com.android.internal.os.BinderInternal");
-
- gBinderInternalOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gBinderInternalOffsets.mForceGc
- = env->GetStaticMethodID(clazz, "forceBinderGc", "()V");
- assert(gBinderInternalOffsets.mForceGc);
-
- return AndroidRuntime::registerNativeMethods(
- env, kBinderInternalPathName,
- gBinderInternalMethods, NELEM(gBinderInternalMethods));
-}
-
-// ****************************************************************************
-// ****************************************************************************
-// ****************************************************************************
-
-static jboolean android_os_BinderProxy_pingBinder(JNIEnv* env, jobject obj)
-{
- IBinder* target = (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- if (target == NULL) {
- return JNI_FALSE;
- }
- status_t err = target->pingBinder();
- return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
-}
-
-static jstring android_os_BinderProxy_getInterfaceDescriptor(JNIEnv* env, jobject obj)
-{
- IBinder* target = (IBinder*) env->GetIntField(obj, gBinderProxyOffsets.mObject);
- if (target != NULL) {
- const String16& desc = target->getInterfaceDescriptor();
- return env->NewString(desc.string(), desc.size());
- }
- jniThrowException(env, "java/lang/RuntimeException",
- "No binder found for object");
- return NULL;
-}
-
-static jboolean android_os_BinderProxy_isBinderAlive(JNIEnv* env, jobject obj)
-{
- IBinder* target = (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- if (target == NULL) {
- return JNI_FALSE;
- }
- bool alive = target->isBinderAlive();
- return alive ? JNI_TRUE : JNI_FALSE;
-}
-
-static int getprocname(pid_t pid, char *buf, size_t len) {
- char filename[20];
- FILE *f;
-
- sprintf(filename, "/proc/%d/cmdline", pid);
- f = fopen(filename, "r");
- if (!f) { *buf = '\0'; return 1; }
- if (!fgets(buf, len, f)) { *buf = '\0'; return 2; }
- fclose(f);
- return 0;
-}
-
-static bool push_eventlog_string(char** pos, const char* end, const char* str) {
- jint len = strlen(str);
- int space_needed = 1 + sizeof(len) + len;
- if (end - *pos < space_needed) {
- LOGW("not enough space for string. remain=%d; needed=%d",
- (end - *pos), space_needed);
- return false;
- }
- **pos = EVENT_TYPE_STRING;
- (*pos)++;
- memcpy(*pos, &len, sizeof(len));
- *pos += sizeof(len);
- memcpy(*pos, str, len);
- *pos += len;
- return true;
-}
-
-static bool push_eventlog_int(char** pos, const char* end, jint val) {
- int space_needed = 1 + sizeof(val);
- if (end - *pos < space_needed) {
- LOGW("not enough space for int. remain=%d; needed=%d",
- (end - *pos), space_needed);
- return false;
- }
- **pos = EVENT_TYPE_INT;
- (*pos)++;
- memcpy(*pos, &val, sizeof(val));
- *pos += sizeof(val);
- return true;
-}
-
-// From frameworks/base/core/java/android/content/EventLogTags.logtags:
-#define LOGTAG_BINDER_OPERATION 52004
-
-static void conditionally_log_binder_call(int64_t start_millis,
- IBinder* target, jint code) {
- int duration_ms = static_cast(uptimeMillis() - start_millis);
-
- int sample_percent;
- if (duration_ms >= 500) {
- sample_percent = 100;
- } else {
- sample_percent = 100 * duration_ms / 500;
- if (sample_percent == 0) {
- return;
- }
- if (sample_percent < (random() % 100 + 1)) {
- return;
- }
- }
-
- char process_name[40];
- getprocname(getpid(), process_name, sizeof(process_name));
- String8 desc(target->getInterfaceDescriptor());
-
- char buf[LOGGER_ENTRY_MAX_PAYLOAD];
- buf[0] = EVENT_TYPE_LIST;
- buf[1] = 5;
- char* pos = &buf[2];
- char* end = &buf[LOGGER_ENTRY_MAX_PAYLOAD - 1]; // leave room for final \n
- if (!push_eventlog_string(&pos, end, desc.string())) return;
- if (!push_eventlog_int(&pos, end, code)) return;
- if (!push_eventlog_int(&pos, end, duration_ms)) return;
- if (!push_eventlog_string(&pos, end, process_name)) return;
- if (!push_eventlog_int(&pos, end, sample_percent)) return;
- *(pos++) = '\n'; // conventional with EVENT_TYPE_LIST apparently.
- android_bWriteLog(LOGTAG_BINDER_OPERATION, buf, pos - buf);
-}
-
-// We only measure binder call durations to potentially log them if
-// we're on the main thread. Unfortunately sim-eng doesn't seem to
-// have gettid, so we just ignore this and don't log if we can't
-// get the thread id.
-static bool should_time_binder_calls() {
-#ifdef HAVE_GETTID
- return (getpid() == androidGetTid());
-#else
-#warning no gettid(), so not logging Binder calls...
- return false;
-#endif
-}
-
-static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
- jint code, jobject dataObj,
- jobject replyObj, jint flags)
-{
- if (dataObj == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return JNI_FALSE;
- }
-
- Parcel* data = parcelForJavaObject(env, dataObj);
- if (data == NULL) {
- return JNI_FALSE;
- }
- Parcel* reply = parcelForJavaObject(env, replyObj);
- if (reply == NULL && replyObj != NULL) {
- return JNI_FALSE;
- }
-
- IBinder* target = (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- if (target == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
- return JNI_FALSE;
- }
-
- LOGV("Java code calling transact on %p in Java object %p with code %d\n",
- target, obj, code);
-
- // Only log the binder call duration for things on the Java-level main thread.
- // But if we don't
- const bool time_binder_calls = should_time_binder_calls();
-
- int64_t start_millis;
- if (time_binder_calls) {
- start_millis = uptimeMillis();
- }
- //printf("Transact from Java code to %p sending: ", target); data->print();
- status_t err = target->transact(code, *data, reply, flags);
- //if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
- if (time_binder_calls) {
- conditionally_log_binder_call(start_millis, target, code);
- }
-
- if (err == NO_ERROR) {
- return JNI_TRUE;
- } else if (err == UNKNOWN_TRANSACTION) {
- return JNI_FALSE;
- }
-
- signalExceptionForError(env, obj, err);
- return JNI_FALSE;
-}
-
-static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
- jobject recipient, jint flags)
-{
- if (recipient == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return;
- }
-
- IBinder* target = (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- if (target == NULL) {
- LOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
- assert(false);
- }
-
- LOGV("linkToDeath: binder=%p recipient=%p\n", target, recipient);
-
- if (!target->localBinder()) {
- sp jdr = new JavaDeathRecipient(env, recipient);
- status_t err = target->linkToDeath(jdr, recipient, flags);
- if (err != NO_ERROR) {
- // Failure adding the death recipient, so clear its reference
- // now.
- jdr->clearReference();
- signalExceptionForError(env, obj, err);
- }
- }
-}
-
-static jboolean android_os_BinderProxy_unlinkToDeath(JNIEnv* env, jobject obj,
- jobject recipient, jint flags)
-{
- jboolean res = JNI_FALSE;
- if (recipient == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return res;
- }
-
- IBinder* target = (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- if (target == NULL) {
- LOGW("Binder has been finalized when calling linkToDeath() with recip=%p)\n", recipient);
- return JNI_FALSE;
- }
-
- LOGV("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
-
- if (!target->localBinder()) {
- wp dr;
- status_t err = target->unlinkToDeath(NULL, recipient, flags, &dr);
- if (err == NO_ERROR && dr != NULL) {
- sp sdr = dr.promote();
- JavaDeathRecipient* jdr = static_cast(sdr.get());
- if (jdr != NULL) {
- jdr->clearReference();
- }
- }
- if (err == NO_ERROR || err == DEAD_OBJECT) {
- res = JNI_TRUE;
- } else {
- jniThrowException(env, "java/util/NoSuchElementException",
- "Death link does not exist");
- }
- }
-
- return res;
-}
-
-static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
-{
- IBinder* b = (IBinder*)
- env->GetIntField(obj, gBinderProxyOffsets.mObject);
- LOGV("Destroying BinderProxy %p: binder=%p\n", obj, b);
- env->SetIntField(obj, gBinderProxyOffsets.mObject, 0);
- b->decStrong(obj);
- IPCThreadState::self()->flushCommands();
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gBinderProxyMethods[] = {
- /* name, signature, funcPtr */
- {"pingBinder", "()Z", (void*)android_os_BinderProxy_pingBinder},
- {"isBinderAlive", "()Z", (void*)android_os_BinderProxy_isBinderAlive},
- {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
- {"transact", "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
- {"linkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
- {"unlinkToDeath", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
- {"destroy", "()V", (void*)android_os_BinderProxy_destroy},
-};
-
-const char* const kBinderProxyPathName = "android/os/BinderProxy";
-
-static int int_register_android_os_BinderProxy(JNIEnv* env)
-{
- jclass clazz;
-
- clazz = env->FindClass("java/lang/ref/WeakReference");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.ref.WeakReference");
- gWeakReferenceOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gWeakReferenceOffsets.mGet
- = env->GetMethodID(clazz, "get", "()Ljava/lang/Object;");
- assert(gWeakReferenceOffsets.mGet);
-
- clazz = env->FindClass("java/lang/Error");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.lang.Error");
- gErrorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
-
- clazz = env->FindClass(kBinderProxyPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.BinderProxy");
-
- gBinderProxyOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gBinderProxyOffsets.mConstructor
- = env->GetMethodID(clazz, "", "()V");
- assert(gBinderProxyOffsets.mConstructor);
- gBinderProxyOffsets.mSendDeathNotice
- = env->GetStaticMethodID(clazz, "sendDeathNotice", "(Landroid/os/IBinder$DeathRecipient;)V");
- assert(gBinderProxyOffsets.mSendDeathNotice);
-
- gBinderProxyOffsets.mObject
- = env->GetFieldID(clazz, "mObject", "I");
- assert(gBinderProxyOffsets.mObject);
- gBinderProxyOffsets.mSelf
- = env->GetFieldID(clazz, "mSelf", "Ljava/lang/ref/WeakReference;");
- assert(gBinderProxyOffsets.mSelf);
-
- return AndroidRuntime::registerNativeMethods(
- env, kBinderProxyPathName,
- gBinderProxyMethods, NELEM(gBinderProxyMethods));
-}
-
-// ****************************************************************************
-// ****************************************************************************
-// ****************************************************************************
-
-static jint android_os_Parcel_dataSize(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- return parcel ? parcel->dataSize() : 0;
-}
-
-static jint android_os_Parcel_dataAvail(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- return parcel ? parcel->dataAvail() : 0;
-}
-
-static jint android_os_Parcel_dataPosition(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- return parcel ? parcel->dataPosition() : 0;
-}
-
-static jint android_os_Parcel_dataCapacity(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- return parcel ? parcel->dataCapacity() : 0;
-}
-
-static void android_os_Parcel_setDataSize(JNIEnv* env, jobject clazz, jint size)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->setDataSize(size);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_setDataPosition(JNIEnv* env, jobject clazz, jint pos)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- parcel->setDataPosition(pos);
- }
-}
-
-static void android_os_Parcel_setDataCapacity(JNIEnv* env, jobject clazz, jint size)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->setDataCapacity(size);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeNative(JNIEnv* env, jobject clazz,
- jobject data, jint offset,
- jint length)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel == NULL) {
- return;
- }
- void *dest;
-
- const status_t err = parcel->writeInt32(length);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
-
- dest = parcel->writeInplace(length);
-
- if (dest == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- return;
- }
-
- jbyte* ar = (jbyte*)env->GetPrimitiveArrayCritical((jarray)data, 0);
- if (ar) {
- memcpy(dest, ar, length);
- env->ReleasePrimitiveArrayCritical((jarray)data, ar, 0);
- }
-}
-
-
-static void android_os_Parcel_writeInt(JNIEnv* env, jobject clazz, jint val)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->writeInt32(val);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeLong(JNIEnv* env, jobject clazz, jlong val)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->writeInt64(val);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeFloat(JNIEnv* env, jobject clazz, jfloat val)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->writeFloat(val);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeDouble(JNIEnv* env, jobject clazz, jdouble val)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->writeDouble(val);
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeString(JNIEnv* env, jobject clazz, jstring val)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- status_t err = NO_MEMORY;
- if (val) {
- const jchar* str = env->GetStringCritical(val, 0);
- if (str) {
- err = parcel->writeString16(str, env->GetStringLength(val));
- env->ReleaseStringCritical(val, str);
- }
- } else {
- err = parcel->writeString16(NULL, 0);
- }
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const status_t err = parcel->writeDupFileDescriptor(
- env->GetIntField(object, gFileDescriptorOffsets.mDescriptor));
- if (err != NO_ERROR) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- }
- }
-}
-
-static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jobject clazz)
-{
- jbyteArray ret = NULL;
-
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- int32_t len = parcel->readInt32();
-
- // sanity check the stored length against the true data size
- if (len >= 0 && len <= (int32_t)parcel->dataAvail()) {
- ret = env->NewByteArray(len);
-
- if (ret != NULL) {
- jbyte* a2 = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
- if (a2) {
- const void* data = parcel->readInplace(len);
- memcpy(a2, data, len);
- env->ReleasePrimitiveArrayCritical(ret, a2, 0);
- }
- }
- }
- }
-
- return ret;
-}
-
-static jint android_os_Parcel_readInt(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- return parcel->readInt32();
- }
- return 0;
-}
-
-static jlong android_os_Parcel_readLong(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- return parcel->readInt64();
- }
- return 0;
-}
-
-static jfloat android_os_Parcel_readFloat(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- return parcel->readFloat();
- }
- return 0;
-}
-
-static jdouble android_os_Parcel_readDouble(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- return parcel->readDouble();
- }
- return 0;
-}
-
-static jstring android_os_Parcel_readString(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str) {
- return env->NewString(str, len);
- }
- return NULL;
- }
- return NULL;
-}
-
-static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- return javaObjectForIBinder(env, parcel->readStrongBinder());
- }
- return NULL;
-}
-
-static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- int fd = parcel->readFileDescriptor();
- if (fd < 0) return NULL;
- fd = dup(fd);
- if (fd < 0) return NULL;
- jobject object = env->NewObject(
- gFileDescriptorOffsets.mClass, gFileDescriptorOffsets.mConstructor);
- if (object != NULL) {
- //LOGI("Created new FileDescriptor %p with fd %d\n", object, fd);
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, fd);
- }
- return object;
- }
- return NULL;
-}
-
-static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jobject clazz,
- jstring name, jint mode)
-{
- if (name == NULL) {
- jniThrowException(env, "java/lang/NullPointerException", NULL);
- return NULL;
- }
- const jchar* str = env->GetStringCritical(name, 0);
- if (str == NULL) {
- // Whatever, whatever.
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
- String8 name8(str, env->GetStringLength(name));
- env->ReleaseStringCritical(name, str);
- int flags=0;
- switch (mode&0x30000000) {
- case 0:
- case 0x10000000:
- flags = O_RDONLY;
- break;
- case 0x20000000:
- flags = O_WRONLY;
- break;
- case 0x30000000:
- flags = O_RDWR;
- break;
- }
-
- if (mode&0x08000000) flags |= O_CREAT;
- if (mode&0x04000000) flags |= O_TRUNC;
- if (mode&0x02000000) flags |= O_APPEND;
-
- int realMode = S_IRWXU|S_IRWXG;
- if (mode&0x00000001) realMode |= S_IROTH;
- if (mode&0x00000002) realMode |= S_IWOTH;
-
- int fd = open(name8.string(), flags, realMode);
- if (fd < 0) {
- jniThrowException(env, "java/io/FileNotFoundException", NULL);
- return NULL;
- }
- jobject object = newFileDescriptor(env, fd);
- if (object == NULL) {
- close(fd);
- }
- return object;
-}
-
-static void android_os_Parcel_closeFileDescriptor(JNIEnv* env, jobject clazz, jobject object)
-{
- int fd = env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
- if (fd >= 0) {
- env->SetIntField(object, gFileDescriptorOffsets.mDescriptor, -1);
- //LOGI("Closing ParcelFileDescriptor %d\n", fd);
- close(fd);
- }
-}
-
-static void android_os_Parcel_freeBuffer(JNIEnv* env, jobject clazz)
-{
- int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject);
- if (own) {
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- //LOGI("Parcel.freeBuffer() called for C++ Parcel %p\n", parcel);
- parcel->freeData();
- }
- }
-}
-
-static void android_os_Parcel_init(JNIEnv* env, jobject clazz, jint parcelInt)
-{
- Parcel* parcel = (Parcel*)parcelInt;
- int own = 0;
- if (!parcel) {
- //LOGI("Initializing obj %p: creating new Parcel\n", clazz);
- own = 1;
- parcel = new Parcel;
- } else {
- //LOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel);
- }
- if (parcel == NULL) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- return;
- }
- //LOGI("Initializing obj %p from C++ Parcel %p, own=%d\n", clazz, parcel, own);
- env->SetIntField(clazz, gParcelOffsets.mOwnObject, own);
- env->SetIntField(clazz, gParcelOffsets.mObject, (int)parcel);
-}
-
-static void android_os_Parcel_destroy(JNIEnv* env, jobject clazz)
-{
- int32_t own = env->GetIntField(clazz, gParcelOffsets.mOwnObject);
- if (own) {
- Parcel* parcel = parcelForJavaObject(env, clazz);
- env->SetIntField(clazz, gParcelOffsets.mObject, 0);
- //LOGI("Destroying obj %p: deleting C++ Parcel %p\n", clazz, parcel);
- delete parcel;
- } else {
- env->SetIntField(clazz, gParcelOffsets.mObject, 0);
- //LOGI("Destroying obj %p: leaving C++ Parcel %p\n", clazz);
- }
-}
-
-static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jobject clazz)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel == NULL) {
- return NULL;
- }
-
- // do not marshall if there are binder objects in the parcel
- if (parcel->objectsCount())
- {
- jniThrowException(env, "java/lang/RuntimeException", "Tried to marshall a Parcel that contained Binder objects.");
- return NULL;
- }
-
- jbyteArray ret = env->NewByteArray(parcel->dataSize());
-
- if (ret != NULL)
- {
- jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
- if (array != NULL)
- {
- memcpy(array, parcel->data(), parcel->dataSize());
- env->ReleasePrimitiveArrayCritical(ret, array, 0);
- }
- }
-
- return ret;
-}
-
-static void android_os_Parcel_unmarshall(JNIEnv* env, jobject clazz, jbyteArray data, jint offset, jint length)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel == NULL || length < 0) {
- return;
- }
-
- jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0);
- if (array)
- {
- parcel->setDataSize(length);
- parcel->setDataPosition(0);
-
- void* raw = parcel->writeInplace(length);
- memcpy(raw, (array + offset), length);
-
- env->ReleasePrimitiveArrayCritical(data, array, 0);
- }
-}
-
-static void android_os_Parcel_appendFrom(JNIEnv* env, jobject clazz, jobject parcel, jint offset, jint length)
-{
- Parcel* thisParcel = parcelForJavaObject(env, clazz);
- if (thisParcel == NULL) {
- return;
- }
- Parcel* otherParcel = parcelForJavaObject(env, parcel);
- if (otherParcel == NULL) {
- return;
- }
-
- (void) thisParcel->appendFrom(otherParcel, offset, length);
-}
-
-static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jobject clazz)
-{
- jboolean ret = JNI_FALSE;
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- if (parcel->hasFileDescriptors()) {
- ret = JNI_TRUE;
- }
- }
- return ret;
-}
-
-static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jobject clazz, jstring name)
-{
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- // In the current implementation, the token is just the serialized interface name that
- // the caller expects to be invoking
- const jchar* str = env->GetStringCritical(name, 0);
- if (str != NULL) {
- parcel->writeInterfaceToken(String16(str, env->GetStringLength(name)));
- env->ReleaseStringCritical(name, str);
- }
- }
-}
-
-static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstring name)
-{
- jboolean ret = JNI_FALSE;
-
- Parcel* parcel = parcelForJavaObject(env, clazz);
- if (parcel != NULL) {
- const jchar* str = env->GetStringCritical(name, 0);
- if (str) {
- IPCThreadState* threadState = IPCThreadState::self();
- const int32_t oldPolicy = threadState->getStrictModePolicy();
- const bool isValid = parcel->enforceInterface(
- String16(str, env->GetStringLength(name)),
- threadState);
- env->ReleaseStringCritical(name, str);
- if (isValid) {
- const int32_t newPolicy = threadState->getStrictModePolicy();
- if (oldPolicy != newPolicy) {
- // Need to keep the Java-level thread-local strict
- // mode policy in sync for the libcore
- // enforcements, which involves an upcall back
- // into Java. (We can't modify the
- // Parcel.enforceInterface signature, as it's
- // pseudo-public, and used via AIDL
- // auto-generation...)
- set_dalvik_blockguard_policy(env, newPolicy);
- }
- return; // everything was correct -> return silently
- }
- }
- }
-
- // all error conditions wind up here
- jniThrowException(env, "java/lang/SecurityException",
- "Binder invocation to an incorrect interface");
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gParcelMethods[] = {
- {"dataSize", "()I", (void*)android_os_Parcel_dataSize},
- {"dataAvail", "()I", (void*)android_os_Parcel_dataAvail},
- {"dataPosition", "()I", (void*)android_os_Parcel_dataPosition},
- {"dataCapacity", "()I", (void*)android_os_Parcel_dataCapacity},
- {"setDataSize", "(I)V", (void*)android_os_Parcel_setDataSize},
- {"setDataPosition", "(I)V", (void*)android_os_Parcel_setDataPosition},
- {"setDataCapacity", "(I)V", (void*)android_os_Parcel_setDataCapacity},
- {"writeNative", "([BII)V", (void*)android_os_Parcel_writeNative},
- {"writeInt", "(I)V", (void*)android_os_Parcel_writeInt},
- {"writeLong", "(J)V", (void*)android_os_Parcel_writeLong},
- {"writeFloat", "(F)V", (void*)android_os_Parcel_writeFloat},
- {"writeDouble", "(D)V", (void*)android_os_Parcel_writeDouble},
- {"writeString", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeString},
- {"writeStrongBinder", "(Landroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
- {"writeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor},
- {"createByteArray", "()[B", (void*)android_os_Parcel_createByteArray},
- {"readInt", "()I", (void*)android_os_Parcel_readInt},
- {"readLong", "()J", (void*)android_os_Parcel_readLong},
- {"readFloat", "()F", (void*)android_os_Parcel_readFloat},
- {"readDouble", "()D", (void*)android_os_Parcel_readDouble},
- {"readString", "()Ljava/lang/String;", (void*)android_os_Parcel_readString},
- {"readStrongBinder", "()Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
- {"internalReadFileDescriptor", "()Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
- {"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor},
- {"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor},
- {"freeBuffer", "()V", (void*)android_os_Parcel_freeBuffer},
- {"init", "(I)V", (void*)android_os_Parcel_init},
- {"destroy", "()V", (void*)android_os_Parcel_destroy},
- {"marshall", "()[B", (void*)android_os_Parcel_marshall},
- {"unmarshall", "([BII)V", (void*)android_os_Parcel_unmarshall},
- {"appendFrom", "(Landroid/os/Parcel;II)V", (void*)android_os_Parcel_appendFrom},
- {"hasFileDescriptors", "()Z", (void*)android_os_Parcel_hasFileDescriptors},
- {"writeInterfaceToken", "(Ljava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
- {"enforceInterface", "(Ljava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
-};
-
-const char* const kParcelPathName = "android/os/Parcel";
-
-static int int_register_android_os_Parcel(JNIEnv* env)
-{
- jclass clazz;
-
- clazz = env->FindClass("android/util/Log");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.util.Log");
- gLogOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gLogOffsets.mLogE = env->GetStaticMethodID(
- clazz, "e", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");
- assert(gLogOffsets.mLogE);
-
- clazz = env->FindClass("java/io/FileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
- gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gFileDescriptorOffsets.mConstructor
- = env->GetMethodID(clazz, "", "()V");
- gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
- LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
- "Unable to find descriptor field in java.io.FileDescriptor");
-
- clazz = env->FindClass("android/os/ParcelFileDescriptor");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
- gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gParcelFileDescriptorOffsets.mConstructor
- = env->GetMethodID(clazz, "", "(Ljava/io/FileDescriptor;)V");
-
- clazz = env->FindClass(kParcelPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel");
-
- gParcelOffsets.mObject
- = env->GetFieldID(clazz, "mObject", "I");
- gParcelOffsets.mOwnObject
- = env->GetFieldID(clazz, "mOwnObject", "I");
-
- clazz = env->FindClass("android/os/StrictMode");
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.StrictMode");
- gStrictModeCallbackOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gStrictModeCallbackOffsets.mCallback = env->GetStaticMethodID(
- clazz, "onBinderStrictModePolicyChange", "(I)V");
- LOG_FATAL_IF(gStrictModeCallbackOffsets.mCallback == NULL,
- "Unable to find strict mode callback.");
-
- return AndroidRuntime::registerNativeMethods(
- env, kParcelPathName,
- gParcelMethods, NELEM(gParcelMethods));
-}
-
-int register_android_os_Binder(JNIEnv* env)
-{
- if (int_register_android_os_Binder(env) < 0)
- return -1;
- if (int_register_android_os_BinderInternal(env) < 0)
- return -1;
- if (int_register_android_os_BinderProxy(env) < 0)
- return -1;
- if (int_register_android_os_Parcel(env) < 0)
- return -1;
- return 0;
-}
-
-namespace android {
-
-// Returns the Unix file descriptor for a ParcelFileDescriptor object
-int getParcelFileDescriptorFD(JNIEnv* env, jobject object)
-{
- return env->GetIntField(object, gFileDescriptorOffsets.mDescriptor);
-}
-
-}
diff --git a/jni/android_util_Binder.h b/jni/android_util_Binder.h
deleted file mode 100644
index 495e76a9..00000000
--- a/jni/android_util_Binder.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* //device/libs/android_runtime/android_util_Binder.h
-**
-** Copyright 2006, 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.
-*/
-
-#include
-
-#include "jni.h"
-
-namespace android {
-
-// Converstion to/from Java IBinder Object and C++ IBinder instance.
-extern jobject javaObjectForIBinder(JNIEnv* env, const sp& val);
-extern sp ibinderForJavaObject(JNIEnv* env, jobject obj);
-
-// Conversion from Java Parcel Object to C++ Parcel instance.
-// Note: does not type checking; must guarantee jobject is a Java Parcel
-extern Parcel* parcelForJavaObject(JNIEnv* env, jobject obj);
-
-extern jobject newFileDescriptor(JNIEnv* env, int fd);
-extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc);
-
-}
diff --git a/jni/include/utils/Log.h b/jni/include/utils/Log.h
deleted file mode 100644
index 3c6cc8bd..00000000
--- a/jni/include/utils/Log.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// C/C++ logging functions. See the logging documentation for API details.
-//
-// We'd like these to be available from C code (in case we import some from
-// somewhere), so this has a C interface.
-//
-// The output will be correct when the log file is shared between multiple
-// threads and/or multiple processes so long as the operating system
-// supports O_APPEND. These calls have mutex-protected data structures
-// and so are NOT reentrant. Do not use LOG in a signal handler.
-//
-#ifndef _LIBS_UTILS_LOG_H
-#define _LIBS_UTILS_LOG_H
-
-#include
-
-#endif // _LIBS_UTILS_LOG_H
diff --git a/jni/net_sqlcipher_CursorWindow.cpp b/jni/net_sqlcipher_CursorWindow.cpp
deleted file mode 100644
index c1e53af0..00000000
--- a/jni/net_sqlcipher_CursorWindow.cpp
+++ /dev/null
@@ -1,723 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "CursorWindow"
-
-#include
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-
-#include "CursorWindow.h"
-#include "sqlite3_exception.h"
-#include "android_util_Binder.h"
-
-namespace sqlcipher {
-
-static jfieldID gWindowField;
-static jfieldID gBufferField;
-static jfieldID gSizeCopiedField;
-
-#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField))
-#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window))
-#define SET_BUFFER(env, object, buf) (env->SetObjectField(object, gBufferField, buf))
-#define SET_SIZE_COPIED(env, object, size) (env->SetIntField(object, gSizeCopiedField, size))
-
-CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow)
-{
- return GET_WINDOW(env, javaWindow);
-}
-
-static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)
-{
- uint8_t * data;
- size_t size;
- CursorWindow * window;
-
- window = new CursorWindow(MAX_WINDOW_SIZE);
- if (!window) {
- jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
- return;
- }
-
- if (!window->initBuffer(localOnly)) {
- jniThrowException(env, "java/lang/IllegalStateException", "Couldn't init cursor window");
- delete window;
- return;
- }
-
-LOG_WINDOW("native_init_empty: window = %p", window);
- SET_WINDOW(env, object, window);
-}
-
-static void native_init_memory(JNIEnv * env, jobject object, jobject memObj)
-{
- android::sp memory = android::interface_cast(android::ibinderForJavaObject(env, memObj));
-
- if (memory == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder");
- return;
- }
-
- CursorWindow * window = new CursorWindow();
- if (!window) {
- jniThrowException(env, "java/lang/RuntimeException", "No memory for native window object");
- return;
- }
- if (!window->setMemory(memory)) {
- jniThrowException(env, "java/lang/RuntimeException", "No memory in memObj");
- delete window;
- return;
- }
-
-LOG_WINDOW("native_init_memory: numRows = %d, numColumns = %d, window = %p", window->getNumRows(), window->getNumColumns(), window);
- SET_WINDOW(env, object, window);
-}
-
-static jobject native_getBinder(JNIEnv * env, jobject object)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (window) {
- android::sp memory = window->getMemory();
- if (memory != NULL) {
- android::sp binder = memory->asBinder();
- return javaObjectForIBinder(env, binder);
- }
- }
- return NULL;
-}
-
-static void native_clear(JNIEnv * env, jobject object)
-{
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Clearing window %p", window);
- if (window == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "clear() called after close()");
- return;
- }
- window->clear();
-}
-
-static void native_close(JNIEnv * env, jobject object)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (window) {
-LOG_WINDOW("Closing window %p", window);
- delete window;
- SET_WINDOW(env, object, 0);
- }
-}
-
-static void throwExceptionWithRowCol(JNIEnv * env, jint row, jint column)
-{
- char buf[100];
- snprintf(buf, sizeof(buf), "get field slot from row %d col %d failed", row, column);
- jniThrowException(env, "java/lang/IllegalStateException", buf);
-}
-
-static void throwUnknowTypeException(JNIEnv * env, jint type)
-{
- char buf[80];
- snprintf(buf, sizeof(buf), "UNKNOWN type %d", type);
- jniThrowException(env, "java/lang/IllegalStateException", buf);
-}
-
-static jlong getLong_native(JNIEnv * env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return 0;
- }
-
- uint8_t type = field.type;
- if (type == FIELD_TYPE_INTEGER) {
- int64_t value;
- if (window->getLong(row, column, &value)) {
- return value;
- }
- return 0;
- } else if (type == FIELD_TYPE_STRING) {
- uint32_t size = field.data.buffer.size;
- if (size > 0) {
-#if WINDOW_STORAGE_UTF8
- return strtoll((char const *)window->offsetToPtr(field.data.buffer.offset), NULL, 0);
-#else
- String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
- char const * str = ascii.string();
- return strtoll(str, NULL, 0);
-#endif
- } else {
- return 0;
- }
- } else if (type == FIELD_TYPE_FLOAT) {
- double value;
- if (window->getDouble(row, column, &value)) {
- return value;
- }
- return 0;
- } else if (type == FIELD_TYPE_NULL) {
- return 0;
- } else if (type == FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to long");
- return 0;
- } else {
- throwUnknowTypeException(env, type);
- return 0;
- }
-}
-
-static jbyteArray getBlob_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- uint8_t type = field.type;
- if (type == FIELD_TYPE_BLOB || type == FIELD_TYPE_STRING) {
- jbyteArray byteArray = env->NewByteArray(field.data.buffer.size);
- LOG_ASSERT(byteArray, "Native could not create new byte[]");
- env->SetByteArrayRegion(byteArray, 0, field.data.buffer.size,
- (const jbyte*)window->offsetToPtr(field.data.buffer.offset));
- return byteArray;
- } else if (type == FIELD_TYPE_INTEGER) {
- throw_sqlite3_exception(env, "INTEGER data in getBlob_native ");
- } else if (type == FIELD_TYPE_FLOAT) {
- throw_sqlite3_exception(env, "FLOAT data in getBlob_native ");
- } else if (type == FIELD_TYPE_NULL) {
- // do nothing
- } else {
- throwUnknowTypeException(env, type);
- }
- return NULL;
-}
-
-static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
-}
-
-static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
-}
-
-static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- return field.type == FIELD_TYPE_INTEGER;
-}
-
-static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- return field.type == FIELD_TYPE_FLOAT;
-}
-
-static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- uint8_t type = field.type;
- if (type == FIELD_TYPE_STRING) {
- uint32_t size = field.data.buffer.size;
- if (size > 0) {
-#if WINDOW_STORAGE_UTF8
- // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
- android::String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
- return env->NewString((jchar const *)utf16.string(), utf16.size());
-#else
- return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2);
-#endif
- } else {
- return env->NewStringUTF("");
- }
- } else if (type == FIELD_TYPE_INTEGER) {
- int64_t value;
- if (window->getLong(row, column, &value)) {
- char buf[32];
- snprintf(buf, sizeof(buf), "%lld", value);
- return env->NewStringUTF(buf);
- }
- return NULL;
- } else if (type == FIELD_TYPE_FLOAT) {
- double value;
- if (window->getDouble(row, column, &value)) {
- char buf[32];
- snprintf(buf, sizeof(buf), "%g", value);
- return env->NewStringUTF(buf);
- }
- return NULL;
- } else if (type == FIELD_TYPE_NULL) {
- return NULL;
- } else if (type == FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to string");
- return NULL;
- } else {
- throwUnknowTypeException(env, type);
- return NULL;
- }
-}
-
-/**
- * Use this only to convert characters that are known to be within the
- * 0-127 range for direct conversion to UTF-16
- */
-static jint charToJchar(const char* src, jchar* dst, jint bufferSize)
-{
- int32_t len = strlen(src);
-
- if (bufferSize < len) {
- len = bufferSize;
- }
-
- for (int i = 0; i < len; i++) {
- *dst++ = (*src++ & 0x7F);
- }
- return len;
-}
-
-static jcharArray copyStringToBuffer_native(JNIEnv* env, jobject object, jint row,
- jint column, jint bufferSize, jobject buf)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
- return NULL;
- }
-
- jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
- if (buffer == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
- return NULL;
- }
- jchar* dst = env->GetCharArrayElements(buffer, NULL);
- uint8_t type = field.type;
- uint32_t sizeCopied = 0;
- jcharArray newArray = NULL;
- if (type == FIELD_TYPE_STRING) {
- uint32_t size = field.data.buffer.size;
- if (size > 0) {
-#if WINDOW_STORAGE_UTF8
- // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
- android::String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
- int32_t strSize = utf16.size();
- if (strSize > bufferSize || dst == NULL) {
- newArray = env->NewCharArray(strSize);
- env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
- } else {
- memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
- }
- sizeCopied = strSize;
-#else
- sizeCopied = size/2 + size % 2;
- if (size > bufferSize * 2 || dst == NULL) {
- newArray = env->NewCharArray(sizeCopied);
- memcpy(newArray, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
- } else {
- memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
- }
-#endif
- }
- } else if (type == FIELD_TYPE_INTEGER) {
- int64_t value;
- if (window->getLong(row, column, &value)) {
- char buf[32];
- int len;
- snprintf(buf, sizeof(buf), "%lld", value);
- jchar* dst = env->GetCharArrayElements(buffer, NULL);
- sizeCopied = charToJchar(buf, dst, bufferSize);
- }
- } else if (type == FIELD_TYPE_FLOAT) {
- double value;
- if (window->getDouble(row, column, &value)) {
- char tempbuf[32];
- snprintf(tempbuf, sizeof(tempbuf), "%g", value);
- jchar* dst = env->GetCharArrayElements(buffer, NULL);
- sizeCopied = charToJchar(tempbuf, dst, bufferSize);
- }
- } else if (type == FIELD_TYPE_NULL) {
- } else if (type == FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to string");
- } else {
- LOGE("Unknown field type %d", type);
- throw_sqlite3_exception(env, "UNKNOWN type in copyStringToBuffer_native()");
- }
- SET_SIZE_COPIED(env, buf, sizeCopied);
- env->ReleaseCharArrayElements(buffer, dst, JNI_OK);
- return newArray;
-}
-
-static jdouble getDouble_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- int32_t err;
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
-
- field_slot_t field;
- err = window->read_field_slot(row, column, &field);
- if (err != 0) {
- throwExceptionWithRowCol(env, row, column);
- return 0.0;
- }
-
- uint8_t type = field.type;
- if (type == FIELD_TYPE_FLOAT) {
- double value;
- if (window->getDouble(row, column, &value)) {
- return value;
- }
- return 0.0;
- } else if (type == FIELD_TYPE_STRING) {
- uint32_t size = field.data.buffer.size;
- if (size > 0) {
-#if WINDOW_STORAGE_UTF8
- return strtod((char const *)window->offsetToPtr(field.data.buffer.offset), NULL);
-#else
- String8 ascii((char16_t *) window->offsetToPtr(field.data.buffer.offset), size / 2);
- char const * str = ascii.string();
- return strtod(str, NULL);
-#endif
- } else {
- return 0.0;
- }
- } else if (type == FIELD_TYPE_INTEGER) {
- int64_t value;
- if (window->getLong(row, column, &value)) {
- return (double) value;
- }
- return 0.0;
- } else if (type == FIELD_TYPE_NULL) {
- return 0.0;
- } else if (type == FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to double");
- return 0.0;
- } else {
- throwUnknowTypeException(env, type);
- return 0.0;
- }
-}
-
-static jboolean isNull_native(JNIEnv* env, jobject object, jint row, jint column)
-{
- CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking for NULL at %d,%d from %p", row, column, window);
-
- bool isNull;
- if (window->getNull(row, column, &isNull)) {
- return isNull;
- }
-
- //TODO throw execption?
- return true;
-}
-
-static jint getNumRows(JNIEnv * env, jobject object)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- return window->getNumRows();
-}
-
-static jboolean setNumColumns(JNIEnv * env, jobject object, jint columnNum)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- return window->setNumColumns(columnNum);
-}
-
-static jboolean allocRow(JNIEnv * env, jobject object)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- return window->allocRow() != NULL;
-}
-
-static jboolean putBlob_native(JNIEnv * env, jobject object, jbyteArray value, jint row, jint col)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (!value) {
- LOG_WINDOW("How did a null value send to here");
- return false;
- }
- field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
- if (fieldSlot == NULL) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-
- jint len = env->GetArrayLength(value);
- int offset = window->alloc(len);
- if (!offset) {
- LOG_WINDOW("Failed allocating %u bytes", len);
- return false;
- }
- jbyte * bytes = env->GetByteArrayElements(value, NULL);
- window->copyIn(offset, (uint8_t const *)bytes, len);
-
- // This must be updated after the call to alloc(), since that
- // may move the field around in the window
- fieldSlot->type = FIELD_TYPE_BLOB;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = len;
- env->ReleaseByteArrayElements(value, bytes, JNI_ABORT);
- LOG_WINDOW("%d,%d is BLOB with %u bytes @ %d", row, col, len, offset);
- return true;
-}
-
-static jboolean putString_native(JNIEnv * env, jobject object, jstring value, jint row, jint col)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (!value) {
- LOG_WINDOW("How did a null value send to here");
- return false;
- }
- field_slot_t * fieldSlot = window->getFieldSlotWithCheck(row, col);
- if (fieldSlot == NULL) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-
-#if WINDOW_STORAGE_UTF8
- int len = env->GetStringUTFLength(value) + 1;
- char const * valStr = env->GetStringUTFChars(value, NULL);
-#else
- int len = env->GetStringLength(value);
- // GetStringLength return number of chars and one char takes 2 bytes
- len *= 2;
- const jchar* valStr = env->GetStringChars(value, NULL);
-#endif
- if (!valStr) {
- LOG_WINDOW("value can't be transfer to UTFChars");
- return false;
- }
-
- int offset = window->alloc(len);
- if (!offset) {
- LOG_WINDOW("Failed allocating %u bytes", len);
-#if WINDOW_STORAGE_UTF8
- env->ReleaseStringUTFChars(value, valStr);
-#else
- env->ReleaseStringChars(value, valStr);
-#endif
- return false;
- }
-
- window->copyIn(offset, (uint8_t const *)valStr, len);
-
- // This must be updated after the call to alloc(), since that
- // may move the field around in the window
- fieldSlot->type = FIELD_TYPE_STRING;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = len;
-
- LOG_WINDOW("%d,%d is TEXT with %u bytes @ %d", row, col, len, offset);
-#if WINDOW_STORAGE_UTF8
- env->ReleaseStringUTFChars(value, valStr);
-#else
- env->ReleaseStringChars(value, valStr);
-#endif
-
- return true;
-}
-
-static jboolean putLong_native(JNIEnv * env, jobject object, jlong value, jint row, jint col)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (!window->putLong(row, col, value)) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-
- LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, col, value);
-
- return true;
-}
-
-static jboolean putDouble_native(JNIEnv * env, jobject object, jdouble value, jint row, jint col)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (!window->putDouble(row, col, value)) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-
- LOG_WINDOW("%d,%d is FLOAT %lf", row, col, value);
-
- return true;
-}
-
-static jboolean putNull_native(JNIEnv * env, jobject object, jint row, jint col)
-{
- CursorWindow * window = GET_WINDOW(env, object);
- if (!window->putNull(row, col)) {
- LOG_WINDOW(" getFieldSlotWithCheck error ");
- return false;
- }
-
- LOG_WINDOW("%d,%d is NULL", row, col);
-
- return true;
-}
-
-// free the last row
-static void freeLastRow(JNIEnv * env, jobject object) {
- CursorWindow * window = GET_WINDOW(env, object);
- window->freeLastRow();
-}
-
-static JNINativeMethod sMethods[] =
-{
- /* name, signature, funcPtr */
- {"native_init", "(Z)V", (void *)native_init_empty},
- {"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
- {"native_getBinder", "()Landroid/os/IBinder;", (void *)native_getBinder},
- {"native_clear", "()V", (void *)native_clear},
- {"close_native", "()V", (void *)native_close},
- {"getLong_native", "(II)J", (void *)getLong_native},
- {"getBlob_native", "(II)[B", (void *)getBlob_native},
- {"isBlob_native", "(II)Z", (void *)isBlob_native},
- {"getString_native", "(II)Ljava/lang/String;", (void *)getString_native},
- {"copyStringToBuffer_native", "(IIILandroid/database/CharArrayBuffer;)[C", (void *)copyStringToBuffer_native},
- {"getDouble_native", "(II)D", (void *)getDouble_native},
- {"isNull_native", "(II)Z", (void *)isNull_native},
- {"getNumRows_native", "()I", (void *)getNumRows},
- {"setNumColumns_native", "(I)Z", (void *)setNumColumns},
- {"allocRow_native", "()Z", (void *)allocRow},
- {"putBlob_native", "([BII)Z", (void *)putBlob_native},
- {"putString_native", "(Ljava/lang/String;II)Z", (void *)putString_native},
- {"putLong_native", "(JII)Z", (void *)putLong_native},
- {"putDouble_native", "(DII)Z", (void *)putDouble_native},
- {"freeLastRow_native", "()V", (void *)freeLastRow},
- {"putNull_native", "(II)Z", (void *)putNull_native},
- {"isString_native", "(II)Z", (void *)isString_native},
- {"isFloat_native", "(II)Z", (void *)isFloat_native},
- {"isInteger_native", "(II)Z", (void *)isInteger_native},
-};
-
-int register_android_database_CursorWindow(JNIEnv * env)
-{
- jclass clazz;
-
- clazz = env->FindClass("net/sqlcipher/CursorWindow");
- if (clazz == NULL) {
- LOGE("Can't find net/sqlcipher/CursorWindow");
- return -1;
- }
-
- gWindowField = env->GetFieldID(clazz, "nWindow", "I");
-
- if (gWindowField == NULL) {
- LOGE("Error locating fields");
- return -1;
- }
-
- clazz = env->FindClass("android/database/CharArrayBuffer");
- if (clazz == NULL) {
- LOGE("Can't find android/database/CharArrayBuffer");
- return -1;
- }
-
- gBufferField = env->GetFieldID(clazz, "data", "[C");
-
- if (gBufferField == NULL) {
- LOGE("Error locating fields data in CharArrayBuffer");
- return -1;
- }
-
- gSizeCopiedField = env->GetFieldID(clazz, "sizeCopied", "I");
-
- if (gSizeCopiedField == NULL) {
- LOGE("Error locating fields sizeCopied in CharArrayBuffer");
- return -1;
- }
-
- return android::AndroidRuntime::registerNativeMethods(env, "net/sqlcipher/CursorWindow",
- sMethods, NELEM(sMethods));
-}
-
-} // namespace sqlcipher
diff --git a/jni/net_sqlcipher_database_SQLiteDatabase.cpp b/jni/net_sqlcipher_database_SQLiteDatabase.cpp
deleted file mode 100644
index 812388d7..00000000
--- a/jni/net_sqlcipher_database_SQLiteDatabase.cpp
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Copyright (C) 2006-2007 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Database"
-
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include