-namespace guardianproject {
+namespace sqlcipher {
/* throw a SQLiteException with a message appropriate for the error in handle */
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle);
@@ -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/info/guardianproject/database/AbstractCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractCursor.java
similarity index 90%
rename from src/info/guardianproject/database/AbstractCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractCursor.java
index bb1c627f..f3eaf8aa 100644
--- a/src/info/guardianproject/database/AbstractCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractCursor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -22,6 +22,10 @@
import android.content.ContentResolver;
import android.database.CharArrayBuffer;
+import android.database.ContentObservable;
+import android.database.DataSetObservable;
+import android.database.DataSetObserver;
+import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.util.Config;
@@ -32,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 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();
@@ -52,6 +58,8 @@ public abstract class AbstractCursor implements CrossProcessCursor {
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");
@@ -69,26 +77,26 @@ public CursorWindow getWindow() {
public int getColumnCount() {
return getColumnNames().length;
}
-
+
public void deactivate() {
deactivateInternal();
}
-
+
/**
* @hide
*/
public void deactivateInternal() {
if (mSelfObserver != null) {
- // mContentResolver.unregisterContentObserver(mSelfObserver);
+ mContentResolver.unregisterContentObserver(mSelfObserver);
mSelfObserverRegistered = false;
}
mDataSetObservable.notifyInvalidated();
}
-
+
public boolean requery() {
if (mSelfObserver != null && mSelfObserverRegistered == false) {
-
- // mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
+
+ mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
mSelfObserverRegistered = true;
}
mDataSetObservable.notifyChanged();
@@ -98,7 +106,7 @@ public boolean requery() {
public boolean isClosed() {
return mClosed;
}
-
+
public void close() {
mClosed = true;
mContentObservable.unregisterAll();
@@ -135,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);
@@ -147,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() {
@@ -194,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, 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();
- }
+ public void fillWindow(int position, android.database.CursorWindow window) {
+ DatabaseUtils.cursorFillWindow(this, position, window);
}
public final boolean move(int offset) {
@@ -392,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);
}
@@ -411,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
@@ -458,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
@@ -466,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) {
@@ -519,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) {
@@ -608,6 +593,7 @@ public void onChange(boolean selfChange) {
cursor.onChange(false);
}
}
+
}
/**
@@ -625,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/info/guardianproject/database/AbstractWindowedCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractWindowedCursor.java
similarity index 97%
rename from src/info/guardianproject/database/AbstractWindowedCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractWindowedCursor.java
index 5b654434..deb25f33 100644
--- a/src/info/guardianproject/database/AbstractWindowedCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/AbstractWindowedCursor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
import android.database.CharArrayBuffer;
@@ -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/info/guardianproject/database/BulkCursorNative.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.java
similarity index 97%
rename from src/info/guardianproject/database/BulkCursorNative.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.java
index 250495a9..868dde6a 100644
--- a/src/info/guardianproject/database/BulkCursorNative.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
import android.os.Binder;
import android.os.RemoteException;
@@ -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/info/guardianproject/database/BulkCursorToCursorAdaptor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.java
similarity index 98%
rename from src/info/guardianproject/database/BulkCursorToCursorAdaptor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.java
index ba104e0b..932507e3 100644
--- a/src/info/guardianproject/database/BulkCursorToCursorAdaptor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.java
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
+
+import java.util.Map;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.DataSetObserver;
-import android.os.RemoteException;
+
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.Log;
-import java.util.Map;
-
/**
* Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local
* process.
@@ -89,7 +90,7 @@ public synchronized IContentObserver getObserver() {
if (mObserverBridge == null) {
mObserverBridge = new SelfContentObserver(this);
}
- return mObserverBridge.getContentObserver();
+ return null;//mObserverBridge.getContentObserver(); //TODO nf fix this
}
@Override
@@ -138,7 +139,7 @@ public void deactivate() {
}
mWindow = null;
}
-
+
@Override
public void close() {
super.close();
@@ -147,7 +148,7 @@ public void close() {
} catch (RemoteException ex) {
Log.w(TAG, "Remote process exception when closing");
}
- mWindow = null;
+ mWindow = null;
}
@Override
@@ -188,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) {
@@ -245,7 +246,7 @@ public boolean commitUpdates(Map extends Long,
try {
boolean result = mBulkCursor.updateRows(mUpdatedRows);
-
+
if (result == true) {
mUpdatedRows.clear();
@@ -287,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/info/guardianproject/database/CursorIndexOutOfBoundsException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorIndexOutOfBoundsException.java
similarity index 96%
rename from src/info/guardianproject/database/CursorIndexOutOfBoundsException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/CursorIndexOutOfBoundsException.java
index 7f2b35d3..0788b377 100644
--- a/src/info/guardianproject/database/CursorIndexOutOfBoundsException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorIndexOutOfBoundsException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
/**
* An exception indicating that a cursor is out of bounds.
diff --git a/src/info/guardianproject/database/CursorWindow.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java
similarity index 70%
rename from src/info/guardianproject/database/CursorWindow.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java
index b9d3d032..b130dc26 100644
--- a/src/info/guardianproject/database/CursorWindow.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindow.java
@@ -14,13 +14,25 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
-import info.guardianproject.database.sqlcipher.SQLiteClosable;
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.
@@ -28,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
@@ -40,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());
}
/**
@@ -60,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() {
@@ -75,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
*/
@@ -90,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
@@ -105,9 +145,9 @@ public boolean allocRow(){
releaseReference();
}
}
-
- private native boolean allocRow_native();
-
+
+ private native boolean allocRow_native();
+
/**
* Free the last row
*/
@@ -119,7 +159,7 @@ public void freeLastRow(){
releaseReference();
}
}
-
+
private native void freeLastRow_native();
/**
@@ -137,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
@@ -155,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
@@ -173,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
@@ -192,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
@@ -209,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}
@@ -228,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.
*
@@ -247,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();
@@ -271,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();
@@ -287,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();
@@ -303,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();
@@ -318,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
*/
@@ -364,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
*/
@@ -384,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
*/
@@ -403,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
*/
@@ -425,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
*/
@@ -438,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
*/
@@ -454,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
@@ -464,7 +581,7 @@ public float getFloat(int row, int col) {
public void clear() {
acquireReference();
try {
- mStartPos = 0;
+ mStartPos = 0;
native_clear();
} finally {
releaseReference();
@@ -480,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) {
@@ -514,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();
@@ -527,13 +647,16 @@ 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);
@Override
protected void onAllReferencesReleased() {
- close_native();
+ close_native();
+
+ 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/info/guardianproject/database/sqlcipher/SQLiteException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.java
similarity index 57%
rename from src/info/guardianproject/database/sqlcipher/SQLiteException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.java
index beb291df..aae6be0f 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.java
@@ -14,17 +14,26 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
-
-import info.guardianproject.database.*;
+package net.sqlcipher;
/**
- * A SQLite 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 SQLiteException extends SQLException {
- public SQLiteException() {}
+public class CursorWrapper extends android.database.CursorWrapper implements Cursor {
+
+ private final Cursor mCursor;
+
+ public CursorWrapper(Cursor cursor) {
+ super(cursor);
+ mCursor = cursor;
+ }
- public SQLiteException(String error) {
- super(error);
+ 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/info/guardianproject/database/sqlcipher/SQLiteFullException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseErrorHandler.java
similarity index 51%
rename from src/info/guardianproject/database/sqlcipher/SQLiteFullException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseErrorHandler.java
index 67964049..58096f1d 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteFullException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseErrorHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 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,15 +14,20 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher;
+
+import net.sqlcipher.database.SQLiteDatabase;
/**
- * An exception that indicates that the SQLite database is full.
+ * An interface to let the apps define the actions to take when the following errors are detected
+ * database corruption
*/
-public class SQLiteFullException extends SQLiteException {
- public SQLiteFullException() {}
+public interface DatabaseErrorHandler {
- public SQLiteFullException(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/info/guardianproject/database/DatabaseUtils.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseUtils.java
similarity index 87%
rename from src/info/guardianproject/database/DatabaseUtils.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseUtils.java
index 075f1ea1..a89bebab 100644
--- a/src/info/guardianproject/database/DatabaseUtils.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/DatabaseUtils.java
@@ -14,19 +14,11 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
-import android.database.Cursor;
-
-import info.guardianproject.database.sqlcipher.SQLiteAbortException;
-import info.guardianproject.database.sqlcipher.SQLiteConstraintException;
-import info.guardianproject.database.sqlcipher.SQLiteDatabase;
-import info.guardianproject.database.sqlcipher.SQLiteDatabaseCorruptException;
-import info.guardianproject.database.sqlcipher.SQLiteDiskIOException;
-import info.guardianproject.database.sqlcipher.SQLiteException;
-import info.guardianproject.database.sqlcipher.SQLiteFullException;
-import info.guardianproject.database.sqlcipher.SQLiteProgram;
-import info.guardianproject.database.sqlcipher.SQLiteStatement;
+import net.sqlcipher.database.SQLiteDatabase;
+import net.sqlcipher.database.SQLiteProgram;
+import net.sqlcipher.database.SQLiteStatement;
import java.io.FileNotFoundException;
import java.io.PrintStream;
@@ -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/info/guardianproject/database/IBulkCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/IBulkCursor.java
similarity index 98%
rename from src/info/guardianproject/database/IBulkCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/IBulkCursor.java
index 8b3ef2fb..a0a86164 100644
--- a/src/info/guardianproject/database/IBulkCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/IBulkCursor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
import android.os.RemoteException;
import android.os.IBinder;
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/info/guardianproject/database/MatrixCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.java
similarity index 87%
rename from src/info/guardianproject/database/MatrixCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.java
index c8a42593..6ca0798c 100644
--- a/src/info/guardianproject/database/MatrixCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
import java.util.ArrayList;
@@ -274,68 +274,15 @@ 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;
}
- @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
-
- }
-
- @Override
- public void copyStringToBuffer(int columnIndex,
- android.database.CharArrayBuffer buffer) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void registerContentObserver(
- android.database.ContentObserver observer) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void registerDataSetObserver(
- android.database.DataSetObserver observer) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void unregisterContentObserver(
- android.database.ContentObserver observer) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void unregisterDataSetObserver(
- android.database.DataSetObserver observer) {
- // TODO Auto-generated method stub
-
- }
}
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/info/guardianproject/database/StaleDataException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/StaleDataException.java
similarity index 96%
rename from src/info/guardianproject/database/StaleDataException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/StaleDataException.java
index 9c891e3e..17209bcc 100644
--- a/src/info/guardianproject/database/StaleDataException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/StaleDataException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database;
+package net.sqlcipher;
/**
* This exception is thrown when a Cursor contains stale data and must be
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/info/guardianproject/database/sqlcipher/DatabaseObjectNotClosedException.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/DatabaseObjectNotClosedException.java
similarity index 95%
rename from src/info/guardianproject/database/sqlcipher/DatabaseObjectNotClosedException.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/DatabaseObjectNotClosedException.java
index b983e525..89f2adf7 100644
--- a/src/info/guardianproject/database/sqlcipher/DatabaseObjectNotClosedException.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/DatabaseObjectNotClosedException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
/**
* An exception that indicates that garbage-collector is finalizing a database object
diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteClosable.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteClosable.java
similarity index 96%
rename from src/info/guardianproject/database/sqlcipher/SQLiteClosable.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteClosable.java
index 7fb6661f..93c50db2 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteClosable.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteClosable.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
-import info.guardianproject.database.*;
+import net.sqlcipher.*;
/**
* An object created from a SQLiteDatabase that can be closed.
diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.java
similarity index 87%
rename from src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.java
index 710984f6..52787c14 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
import android.util.Log;
@@ -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/info/guardianproject/database/sqlcipher/SQLiteContentHelper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.java
similarity index 73%
rename from src/info/guardianproject/database/sqlcipher/SQLiteContentHelper.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.java
index de770c16..3300b610 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteContentHelper.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.java
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
-import info.guardianproject.database.*;
+package net.sqlcipher.database;
+import net.sqlcipher.*;
import android.content.res.AssetFileDescriptor;
import android.os.MemoryFile;
@@ -45,18 +45,29 @@ public class SQLiteContentHelper {
* value of column 0 is NULL, or if there is an error creating the
* asset file descriptor.
*/
- public static MemoryFile getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
- String[] selectionArgs) throws FileNotFoundException {
- try {
- MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
- if (file == null) {
- throw new FileNotFoundException("No results.");
- }
- return file;
- } catch (IOException ex) {
- throw new FileNotFoundException(ex.toString());
- }
- }
+ public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
+ String[] selectionArgs) throws FileNotFoundException {
+ android.os.ParcelFileDescriptor fd = null;
+
+ try {
+ MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
+ if (file == null) {
+ throw new FileNotFoundException("No results.");
+ }
+ Class c = file.getClass();
+ try {
+ java.lang.reflect.Method m = c.getDeclaredMethod("getParcelFileDescriptor");
+ m.setAccessible(true);
+ 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) {
+ throw new FileNotFoundException(ex.toString());
+ }
+ }
/**
* Runs an SQLite query and returns a MemoryFile for the
@@ -84,6 +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/info/guardianproject/database/sqlcipher/SQLiteCursor.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursor.java
similarity index 77%
rename from src/info/guardianproject/database/sqlcipher/SQLiteCursor.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursor.java
index f9da9743..a216d986 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteCursor.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursor.java
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
-import info.guardianproject.database.AbstractWindowedCursor;
-import info.guardianproject.database.CursorWindow;
-import info.guardianproject.database.DataSetObserver;
-import info.guardianproject.database.SQLException;
+import net.sqlcipher.AbstractWindowedCursor;
+import net.sqlcipher.BuildConfig;
+import net.sqlcipher.CursorWindow;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import android.database.CharArrayBuffer;
-import android.database.ContentObserver;
-import android.database.CrossProcessCursor;
+import android.database.DataSetObserver;
+import android.database.SQLException;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
@@ -43,7 +43,7 @@
* SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple
* threads should perform its own synchronization when using the SQLiteCursor.
*/
-public class SQLiteCursor extends AbstractWindowedCursor implements CrossProcessCursor {
+public class SQLiteCursor extends AbstractWindowedCursor {
static final String TAG = "Cursor";
static final int NO_COUNT = -1;
@@ -65,14 +65,18 @@ public class SQLiteCursor extends AbstractWindowedCursor implements CrossProcess
/** 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;
@@ -80,13 +84,17 @@ public class SQLiteCursor extends AbstractWindowedCursor implements CrossProcess
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
@@ -96,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
*/
@@ -125,7 +133,7 @@ private void sendMessage() {
} else {
mPendingData = true;
}
-
+
}
public void run() {
// use cached mWindow, to avoid get null mWindow
@@ -133,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();
@@ -145,7 +156,7 @@ public void run() {
if (count == NO_COUNT){
mCount += mMaxRead;
sendMessage();
- } else {
+ } else {
mCount = count;
sendMessage();
break;
@@ -160,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;
@@ -195,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
@@ -234,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;
@@ -275,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 */);
@@ -288,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
@@ -315,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);
@@ -337,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;
@@ -404,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;
}
/*
@@ -489,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
@@ -546,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();
@@ -590,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 {
@@ -607,52 +650,51 @@ protected void finalize() {
}
}
- @Override
- public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
-
-
- }
-
- @Override
- public void registerContentObserver(ContentObserver observer) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void registerDataSetObserver(
- android.database.DataSetObserver observer) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void unregisterContentObserver(ContentObserver observer) {
- // TODO Auto-generated method stub
-
- }
- @Override
- public void unregisterDataSetObserver(
- android.database.DataSetObserver observer) {
- // TODO Auto-generated method stub
-
- }
@Override
- public void fillWindow(int startPos, android.database.CursorWindow window) {
-
-
- window.setStartPosition(startPos);
- mCount = mQuery.fillWindow((info.guardianproject.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();
- }
-
+ 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/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursorDriver.java
similarity index 84%
rename from src/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursorDriver.java
index c2a8fb65..1ea66aba 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCursorDriver.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
-import info.guardianproject.database.sqlcipher.SQLiteDatabase.CursorFactory;
-import info.guardianproject.database.*;
+import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
+import net.sqlcipher.*;
/**
* A driver for SQLiteCursors that is used to create them and gets notified
@@ -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,10 +40,10 @@ public interface SQLiteCursorDriver {
/**
* Called by a SQLiteCursor when it is requeryed.
- *
+ *
* @return The new count value.
*/
- void cursorRequeried(Cursor cursor);
+ void cursorRequeried(android.database.Cursor cursor);
/**
* Called by a SQLiteCursor when it it closed to destroy this object as well.
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/info/guardianproject/database/sqlcipher/SQLiteDebug.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDebug.java
similarity index 99%
rename from src/info/guardianproject/database/sqlcipher/SQLiteDebug.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDebug.java
index 5a8288dc..d90f0174 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteDebug.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDebug.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
import java.util.ArrayList;
diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDirectCursorDriver.java
similarity index 70%
rename from src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDirectCursorDriver.java
index a7af18cf..36ae59a4 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDirectCursorDriver.java
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
-import info.guardianproject.database.*;
-import info.guardianproject.database.sqlcipher.SQLiteDatabase.CursorFactory;
+import net.sqlcipher.Cursor;
+import net.sqlcipher.database.SQLiteDatabase.CursorFactory;
/**
* 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,6 +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);
}
@@ -75,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/src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java
similarity index 54%
rename from src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java
index b883358d..e3a24f43 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteOpenHelper.java
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
import java.io.File;
import android.content.Context;
-import info.guardianproject.database.sqlcipher.SQLiteDatabase.CursorFactory;
+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;
/**
@@ -38,10 +42,30 @@ public abstract class SQLiteOpenHelper {
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
@@ -52,14 +76,43 @@ public abstract class SQLiteOpenHelper {
* @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) {
+ 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;
}
/**
@@ -74,7 +127,16 @@ public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int
* @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
}
@@ -95,24 +157,19 @@ public synchronized SQLiteDatabase getWritableDatabase(String password) {
try {
mIsInitializing = true;
if (mName == null) {
- db = SQLiteDatabase.create(null, password);
-
+ db = SQLiteDatabase.create(null, "");
} else {
String path = mContext.getDatabasePath(mName).getPath();
-
File dbPathFile = new File (path);
- if (!dbPathFile.exists())
+ if (!dbPathFile.exists()) {
dbPathFile.getParentFile().mkdirs();
-
- db = SQLiteDatabase.openOrCreateDatabase(path, password, mFactory);
-
- // db = SQLiteDatabase.openDatabase(path,mFactory , SQLiteDatabase.OPEN_READWRITE);
-
- //db = mContext.openOrCreateDatabase(mName, 0, mFactory);
-
+ }
+ db = SQLiteDatabase.openOrCreateDatabase(path, password, mFactory, mHook, mErrorHandler);
}
-
-
+ if(mDeferSetWriteAheadLoggingEnabled) {
+ mEnableWriteAheadLogging = db.enableWriteAheadLogging();
+ }
+ onConfigure(db);
int version = db.getVersion();
if (version != mNewVersion) {
db.beginTransaction();
@@ -120,7 +177,11 @@ public synchronized SQLiteDatabase getWritableDatabase(String password) {
if (version == 0) {
onCreate(db);
} else {
+ if(version > mNewVersion) {
+ onDowngrade(db, version, mNewVersion);
+ } else {
onUpgrade(db, version, mNewVersion);
+ }
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
@@ -161,6 +222,14 @@ public synchronized SQLiteDatabase getWritableDatabase(String password) {
* 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
}
@@ -180,7 +249,19 @@ public synchronized SQLiteDatabase getReadableDatabase(String password) {
try {
mIsInitializing = true;
String path = mContext.getDatabasePath(mName).getPath();
- db = SQLiteDatabase.openDatabase(path, password, mFactory, SQLiteDatabase.OPEN_READONLY);
+ 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);
@@ -208,6 +289,81 @@ public synchronized void close() {
}
}
+ /**
+ * 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.
diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteProgram.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.java
similarity index 88%
rename from src/info/guardianproject/database/sqlcipher/SQLiteProgram.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.java
index 99fcce57..e43826d9 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteProgram.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+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/info/guardianproject/database/sqlcipher/SQLiteQuery.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQuery.java
similarity index 66%
rename from src/info/guardianproject/database/sqlcipher/SQLiteQuery.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQuery.java
index f29f7ee7..c87bd590 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteQuery.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQuery.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
-import info.guardianproject.database.*;
+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/info/guardianproject/database/sqlcipher/SQLiteQueryBuilder.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryBuilder.java
similarity index 98%
rename from src/info/guardianproject/database/sqlcipher/SQLiteQueryBuilder.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryBuilder.java
index 56307909..d47f5593 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteQueryBuilder.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryBuilder.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
-import info.guardianproject.database.*;
+package net.sqlcipher.database;
+
+import net.sqlcipher.*;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -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/info/guardianproject/database/sqlcipher/SQLiteStatement.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.java
similarity index 86%
rename from src/info/guardianproject/database/sqlcipher/SQLiteStatement.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.java
index f8f189e0..84b7b4c2 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteStatement.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+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/info/guardianproject/database/sqlcipher/SQLiteTransactionListener.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteTransactionListener.java
similarity index 95%
rename from src/info/guardianproject/database/sqlcipher/SQLiteTransactionListener.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteTransactionListener.java
index 22e6ddfc..69680ee3 100644
--- a/src/info/guardianproject/database/sqlcipher/SQLiteTransactionListener.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteTransactionListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
/**
* A listener for transaction events.
diff --git a/src/info/guardianproject/database/sqlcipher/SqliteWrapper.java b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java
similarity index 94%
rename from src/info/guardianproject/database/sqlcipher/SqliteWrapper.java
rename to android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java
index 424128c5..1d15f99e 100644
--- a/src/info/guardianproject/database/sqlcipher/SqliteWrapper.java
+++ b/android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java
@@ -15,12 +15,15 @@
* limitations under the License.
*/
-package info.guardianproject.database.sqlcipher;
+package net.sqlcipher.database;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
-import info.guardianproject.database.*;
+
+import net.sqlcipher.*;
+
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.util.Log;
import android.widget.Toast;
@@ -63,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/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/default.properties b/default.properties
deleted file mode 100644
index 1c386fdb..00000000
--- a/default.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "build.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-6
diff --git a/external/Android.mk b/external/Android.mk
deleted file mode 100644
index 3cbf5d02..00000000
--- a/external/Android.mk
+++ /dev/null
@@ -1,92 +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
-
-LOCAL_PATH := $(call my-dir)
-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
- 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
- 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/
-## install -p -m644 android-2.2/*.so ../obj/local/armeabi/
-
-project_ldflags:= -Llibs/armeabi/ -Landroid-2.3/
-
-#------------------------------------------------------------------------------#
-# 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
-
-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 := openssl/include sqlcipher
-LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDLIBS += -lcrypto
-LOCAL_MODULE := libsqlcipher
-LOCAL_SRC_FILES := $(sqlcipher_files)
-
-include $(BUILD_SHARED_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 \
-# 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
-LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlite_cflags)
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/sqlcipher \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/icu4c/i18n \
- $(LOCAL_PATH)/icu4c/common \
-# $(LOCAL_PATH)/platform-system-core/include \
-# $(LOCAL_PATH)/platform-frameworks-base/include
-#LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDFLAGS += -L$(LOCAL_PATH)/android-2.3/ -L$(LOCAL_PATH)/libs/armeabi/
-LOCAL_LDLIBS := -lsqlcipher -llog -licuuc -licui18n -lutils -lcutils
-LOCAL_MODULE := libsqlcipher_android
-LOCAL_SRC_FILES := $(libsqlite3_android_local_src_files)
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/external/Android.mk-2.2 b/external/Android.mk-2.2
deleted file mode 100644
index d5b962ec..00000000
--- a/external/Android.mk-2.2
+++ /dev/null
@@ -1,92 +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
-
-LOCAL_PATH := $(call my-dir)
-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
- 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
- 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/
-## install -p -m644 android-2.2/*.so ../obj/local/armeabi/
-
-project_ldflags:= -Llibs/armeabi/ -Landroid-2.2/
-
-#------------------------------------------------------------------------------#
-# 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
-
-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 := openssl/include sqlcipher
-LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDLIBS += -lcrypto
-LOCAL_MODULE := libsqlcipher
-LOCAL_SRC_FILES := $(sqlcipher_files)
-
-include $(BUILD_SHARED_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 \
-# 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
-LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlite_cflags)
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/sqlcipher \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/icu4c/i18n \
- $(LOCAL_PATH)/icu4c/common \
-# $(LOCAL_PATH)/platform-system-core/include \
-# $(LOCAL_PATH)/platform-frameworks-base/include
-#LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDFLAGS += -L$(LOCAL_PATH)/android-2.2/ -L$(LOCAL_PATH)/libs/armeabi/
-LOCAL_LDLIBS := -lsqlcipher -llog -licuuc -licui18n -lutils -lcutils
-LOCAL_MODULE := libsqlcipher_android
-LOCAL_SRC_FILES := $(libsqlite3_android_local_src_files)
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/external/Android.mk-2.3 b/external/Android.mk-2.3
deleted file mode 100644
index 3cbf5d02..00000000
--- a/external/Android.mk-2.3
+++ /dev/null
@@ -1,92 +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
-
-LOCAL_PATH := $(call my-dir)
-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
- 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
- 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/
-## install -p -m644 android-2.2/*.so ../obj/local/armeabi/
-
-project_ldflags:= -Llibs/armeabi/ -Landroid-2.3/
-
-#------------------------------------------------------------------------------#
-# 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
-
-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 := openssl/include sqlcipher
-LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDLIBS += -lcrypto
-LOCAL_MODULE := libsqlcipher
-LOCAL_SRC_FILES := $(sqlcipher_files)
-
-include $(BUILD_SHARED_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 \
-# 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
-LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlite_cflags)
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/sqlcipher \
- $(LOCAL_PATH)/include \
- $(LOCAL_PATH)/icu4c/i18n \
- $(LOCAL_PATH)/icu4c/common \
-# $(LOCAL_PATH)/platform-system-core/include \
-# $(LOCAL_PATH)/platform-frameworks-base/include
-#LOCAL_LDFLAGS += $(project_ldflags)
-LOCAL_LDFLAGS += -L$(LOCAL_PATH)/android-2.3/ -L$(LOCAL_PATH)/libs/armeabi/
-LOCAL_LDLIBS := -lsqlcipher -llog -licuuc -licui18n -lutils -lcutils
-LOCAL_MODULE := libsqlcipher_android
-LOCAL_SRC_FILES := $(libsqlite3_android_local_src_files)
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/external/android-2.2/libcrypto.so b/external/android-2.2/libcrypto.so
deleted file mode 100644
index 0686d909..00000000
Binary files a/external/android-2.2/libcrypto.so and /dev/null differ
diff --git a/external/android-2.2/libcutils.so b/external/android-2.2/libcutils.so
deleted file mode 100644
index 842fc307..00000000
Binary files a/external/android-2.2/libcutils.so and /dev/null differ
diff --git a/external/android-2.2/libicui18n.so b/external/android-2.2/libicui18n.so
deleted file mode 100644
index 0b9875d9..00000000
Binary files a/external/android-2.2/libicui18n.so and /dev/null differ
diff --git a/external/android-2.2/libicuuc.so b/external/android-2.2/libicuuc.so
deleted file mode 100644
index 5de770f8..00000000
Binary files a/external/android-2.2/libicuuc.so and /dev/null differ
diff --git a/external/android-2.2/liblog.so b/external/android-2.2/liblog.so
deleted file mode 100644
index b0674f8f..00000000
Binary files a/external/android-2.2/liblog.so and /dev/null differ
diff --git a/external/android-2.2/libnativehelper.so b/external/android-2.2/libnativehelper.so
deleted file mode 100644
index 5e84fffc..00000000
Binary files a/external/android-2.2/libnativehelper.so and /dev/null differ
diff --git a/external/android-2.2/libssl.so b/external/android-2.2/libssl.so
deleted file mode 100644
index 59398d0e..00000000
Binary files a/external/android-2.2/libssl.so and /dev/null differ
diff --git a/external/android-2.2/libstlport_shared.so b/external/android-2.2/libstlport_shared.so
deleted file mode 100755
index 165ca68f..00000000
Binary files a/external/android-2.2/libstlport_shared.so and /dev/null differ
diff --git a/external/android-2.2/libutils.so b/external/android-2.2/libutils.so
deleted file mode 100644
index 8208fae1..00000000
Binary files a/external/android-2.2/libutils.so and /dev/null differ
diff --git a/external/android-2.3/libcrypto.so b/external/android-2.3/libcrypto.so
deleted file mode 100644
index 3fc3849b..00000000
Binary files a/external/android-2.3/libcrypto.so and /dev/null differ
diff --git a/external/android-2.3/libcutils.so b/external/android-2.3/libcutils.so
deleted file mode 100644
index d6019f9c..00000000
Binary files a/external/android-2.3/libcutils.so and /dev/null differ
diff --git a/external/android-2.3/libicui18n.so b/external/android-2.3/libicui18n.so
deleted file mode 100644
index 8c38c575..00000000
Binary files a/external/android-2.3/libicui18n.so and /dev/null differ
diff --git a/external/android-2.3/libicuuc.so b/external/android-2.3/libicuuc.so
deleted file mode 100644
index b26575ce..00000000
Binary files a/external/android-2.3/libicuuc.so and /dev/null differ
diff --git a/external/android-2.3/liblog.so b/external/android-2.3/liblog.so
deleted file mode 100644
index 01f7a8d2..00000000
Binary files a/external/android-2.3/liblog.so and /dev/null differ
diff --git a/external/android-2.3/libnativehelper.so b/external/android-2.3/libnativehelper.so
deleted file mode 100644
index 3d300ba3..00000000
Binary files a/external/android-2.3/libnativehelper.so and /dev/null differ
diff --git a/external/android-2.3/libssl.so b/external/android-2.3/libssl.so
deleted file mode 100644
index 5c000c6e..00000000
Binary files a/external/android-2.3/libssl.so and /dev/null differ
diff --git a/external/android-2.3/libstlport_shared.so b/external/android-2.3/libstlport_shared.so
deleted file mode 100755
index 165ca68f..00000000
Binary files a/external/android-2.3/libstlport_shared.so and /dev/null differ
diff --git a/external/android-2.3/libutils.so b/external/android-2.3/libutils.so
deleted file mode 100644
index 6b5f517f..00000000
Binary files a/external/android-2.3/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 fa7b84a2..00000000
--- a/external/icu4c
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit fa7b84a26dd6c44cb0d219ef7b2f7e258219b616
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 93552de8..00000000
--- a/external/platform-frameworks-base
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 93552de8e305027fb003401e347b9493c64c981f
diff --git a/external/platform-system-core b/external/platform-system-core
deleted file mode 160000
index 034117e4..00000000
--- a/external/platform-system-core
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 034117e47f2601d46563461a0bfe3cc22f89a0f0
diff --git a/external/sqlcipher b/external/sqlcipher
deleted file mode 160000
index 5e4b18d1..00000000
--- a/external/sqlcipher
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5e4b18d1d9df26eae286eed6f89770dd7c9d748c
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 7d4333db..00000000
--- a/jni/Android.mk
+++ /dev/null
@@ -1,71 +0,0 @@
-uOCAL_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:= \
- info_guardianproject_database_sqlcipher_SQLiteCompiledSql.cpp \
- info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp \
- info_guardianproject_database_sqlcipher_SQLiteProgram.cpp \
- info_guardianproject_database_sqlcipher_SQLiteQuery.cpp \
- info_guardianproject_database_sqlcipher_SQLiteStatement.cpp \
- info_guardianproject_database_CursorWindow.cpp \
- CursorWindow.cpp
-# info_guardianproject_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-2.2/ -L../external/libs/armeabi/
-LOCAL_LDFLAGS += -L/home/n8fr8/android/mydroid/out/target/product/generic/obj/SHARED_LIBRARIES/libutils_intermediates/LINKED/ -L/home/n8fr8/android/mydroid/out/target/product/generic/obj/SHARED_LIBRARIES/libbinder_intermediates/LINKED/ -L/home/n8fr8/android/mydroid/out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/LINKED/
-
-# 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_LDLIBS += -lsqlcipher -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/include/utils/String16.h b/jni/include/utils/String16.h
deleted file mode 100644
index 07a0c118..00000000
--- a/jni/include/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/jni/info_guardianproject_database_CursorWindow.cpp b/jni/info_guardianproject_database_CursorWindow.cpp
deleted file mode 100644
index d2498033..00000000
--- a/jni/info_guardianproject_database_CursorWindow.cpp
+++ /dev/null
@@ -1,725 +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
-#include
-
-#include "CursorWindow.h"
-#include "sqlite3_exception.h"
-#include "android_util_Binder.h"
-
-namespace guardianproject {
-
-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("android/database/CursorWindow");
- if (clazz == NULL) {
- LOGE("Can't find android/database/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, "info/guardianproject/database/CursorWindow",
- sMethods, NELEM(sMethods));
-}
-
-} // namespace guardianproject
diff --git a/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp b/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp
deleted file mode 100644
index bffdb749..00000000
--- a/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp
+++ /dev/null
@@ -1,609 +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
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "sqlite3_exception.h"
-#include "sqlcipher_loading.h"
-
-#define UTF16_STORAGE 0
-#define INVALID_VERSION -1
-#define SQLITE_SOFT_HEAP_LIMIT (4 * 1024 * 1024)
-#define ANDROID_TABLE "android_metadata"
-/* uncomment the next line to force-enable logging of all statements */
-// #define DB_LOG_STATEMENTS
-
-
-
-namespace guardianproject {
-
-
-enum {
- OPEN_READWRITE = 0x00000000,
- OPEN_READONLY = 0x00000001,
- OPEN_READ_MASK = 0x00000001,
- NO_LOCALIZED_COLLATORS = 0x00000010,
- CREATE_IF_NECESSARY = 0x10000000
-};
-
-static jfieldID offset_db_handle;
-
-static char *createStr(const char *path) {
- int len = strlen(path);
- char *str = (char *)malloc(len + 1);
- strncpy(str, path, len);
- str[len] = NULL;
- return str;
-}
-
-static void sqlLogger(void *databaseName, int iErrCode, const char *zMsg) {
- // skip printing this message if it is due to certain types of errors
- if (iErrCode == SQLITE_CONSTRAINT) return;
- LOGI("sqlite returned: error code = %d, msg = %s\n", iErrCode, zMsg);
-}
-
-// register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
-static void registerLoggingFunc(const char *path) {
- static bool loggingFuncSet = false;
- if (loggingFuncSet) {
- return;
- }
-
- LOGV("Registering sqlite logging func \n");
- int err = sqlite3_config(SQLITE_CONFIG_LOG, &sqlLogger, (void *)createStr(path));
- if (err != SQLITE_OK) {
- LOGE("sqlite_config failed error_code = %d. THIS SHOULD NEVER occur.\n", err);
- return;
- }
- loggingFuncSet = true;
-}
-
-
-
-
-
-/* public native void dbopen(String path, int flags, String locale); */
-void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags)
-{
- int err;
- sqlite3 * handle = NULL;
- sqlite3_stmt * statement = NULL;
- char const * path8 = env->GetStringUTFChars(pathString, NULL);
- int sqliteFlags;
-
- // register the logging func on sqlite. needs to be done BEFORE any sqlite3 func is called.
- registerLoggingFunc(path8);
-
- // convert our flags into the sqlite flags
- if (flags & CREATE_IF_NECESSARY) {
- sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
- } else if (flags & OPEN_READONLY) {
- sqliteFlags = SQLITE_OPEN_READONLY;
- } else {
- sqliteFlags = SQLITE_OPEN_READWRITE;
- }
-
- err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
- if (err != SQLITE_OK) {
- LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
- // The soft heap limit prevents the page cache allocations from growing
- // beyond the given limit, no matter what the max page cache sizes are
- // set to. The limit does not, as of 3.5.0, affect any other allocations.
- sqlite3_soft_heap_limit(SQLITE_SOFT_HEAP_LIMIT);
-
- // Set the default busy handler to retry for 1000ms and then return SQLITE_BUSY
- err = sqlite3_busy_timeout(handle, 1000 /* ms */);
- if (err != SQLITE_OK) {
- LOGE("sqlite3_busy_timeout(handle, 1000) failed for \"%s\"\n", path8);
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
-#ifdef DB_INTEGRITY_CHECK
- static const char* integritySql = "pragma integrity_check(1);";
- err = sqlite3_prepare_v2(handle, integritySql, -1, &statement, NULL);
- if (err != SQLITE_OK) {
- LOGE("sqlite_prepare_v2(handle, \"%s\") failed for \"%s\"\n", integritySql, path8);
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
- // first is OK or error message
- err = sqlite3_step(statement);
- if (err != SQLITE_ROW) {
- LOGE("integrity check failed for \"%s\"\n", integritySql, path8);
- throw_sqlite3_exception(env, handle);
- goto done;
- } else {
- const char *text = (const char*)sqlite3_column_text(statement, 0);
- if (strcmp(text, "ok") != 0) {
- LOGE("integrity check failed for \"%s\": %s\n", integritySql, path8, text);
- jniThrowException(env, "info/guardianproject/database/sqlcipher/SQLiteDatabaseCorruptException", text);
- goto done;
- }
- }
-#endif
-
- err = register_android_functions(handle, UTF16_STORAGE);
- if (err) {
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
- LOGV("Opened '%s' - %p\n", path8, handle);
- env->SetIntField(object, offset_db_handle, (int) handle);
- handle = NULL; // The caller owns the handle now.
-
-done:
- // Release allocated resources
- if (path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
- if (statement != NULL) sqlite3_finalize(statement);
- if (handle != NULL) sqlite3_close(handle);
-}
-
-static char *getDatabaseName(JNIEnv* env, sqlite3 * handle, jstring databaseName) {
- char const *path = env->GetStringUTFChars(databaseName, NULL);
- if (path == NULL) {
- LOGE("Failure in getDatabaseName(). VM ran out of memory?\n");
- return NULL; // VM would have thrown OutOfMemoryError
- }
- char *dbNameStr = createStr(path);
- env->ReleaseStringUTFChars(databaseName, path);
- return dbNameStr;
-}
-
-static void sqlTrace(void *databaseName, const char *sql) {
- LOGI("sql_statement|%s|%s\n", (char *)databaseName, sql);
-}
-
-/* public native void enableSqlTracing(); */
-static void enableSqlTracing(JNIEnv* env, jobject object, jstring databaseName)
-{
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
- sqlite3_trace(handle, &sqlTrace, (void *)getDatabaseName(env, handle, databaseName));
-}
-
-static void sqlProfile(void *databaseName, const char *sql, sqlite3_uint64 tm) {
- double d = tm/1000000.0;
- LOGI("elapsedTime4Sql|%s|%.3f ms|%s\n", (char *)databaseName, d, sql);
-}
-
-/* public native void enableSqlProfiling(); */
-static void enableSqlProfiling(JNIEnv* env, jobject object, jstring databaseName)
-{
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
- sqlite3_profile(handle, &sqlProfile, (void *)getDatabaseName(env, handle, databaseName));
-}
-
-
-/* public native void close(); */
-static void dbclose(JNIEnv* env, jobject object)
-{
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-
- if (handle != NULL) {
- // release the memory associated with the traceFuncArg in enableSqlTracing function
- void *traceFuncArg = sqlite3_trace(handle, &sqlTrace, NULL);
- if (traceFuncArg != NULL) {
- free(traceFuncArg);
- }
- // release the memory associated with the traceFuncArg in enableSqlProfiling function
- traceFuncArg = sqlite3_profile(handle, &sqlProfile, NULL);
- if (traceFuncArg != NULL) {
- free(traceFuncArg);
- }
- LOGV("Closing database: handle=%p\n", handle);
- int result = sqlite3_close(handle);
- if (result == SQLITE_OK) {
- LOGV("Closed %p\n", handle);
- env->SetIntField(object, offset_db_handle, 0);
- } else {
- // This can happen if sub-objects aren't closed first. Make sure the caller knows.
- throw_sqlite3_exception(env, handle);
- LOGE("sqlite3_close(%p) failed: %d\n", handle, result);
- }
- }
-}
-
-/* public native void native_execSQL(String sql); */
-static void native_execSQL(JNIEnv* env, jobject object, jstring sqlString)
-{
- int err;
- int stepErr;
- sqlite3_stmt * statement = NULL;
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
- jchar const * sql = env->GetStringChars(sqlString, NULL);
- jsize sqlLen = env->GetStringLength(sqlString);
-
- if (sql == NULL || sqlLen == 0) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "You must supply an SQL string");
- return;
- }
-
- err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
-
- env->ReleaseStringChars(sqlString, sql);
-
- if (err != SQLITE_OK) {
- char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
- LOGE("Failure %d (%s) on %p when preparing '%s'.\n", err, sqlite3_errmsg(handle), handle, sql8);
- throw_sqlite3_exception(env, handle, sql8);
- env->ReleaseStringUTFChars(sqlString, sql8);
- return;
- }
-
- stepErr = sqlite3_step(statement);
- err = sqlite3_finalize(statement);
-
- if (stepErr != SQLITE_DONE) {
- if (stepErr == SQLITE_ROW) {
- throw_sqlite3_exception(env, "Queries cannot be performed using execSQL(), use query() instead.");
- } else {
- char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
- LOGE("Failure %d (%s) on %p when executing '%s'\n", err, sqlite3_errmsg(handle), handle, sql8);
- throw_sqlite3_exception(env, handle, sql8);
- env->ReleaseStringUTFChars(sqlString, sql8);
-
- }
- } else
-#ifndef DB_LOG_STATEMENTS
-// IF_LOGV()
-#endif
- {
- char const * sql8 = env->GetStringUTFChars(sqlString, NULL);
- LOGV("Success on %p when executing '%s'\n", handle, sql8);
- env->ReleaseStringUTFChars(sqlString, sql8);
- }
-}
-
-/* native long lastInsertRow(); */
-static jlong lastInsertRow(JNIEnv* env, jobject object)
-{
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-
- return sqlite3_last_insert_rowid(handle);
-}
-
-/* native int lastChangeCount(); */
-static jint lastChangeCount(JNIEnv* env, jobject object)
-{
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
-
- return sqlite3_changes(handle);
-}
-
-/* native int native_getDbLookaside(); */
-static jint native_getDbLookaside(JNIEnv* env, jobject object)
-{
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
- int pCur = -1;
- int unused;
- sqlite3_db_status(handle, SQLITE_DBSTATUS_LOOKASIDE_USED, &pCur, &unused, 0);
- return pCur;
-}
-
-/* set locale in the android_metadata table, install localized collators, and rebuild indexes */
-static void native_setLocale(JNIEnv* env, jobject object, jstring localeString, jint flags)
-{
- if ((flags & NO_LOCALIZED_COLLATORS)) return;
-
- int err;
- char const* locale8 = env->GetStringUTFChars(localeString, NULL);
- sqlite3 * handle = (sqlite3 *)env->GetIntField(object, offset_db_handle);
- sqlite3_stmt* stmt = NULL;
- char** meta = NULL;
- int rowCount, colCount;
- char* dbLocale = NULL;
-
- // create the table, if necessary and possible
- if (!(flags & OPEN_READONLY)) {
- static const char *createSql ="CREATE TABLE IF NOT EXISTS " ANDROID_TABLE " (locale TEXT)";
- err = sqlite3_exec(handle, createSql, NULL, NULL, NULL);
- if (err != SQLITE_OK) {
- LOGE("CREATE TABLE " ANDROID_TABLE " failed\n");
- throw_sqlite3_exception(env, handle);
- goto done;
- }
- }
-
- // try to read from the table
- static const char *selectSql = "SELECT locale FROM " ANDROID_TABLE " LIMIT 1";
- err = sqlite3_get_table(handle, selectSql, &meta, &rowCount, &colCount, NULL);
- if (err != SQLITE_OK) {
- LOGE("SELECT locale FROM " ANDROID_TABLE " failed\n");
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
- dbLocale = (rowCount >= 1) ? meta[colCount] : NULL;
-
- if (dbLocale != NULL && !strcmp(dbLocale, locale8)) {
- // database locale is the same as the desired locale; set up the collators and go
- err = register_localized_collators(handle, locale8, UTF16_STORAGE);
- if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
- goto done; // no database changes needed
- }
-
- if ((flags & OPEN_READONLY)) {
- // read-only database, so we're going to have to put up with whatever we got
- // For registering new index. Not for modifing the read-only database.
- err = register_localized_collators(handle, locale8, UTF16_STORAGE);
- if (err != SQLITE_OK) throw_sqlite3_exception(env, handle);
- goto done;
- }
-
- // need to update android_metadata and indexes atomically, so use a transaction...
- err = sqlite3_exec(handle, "BEGIN TRANSACTION", NULL, NULL, NULL);
- if (err != SQLITE_OK) {
- LOGE("BEGIN TRANSACTION failed setting locale\n");
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
- err = register_localized_collators(handle, locale8, UTF16_STORAGE);
- if (err != SQLITE_OK) {
- LOGE("register_localized_collators() failed setting locale\n");
- throw_sqlite3_exception(env, handle);
- goto rollback;
- }
-
- err = sqlite3_exec(handle, "DELETE FROM " ANDROID_TABLE, NULL, NULL, NULL);
- if (err != SQLITE_OK) {
- LOGE("DELETE failed setting locale\n");
- throw_sqlite3_exception(env, handle);
- goto rollback;
- }
-
- static const char *sql = "INSERT INTO " ANDROID_TABLE " (locale) VALUES(?);";
- err = sqlite3_prepare_v2(handle, sql, -1, &stmt, NULL);
- if (err != SQLITE_OK) {
- LOGE("sqlite3_prepare_v2(\"%s\") failed\n", sql);
- throw_sqlite3_exception(env, handle);
- goto rollback;
- }
-
- err = sqlite3_bind_text(stmt, 1, locale8, -1, SQLITE_TRANSIENT);
- if (err != SQLITE_OK) {
- LOGE("sqlite3_bind_text() failed setting locale\n");
- throw_sqlite3_exception(env, handle);
- goto rollback;
- }
-
- err = sqlite3_step(stmt);
- if (err != SQLITE_OK && err != SQLITE_DONE) {
- LOGE("sqlite3_step(\"%s\") failed setting locale\n", sql);
- throw_sqlite3_exception(env, handle);
- goto rollback;
- }
-
- err = sqlite3_exec(handle, "REINDEX LOCALIZED", NULL, NULL, NULL);
- if (err != SQLITE_OK) {
- LOGE("REINDEX LOCALIZED failed\n");
- throw_sqlite3_exception(env, handle);
- goto rollback;
- }
-
- // all done, yay!
- err = sqlite3_exec(handle, "COMMIT TRANSACTION", NULL, NULL, NULL);
- if (err != SQLITE_OK) {
- LOGE("COMMIT TRANSACTION failed setting locale\n");
- throw_sqlite3_exception(env, handle);
- goto done;
- }
-
-rollback:
- if (err != SQLITE_OK) {
- sqlite3_exec(handle, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
- }
-
-done:
- if (locale8 != NULL) env->ReleaseStringUTFChars(localeString, locale8);
- if (stmt != NULL) sqlite3_finalize(stmt);
- if (meta != NULL) sqlite3_free_table(meta);
-}
-
-static jint native_releaseMemory(JNIEnv *env, jobject clazz)
-{
- // Attempt to release as much memory from the
- return sqlite3_release_memory(SQLITE_SOFT_HEAP_LIMIT);
-}
-
-static JNINativeMethod sMethods[] =
-{
- /* name, signature, funcPtr */
- {"dbopen", "(Ljava/lang/String;I)V", (void *)dbopen},
- {"dbclose", "()V", (void *)dbclose},
- {"enableSqlTracing", "(Ljava/lang/String;)V", (void *)enableSqlTracing},
- {"enableSqlProfiling", "(Ljava/lang/String;)V", (void *)enableSqlProfiling},
- {"native_execSQL", "(Ljava/lang/String;)V", (void *)native_execSQL},
- {"lastInsertRow", "()J", (void *)lastInsertRow},
- {"lastChangeCount", "()I", (void *)lastChangeCount},
- {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale},
- {"native_getDbLookaside", "()I", (void *)native_getDbLookaside},
- {"releaseMemory", "()I", (void *)native_releaseMemory},
-
-};
-
-int register_android_database_SQLiteDatabase(JNIEnv *env)
-{
- jclass clazz;
-
- clazz = env->FindClass("info/guardianproject/database/sqlcipher/SQLiteDatabase");
- if (clazz == NULL) {
- LOGE("Can't find info/guardianproject/database/sqlcipher/SQLiteDatabase\n");
- return -1;
- }
-
- offset_db_handle = env->GetFieldID(clazz, "mNativeHandle", "I");
- if (offset_db_handle == NULL) {
- LOGE("Can't find SQLiteDatabase.mNativeHandle\n");
- return -1;
- }
-
- return android::AndroidRuntime::registerNativeMethods(env, "info/guardianproject/database/sqlcipher/SQLiteDatabase", sMethods, NELEM(sMethods));
-}
-
-
-
-
-//this code is not executed
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
- JNIEnv *env;
- //gJavaVM = vm;
- LOGI("JNI_OnLoad called");
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_2) != JNI_OK) {
- LOGE("Failed to get the environment using GetEnv()");
- return -1;
- }
-
- LOGI("JNI_OnLoad register methods ");
-
- register_android_database_SQLiteDatabase(env);
- register_android_database_SQLiteCompiledSql(env);
-
- register_android_database_SQLiteQuery(env);
-
- register_android_database_SQLiteProgram(env);
-
- register_android_database_SQLiteStatement(env);
-
- register_android_database_CursorWindow(env);
-
- //register_android_database_SQLiteDebug(env);
-
-return JNI_VERSION_1_2;
-
-}
-
-/* throw a SQLiteException with a message appropriate for the error in handle */
-void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
- throw_sqlite3_exception(env, handle, NULL);
-}
-
-/* throw a SQLiteException with the given message */
-void throw_sqlite3_exception(JNIEnv* env, const char* message) {
- throw_sqlite3_exception(env, NULL, message);
-}
-
-/* throw a SQLiteException with a message appropriate for the error in handle
- concatenated with the given message
- */
-void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
- if (handle) {
- throw_sqlite3_exception(env, sqlite3_errcode(handle),
- sqlite3_errmsg(handle), message);
- } else {
- // we use SQLITE_OK so that a generic SQLiteException is thrown;
- // any code not specified in the switch statement below would do.
- throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
- }
-}
-
-/* throw a SQLiteException for a given error code */
-void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
- if (errcode == SQLITE_DONE) {
- throw_sqlite3_exception(env, errcode, NULL, message);
- } else {
- char temp[21];
- sprintf(temp, "error code %d", errcode);
- throw_sqlite3_exception(env, errcode, temp, message);
- }
-}
-
-/* throw a SQLiteException for a given error code, sqlite3message, and
- user message
- */
-void throw_sqlite3_exception(JNIEnv* env, int errcode,
- const char* sqlite3Message, const char* message) {
- const char* exceptionClass;
- switch (errcode) {
- case SQLITE_IOERR:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteDiskIOException";
- break;
- case SQLITE_CORRUPT:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteDatabaseCorruptException";
- break;
- case SQLITE_CONSTRAINT:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteConstraintException";
- break;
- case SQLITE_ABORT:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteAbortException";
- break;
- case SQLITE_DONE:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteDoneException";
- break;
- case SQLITE_FULL:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteFullException";
- break;
- case SQLITE_MISUSE:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteMisuseException";
- break;
- default:
- exceptionClass = "info/guardianproject/database/sqlcipher/SQLiteException";
- break;
- }
-
- if (sqlite3Message != NULL && message != NULL) {
- char* fullMessage = (char *)malloc(strlen(sqlite3Message) + strlen(message) + 3);
- if (fullMessage != NULL) {
- strcpy(fullMessage, sqlite3Message);
- strcat(fullMessage, ": ");
- strcat(fullMessage, message);
- jniThrowException(env, exceptionClass, fullMessage);
- free(fullMessage);
- } else {
- jniThrowException(env, exceptionClass, sqlite3Message);
- }
- } else if (sqlite3Message != NULL) {
- jniThrowException(env, exceptionClass, sqlite3Message);
- } else {
- jniThrowException(env, exceptionClass, message);
- }
-}
-
-
-} // namespace guardianproject
diff --git a/jni/tinyutils/smartpointer.h b/jni/tinyutils/smartpointer.h
deleted file mode 100644
index 88032d7f..00000000
--- a/jni/tinyutils/smartpointer.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * smartpointer.h
- * Android
- *
- * Copyright 2005 The Android Open Source Project
- *
- */
-
-#ifndef ANDROID_SMART_POINTER_H
-#define ANDROID_SMART_POINTER_H
-
-#include
-#include
-#include
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-#define COMPARE(_op_) \
-inline bool operator _op_ (const sp& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-inline bool operator _op_ (const T* o) const { \
- return m_ptr _op_ o; \
-} \
-template \
-inline bool operator _op_ (const sp& o) const { \
- return m_ptr _op_ o.m_ptr; \
-} \
-template \
-inline bool operator _op_ (const U* o) const { \
- return m_ptr _op_ o; \
-}
-
-// ---------------------------------------------------------------------------
-
-template
-class sp
-{
-public:
- inline sp() : m_ptr(0) { }
-
- sp(T* other);
- sp(const sp& other);
- template sp(U* other);
- template sp(const sp& other);
-
- ~sp();
-
- // Assignment
-
- sp& operator = (T* other);
- sp& operator = (const sp& other);
-
- template sp& operator = (const sp& other);
- template sp& operator = (U* other);
-
- // Reset
- void clear();
-
- // Accessors
-
- inline T& operator* () const { return *m_ptr; }
- inline T* operator-> () const { return m_ptr; }
- inline T* get() const { return m_ptr; }
-
- // Operators
-
- COMPARE(==)
- COMPARE(!=)
- COMPARE(>)
- COMPARE(<)
- COMPARE(<=)
- COMPARE(>=)
-
-private:
- template friend class sp;
-
- T* m_ptr;
-};
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts below here.
-
-template
-sp::sp(T* other)
- : m_ptr(other)
-{
- if (other) other->incStrong(this);
-}
-
-template