├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── README.md.template ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── sqlcipher ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ ├── assets │ │ ├── sqlcipher-1.x-test.db │ │ ├── sqlcipher-3.0-testkey.db │ │ ├── sqlcipher-4-wal-journal.db │ │ ├── sqlcipher-4.x-testkey.db │ │ ├── sqlite-plaintext.db │ │ └── unencrypted.db │ └── java │ │ └── net │ │ └── zetetic │ │ └── database │ │ ├── SQLCipherWALTestScenario.java │ │ ├── database_cts │ │ ├── AbstractCursorTest.java │ │ ├── CursorJoinerTest.java │ │ ├── CursorWindowTest.java │ │ ├── CursorWrapperTest.java │ │ ├── DatabaseCursorTest.java │ │ ├── DatabaseUtilsInsertHelperTest.java │ │ ├── DatabaseUtilsTest.java │ │ ├── MergeCursorTest.java │ │ └── README │ │ ├── sqlcipher_android │ │ └── DatabaseGeneralTest.java │ │ └── sqlcipher_cts │ │ ├── AndroidSQLCipherTestCase.java │ │ ├── CipherCompatibilityTest.java │ │ ├── DatabaseStatementTest.java │ │ ├── ImportUnencryptedDatabaseTest.java │ │ ├── JsonCastTest.java │ │ ├── MigrateDatabaseFrom1xFormatToCurrentFormatTest.java │ │ ├── README │ │ ├── RoomUpsertTest.java │ │ ├── SQLCipherDatabaseTest.java │ │ ├── SQLCipherOpenHelperTest.java │ │ ├── SQLCipherVersionTest.java │ │ ├── SQLiteAbortExceptionTest.java │ │ ├── SQLiteClosableTest.java │ │ ├── SQLiteConstraintExceptionTest.java │ │ ├── SQLiteCursorTest.java │ │ ├── SQLiteDatabaseCorruptExceptionTest.java │ │ ├── SQLiteDatabaseTest.java │ │ ├── SQLiteDiskIOExceptionTest.java │ │ ├── SQLiteDoneExceptionTest.java │ │ ├── SQLiteExceptionTest.java │ │ ├── SQLiteFtsTest.java │ │ ├── SQLiteFullExceptionTest.java │ │ ├── SQLiteMisuseExceptionTest.java │ │ ├── SQLiteOpenHelperTest.java │ │ ├── SQLiteProgramTest.java │ │ ├── SQLiteQueryBuilderTest.java │ │ ├── SQLiteQueryTest.java │ │ ├── SQLiteStatementTest.java │ │ ├── SupportAPIRoomTest.java │ │ └── SupportHelperTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── net │ │ └── zetetic │ │ └── database │ │ ├── AbstractCursor.java │ │ ├── AbstractWindowedCursor.java │ │ ├── CursorWindow.java │ │ ├── CursorWindowAllocationException.java │ │ ├── DatabaseErrorHandler.java │ │ ├── DatabaseUtils.java │ │ ├── DefaultDatabaseErrorHandler.java │ │ ├── LogTarget.java │ │ ├── LogcatTarget.java │ │ ├── Logger.java │ │ ├── MatrixCursor.java │ │ ├── NoopTarget.java │ │ └── sqlcipher │ │ ├── CloseGuard.java │ │ ├── DatabaseObjectNotClosedException.java │ │ ├── SQLiteClosable.java │ │ ├── SQLiteConnection.java │ │ ├── SQLiteConnectionPool.java │ │ ├── SQLiteCursor.java │ │ ├── SQLiteCursorDriver.java │ │ ├── SQLiteCustomFunction.java │ │ ├── SQLiteDatabase.java │ │ ├── SQLiteDatabaseConfiguration.java │ │ ├── SQLiteDatabaseHook.java │ │ ├── SQLiteDebug.java │ │ ├── SQLiteDirectCursorDriver.java │ │ ├── SQLiteGlobal.java │ │ ├── SQLiteNotADatabaseException.java │ │ ├── SQLiteOpenHelper.java │ │ ├── SQLiteProgram.java │ │ ├── SQLiteQuery.java │ │ ├── SQLiteQueryBuilder.java │ │ ├── SQLiteSession.java │ │ ├── SQLiteStatement.java │ │ ├── SQLiteStatementInfo.java │ │ ├── SQLiteTransactionListener.java │ │ ├── SupportHelper.java │ │ └── SupportOpenHelperFactory.java │ ├── jni │ ├── Android.mk │ ├── Application.mk │ └── sqlcipher │ │ ├── ALog-priv.h │ │ ├── Android.mk │ │ ├── CursorWindow.cpp │ │ ├── CursorWindow.h │ │ ├── Errors.h │ │ ├── JNIHelp.cpp │ │ ├── JNIString.cpp │ │ ├── JniConstants.cpp │ │ ├── README │ │ ├── android_database_CursorWindow.cpp │ │ ├── android_database_SQLiteCommon.cpp │ │ ├── android_database_SQLiteCommon.h │ │ ├── android_database_SQLiteConnection.cpp │ │ ├── android_database_SQLiteDebug.cpp │ │ ├── android_database_SQLiteGlobal.cpp │ │ └── nativehelper │ │ ├── JNIHelp.h │ │ ├── JniConstants.h │ │ ├── ScopedLocalRef.h │ │ └── jni.h │ └── res │ └── values │ └── strings.xml ├── sqlciphertest ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── net │ │ └── zetetic │ │ └── customsqlciphertest │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── www ├── index.wiki ├── install.wiki ├── see.wiki ├── usage.wiki ├── verysimpleapp.wiki └── vsa ├── 1adddep.png ├── 1build.png ├── 1editmainactivity.png ├── 1importmodule.png ├── 1newproject.png ├── 1setwidgetid.png ├── 2adddep.png ├── 2importmodule.png ├── 2newproject.png ├── 3adddep.png ├── 3importmodule.png ├── 3newproject.png └── 4newproject.png /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | sqlcipher/build 4 | sqlciphertest/build 5 | .DS_Store 6 | *.iml 7 | local.properties 8 | sqlcipher/.cxx 9 | sqlcipher/src/main/jni/sqlcipher/sqlite3.c 10 | sqlcipher/src/main/jni/sqlcipher/sqlite3.h 11 | sqlcipher/src/main/jni/sqlcipher/android-libs 12 | build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2023, ZETETIC LLC 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the ZETETIC LLC nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY 16 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean build-debug build-release \ 2 | publish-snapshot-to-local-maven \ 3 | publish-snapshot-to-local-nexus test 4 | GRADLE = ./gradlew 5 | 6 | clean: 7 | -rm -rf build 8 | $(GRADLE) clean 9 | 10 | test: 11 | ANDROID_SERIAL=$(shell adb devices | tail -n +2 | awk '!/emulator/{print $$1}') \ 12 | $(GRADLE) :sqlcipher:connectedDebugAndroidTest 13 | 14 | build-debug: 15 | $(GRADLE) assembleDebug 16 | 17 | build-release: 18 | $(GRADLE) \ 19 | -PsqlcipherAndroidVersion="$(SQLCIPHER_ANDROID_VERSION)" \ 20 | assembleRelease 21 | 22 | publish-snapshot-to-local-maven: 23 | @ $(collect-signing-info) \ 24 | $(GRADLE) \ 25 | -PpublishSnapshot=true \ 26 | -Psigning.keyId="$$gpgKeyId" \ 27 | -Psigning.secretKeyRingFile="$$gpgKeyRingFile" \ 28 | -Psigning.password="$$gpgPassword" \ 29 | publishReleasePublicationToMavenLocal 30 | 31 | publish-remote-release: 32 | @ $(collect-signing-info) \ 33 | $(collect-nexus-info) \ 34 | $(GRADLE) \ 35 | -PpublishSnapshot=false \ 36 | -PpublishLocal=false \ 37 | -PdebugBuild=false \ 38 | -PsigningKeyId="$$gpgKeyId" \ 39 | -PsigningKeyRingFile="$$gpgKeyRingFile" \ 40 | -PsigningKeyPassword="$$gpgPassword" \ 41 | -PnexusUsername="$$nexusUsername" \ 42 | -PnexusPassword="$$nexusPassword" \ 43 | -PsqlcipherAndroidVersion="$(SQLCIPHER_ANDROID_VERSION)" \ 44 | sqlcipher:publish 45 | 46 | collect-signing-info := \ 47 | read -p "Enter GPG signing key id:" gpgKeyId; \ 48 | read -p "Enter full path to GPG keyring file \ 49 | (possibly ${HOME}/.gnupg/secring.gpg)" gpgKeyRingFile; stty -echo; \ 50 | read -p "Enter GPG password:" gpgPassword; stty echo; 51 | 52 | collect-nexus-info := \ 53 | read -p "Enter Nexus username:" nexusUsername; \ 54 | stty -echo; read -p "Enter Nexus password:" nexusPassword; stty echo; 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLCipher for Android 2 | 3 | SQLCipher for Android provides a library replacement for `android.database.sqlite` on the Android platform for use on [SQLCipher](https://github.com/sqlcipher/sqlcipher) databases. This library is based on the upstream [Android Bindings](https://www.sqlite.org/android/doc/trunk/www/index.wiki) project and aims to be a long-term replacement for the original [SQLCipher for Android](https://github.com/sqlcipher/android-database-sqlcipher) library. 4 | 5 | ### Compatibility 6 | 7 | SQLCipher for Android supports Android API 21 and up on `armeabi-v7a`, `x86`, `x86_64`, and `arm64-v8a` architectures. 8 | 9 | ### Contributions 10 | 11 | We welcome contributions, to contribute to SQLCipher for Android, a [contributor agreement](https://www.zetetic.net/contributions/) needs to be submitted. All submissions should be based on the `master` branch. 12 | 13 | 14 | ### Application Integration 15 | 16 | Add a local reference to the local library and dependency: 17 | 18 | ```groovy 19 | implementation files('libs/sqlcipher-android-undefined-release.aar') 20 | implementation 'androidx.sqlite:sqlite:2.2.0' 21 | ``` 22 | 23 | or source a Community edition build from Maven Central: 24 | 25 | ```groovy 26 | implementation 'net.zetetic:sqlcipher-android:undefined@aar' 27 | implementation 'androidx.sqlite:sqlite:2.2.0' 28 | ``` 29 | 30 | ```java 31 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 32 | 33 | System.loadLibrary("sqlcipher"); 34 | SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, password, null, null, null); 35 | ``` 36 | 37 | ### Pre/Post Key Operations 38 | 39 | To perform operations on the database instance immediately before or after the keying operation is performed, provide a `SQLiteDatabaseHook` instance when creating your database connection: 40 | 41 | ```java 42 | SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { 43 | public void preKey(SQLiteConnection connection) { } 44 | public void postKey(SQLiteConnection connection) { } 45 | }; 46 | ``` 47 | 48 | ### API Usage 49 | 50 | There are two main options for using SQLCipher for Android within an Application: 51 | 52 | 1. [Using the SQLCipher for Android classes](#sqlcipher-for-android-classes) 53 | 2. [Using SQLCipher for Android in conjunction with the Android Room API](#sqlcipher-for-android-room-integration) 54 | 55 | In both cases, prior to using any portion of the SQLCipher for Android library, the native SQLCipher core library must be loaded into the running application process. The SQLCipher core library is bundled within the AAR of SQLCipher for Android, however, the developer must load this library explicitly. An example below: 56 | 57 | ```java 58 | System.loadLibrary("sqlcipher"); 59 | ``` 60 | 61 | #### SQLCipher for Android classes 62 | 63 | SQLCipher for Android provides two classes for opening and access database files. The `SQLiteDatabase` provides static methods for opening/creating database files and general data access. 64 | Additionally, applications may choose to subclass the `SQLiteOpenHelper` class which provides mechanisms for performing database migrations, as well as general data access. 65 | 66 | #### SQLCipher for Android Room Integration 67 | 68 | SQLCipher for Android may also integrate with the Room API via the `SupportOpenHelperFactory`, an example is given below: 69 | 70 | ```java 71 | System.loadLibrary("sqlcipher"); 72 | String password = "Password1!"; 73 | File databaseFile = context.getDatabasePath("demo.db"); 74 | SupportOpenHelperFactory factory = new SupportOpenHelperFactory("password.getBytes(StandardCharsets.UTF_8)); 75 | db = Room.databaseBuilder(context, AppDatabase.class, databaseFile.getAbsolutePath()) 76 | .openHelperFactory(factory).build(); 77 | ``` 78 | 79 | ### Logging 80 | 81 | Logging may occur in 3 distinct areas within this library: 82 | 83 | 1. Within the Java client library 84 | 2. Within the JNI interop layer 85 | 3. Within SQLCipher core 86 | 87 | ##### Java Client Logging 88 | 89 | By default, logging within the Java client library is routed to Logcat. If you wish to disable this logging entirely, you may utilize 90 | the [`NoopTarget`](sqlcipher/src/main/java/net/zetetic/database/NoopTarget.java) instead: 91 | 92 | ```java 93 | Logger.setTarget(new NoopTarget()); 94 | ``` 95 | 96 | You can instead provide a custom logging target by registering a different target that implements the [`LogTarget`](sqlcipher/src/main/java/net/zetetic/database/LogTarget.java) interface. 97 | 98 | ##### JNI Interop Layer 99 | 100 | There are two different compile-specific options available to alter the logging output from the JNI layer. To remove `INFO`, `DEBUG`, and `VERBOSE` log messages from the JNI layer, include `-DNDEBUG` with CFLAGS; this will allow `WARN` and `ERROR` logs to output to logcat. Alternatively, to exclude all log output from JNI, build the library using `-DSQLCIPHER_OMIT_LOG`. 101 | 102 | ##### SQLCipher core 103 | 104 | To manage the logging produced from SQLCipher core, please review the runtime configurations: [`PRAGMA cipher_log`](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log), 105 | [`PRAGMA cipher_log_level`](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log_level), and [`PRAGMA cipher_log_source`](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log_source). 106 | 107 | ### Building 108 | 109 | ## Android NDK 110 | 111 | Currently, SQLCipher for Android uses NDK version "25.2.9519653". 112 | 113 | ## External dependencies 114 | 115 | This repository is not batteries-included. Specifically, you will need to build `libcrypto.a`, the static library from OpenSSL using the NDK for the [supported platforms](#compatibility), and bundle the top-level `include` folder from OpenSSL. Additionally, you will need to build a SQLCipher amalgamation. These files will need to be placed in the following locations: 116 | 117 | ``` 118 | /sqlcipher/src/main/jni/sqlcipher/android-libs/armeabi-v7a/libcrypto.a 119 | /sqlcipher/src/main/jni/sqlcipher/android-libs/x86/libcrypto.a 120 | /sqlcipher/src/main/jni/sqlcipher/android-libs/x86_64/libcrypto.a 121 | /sqlcipher/src/main/jni/sqlcipher/android-libs/arm64-v8a/libcrypto.a 122 | /sqlcipher/src/main/jni/sqlcipher/android-libs/include/ 123 | /sqlcipher/src/main/jni/sqlcipher/sqlite3.c 124 | /sqlcipher/src/main/jni/sqlcipher/sqlite3.h 125 | ``` 126 | 127 | To build the AAR package, either build directly within Android Studio, or from the command line: 128 | 129 | ``` 130 | ./gradlew assembleRelease 131 | ``` 132 | 133 | -------------------------------------------------------------------------------- /README.md.template: -------------------------------------------------------------------------------- 1 | # SQLCipher for Android 2 | 3 | SQLCipher for Android provides a library replacement for `android.database.sqlite` on the Android platform for use on [SQLCipher](https://github.com/sqlcipher/sqlcipher) databases. This library is based on the upstream [Android Bindings](https://www.sqlite.org/android/doc/trunk/www/index.wiki) project and aims to be a long-term replacement for the original [SQLCipher for Android](https://github.com/sqlcipher/android-database-sqlcipher) library. 4 | 5 | ### Compatibility 6 | 7 | SQLCipher for Android supports Android API <%=minSdkVersion%> and up on `armeabi-v7a`, `x86`, `x86_64`, and `arm64-v8a` architectures. 8 | 9 | ### Contributions 10 | 11 | We welcome contributions, to contribute to SQLCipher for Android, a [contributor agreement](https://www.zetetic.net/contributions/) needs to be submitted. All submissions should be based on the `master` branch. 12 | 13 | 14 | ### Application Integration 15 | 16 | Add a local reference to the local library and dependency: 17 | 18 | ```groovy 19 | implementation files('libs/sqlcipher-android-<%=libraryVersion%>-release.aar') 20 | implementation 'androidx.sqlite:sqlite:<%=androidXSQLiteVersion%>' 21 | ``` 22 | 23 | or source a Community edition build from Maven Central: 24 | 25 | ```groovy 26 | implementation 'net.zetetic:sqlcipher-android:<%=libraryVersion%>@aar' 27 | implementation 'androidx.sqlite:sqlite:<%=androidXSQLiteVersion%>' 28 | ``` 29 | 30 | ```java 31 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 32 | 33 | System.loadLibrary("sqlcipher"); 34 | SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(databaseFile, password, null, null, null); 35 | ``` 36 | 37 | ### Pre/Post Key Operations 38 | 39 | To perform operations on the database instance immediately before or after the keying operation is performed, provide a `SQLiteDatabaseHook` instance when creating your database connection: 40 | 41 | ```java 42 | SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { 43 | public void preKey(SQLiteConnection connection) { } 44 | public void postKey(SQLiteConnection connection) { } 45 | }; 46 | ``` 47 | 48 | ### API Usage 49 | 50 | There are two main options for using SQLCipher for Android within an Application: 51 | 52 | 1. [Using the SQLCipher for Android classes](#sqlcipher-for-android-classes) 53 | 2. [Using SQLCipher for Android in conjunction with the Android Room API](#sqlcipher-for-android-room-integration) 54 | 55 | In both cases, prior to using any portion of the SQLCipher for Android library, the native SQLCipher core library must be loaded into the running application process. The SQLCipher core library is bundled within the AAR of SQLCipher for Android, however, the developer must load this library explicitly. An example below: 56 | 57 | ```java 58 | System.loadLibrary("sqlcipher"); 59 | ``` 60 | 61 | #### SQLCipher for Android classes 62 | 63 | SQLCipher for Android provides two classes for opening and access database files. The `SQLiteDatabase` provides static methods for opening/creating database files and general data access. 64 | Additionally, applications may choose to subclass the `SQLiteOpenHelper` class which provides mechanisms for performing database migrations, as well as general data access. 65 | 66 | #### SQLCipher for Android Room Integration 67 | 68 | SQLCipher for Android may also integrate with the Room API via the `SupportOpenHelperFactory`, an example is given below: 69 | 70 | ```java 71 | System.loadLibrary("sqlcipher"); 72 | String password = "Password1!"; 73 | File databaseFile = context.getDatabasePath("demo.db"); 74 | SupportOpenHelperFactory factory = new SupportOpenHelperFactory("password.getBytes(StandardCharsets.UTF_8)); 75 | db = Room.databaseBuilder(context, AppDatabase.class, databaseFile.getAbsolutePath()) 76 | .openHelperFactory(factory).build(); 77 | ``` 78 | 79 | ### Logging 80 | 81 | Logging may occur in 3 distinct areas within this library: 82 | 83 | 1. Within the Java client library 84 | 2. Within the JNI interop layer 85 | 3. Within SQLCipher core 86 | 87 | ##### Java Client Logging 88 | 89 | By default, logging within the Java client library is routed to Logcat. If you wish to disable this logging entirely, you may utilize 90 | the [`NoopTarget`](sqlcipher/src/main/java/net/zetetic/database/NoopTarget.java) instead: 91 | 92 | ```java 93 | Logger.setTarget(new NoopTarget()); 94 | ``` 95 | 96 | You can instead provide a custom logging target by registering a different target that implements the [`LogTarget`](sqlcipher/src/main/java/net/zetetic/database/LogTarget.java) interface. 97 | 98 | ##### JNI Interop Layer 99 | 100 | There are two different compile-specific options available to alter the logging output from the JNI layer. To remove `INFO`, `DEBUG`, and `VERBOSE` log messages from the JNI layer, include `-DNDEBUG` with CFLAGS; this will allow `WARN` and `ERROR` logs to output to logcat. Alternatively, to exclude all log output from JNI, build the library using `-DSQLCIPHER_OMIT_LOG`. 101 | 102 | ##### SQLCipher core 103 | 104 | To manage the logging produced from SQLCipher core, please review the runtime configurations: [`PRAGMA cipher_log`](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log), 105 | [`PRAGMA cipher_log_level`](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log_level), and [`PRAGMA cipher_log_source`](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log_source). 106 | 107 | ### Building 108 | 109 | ## Android NDK 110 | 111 | Currently, SQLCipher for Android uses NDK version "<%=androidNdkVersion%>". 112 | 113 | ## External dependencies 114 | 115 | This repository is not batteries-included. Specifically, you will need to build `libcrypto.a`, the static library from OpenSSL using the NDK for the [supported platforms](#compatibility), and bundle the top-level `include` folder from OpenSSL. Additionally, you will need to build a SQLCipher amalgamation. These files will need to be placed in the following locations: 116 | 117 | ``` 118 | /sqlcipher/src/main/jni/sqlcipher/android-libs/armeabi-v7a/libcrypto.a 119 | /sqlcipher/src/main/jni/sqlcipher/android-libs/x86/libcrypto.a 120 | /sqlcipher/src/main/jni/sqlcipher/android-libs/x86_64/libcrypto.a 121 | /sqlcipher/src/main/jni/sqlcipher/android-libs/arm64-v8a/libcrypto.a 122 | /sqlcipher/src/main/jni/sqlcipher/android-libs/include/ 123 | /sqlcipher/src/main/jni/sqlcipher/sqlite3.c 124 | /sqlcipher/src/main/jni/sqlcipher/sqlite3.h 125 | ``` 126 | 127 | To build the AAR package, either build directly within Android Studio, or from the command line: 128 | 129 | ``` 130 | ./gradlew assembleRelease 131 | ``` 132 | 133 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import groovy.text.SimpleTemplateEngine 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | google() 7 | } 8 | dependencies { 9 | def gradleToolsVersion = "8.7.2" 10 | classpath "com.android.tools.build:gradle:${gradleToolsVersion}" 11 | } 12 | } 13 | 14 | plugins { 15 | id('maven-publish') 16 | id('signing') 17 | id("io.github.gradle-nexus.publish-plugin") version "1.1.0" 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | mavenCentral() 23 | google() 24 | } 25 | tasks.withType(JavaCompile).tap { 26 | configureEach { 27 | options.compilerArgs << "-Xlint:deprecation" 28 | } 29 | } 30 | } 31 | 32 | project.ext { 33 | if(project.hasProperty('sqlcipherAndroidVersion') && "${sqlcipherAndroidVersion}") { 34 | libraryVersion = "${sqlcipherAndroidVersion}" 35 | } else { 36 | libraryVersion = "undefined" 37 | } 38 | minSdkVersion = 21 39 | androidXSQLiteVersion = "2.2.0" 40 | roomVersion = "2.5.0" 41 | androidNdkVersion = "25.2.9519653" 42 | mavenLocalRepositoryPrefix = "file://" 43 | if(project.hasProperty('publishLocal') && publishLocal.toBoolean()){ 44 | mavenSnapshotRepositoryUrl = "outputs/snapshot" 45 | mavenReleaseRepositoryUrl = "outputs/release" 46 | } else { 47 | mavenLocalRepositoryPrefix = "" 48 | mavenSnapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots" 49 | mavenReleaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" 50 | } 51 | if(project.hasProperty('publishSnapshot') && publishSnapshot.toBoolean()){ 52 | mavenVersionName = "${libraryVersion}-SNAPSHOT" 53 | } else { 54 | mavenVersionName = "${libraryVersion}" 55 | } 56 | if(project.hasProperty('nexusUsername')){ 57 | nexusUsername = "${nexusUsername}" 58 | } 59 | if(project.hasProperty('nexusPassword')){ 60 | nexusPassword = "${nexusPassword}" 61 | } 62 | nexusUsername = project.hasProperty('nexusUsername') ? "${nexusUsername}" : "" 63 | nexusPassword = project.hasProperty('nexusPassword') ? "${nexusPassword}" : "" 64 | nexusStagingProfileId = project.hasProperty('nexusStagingProfileId') ? "${nexusStagingProfileId}" : "" 65 | } 66 | 67 | task generateReadMe { 68 | def readme = new File("README.md") 69 | def engine = new SimpleTemplateEngine() 70 | def reader = new FileReader("README.md.template") 71 | def binding = [ 72 | libraryVersion: project.ext.libraryVersion, 73 | androidXSQLiteVersion: project.ext.androidXSQLiteVersion, 74 | androidNdkVersion: project.ext.androidNdkVersion, 75 | minSdkVersion: project.ext.minSdkVersion, 76 | ] 77 | def template = engine.createTemplate(reader).make(binding) 78 | readme.write(template.toString()) 79 | } 80 | 81 | project.afterEvaluate { 82 | build.dependsOn generateReadMe 83 | } 84 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.enableJetifier=true 21 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jun 16 15:29:23 CDT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sqlcipher' 2 | -------------------------------------------------------------------------------- /sqlcipher/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class net.zetetic.** { 2 | native ; 3 | private native ; 4 | public (...); 5 | long mNativeHandle; 6 | } 7 | 8 | -keepclassmembers class net.zetetic.database.sqlcipher.SQLiteCustomFunction { 9 | public java.lang.String name; 10 | public int numArgs; 11 | private void dispatchCallback(java.lang.String[]); 12 | } 13 | 14 | -keepclassmembers class net.zetetic.database.sqlcipher.SQLiteDebug$PagerStats { 15 | public int largestMemAlloc; 16 | public int memoryUsed; 17 | public int pageCacheOverflow; 18 | } 19 | -------------------------------------------------------------------------------- /sqlcipher/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/dan/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/assets/sqlcipher-1.x-test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/sqlcipher/src/androidTest/assets/sqlcipher-1.x-test.db -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/assets/sqlcipher-3.0-testkey.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/sqlcipher/src/androidTest/assets/sqlcipher-3.0-testkey.db -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/assets/sqlcipher-4-wal-journal.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/sqlcipher/src/androidTest/assets/sqlcipher-4-wal-journal.db -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/assets/sqlcipher-4.x-testkey.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/sqlcipher/src/androidTest/assets/sqlcipher-4.x-testkey.db -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/assets/sqlite-plaintext.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/sqlcipher/src/androidTest/assets/sqlite-plaintext.db -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/assets/unencrypted.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/sqlcipher-android/ffe6c588b06ab042d60264f1f0da79a7ac56db0f/sqlcipher/src/androidTest/assets/unencrypted.db -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/SQLCipherWALTestScenario.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | import android.database.Cursor; 7 | import android.os.AsyncTask; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | import androidx.test.ext.junit.runners.AndroidJUnit4; 10 | 11 | import junit.framework.Assert; 12 | 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | 17 | import net.zetetic.database.sqlcipher.SQLiteConnection; 18 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 19 | import net.zetetic.database.sqlcipher.SQLiteOpenHelper; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.util.Arrays; 24 | 25 | 26 | class MyHelper extends SQLiteOpenHelper { 27 | 28 | public static final String DATABASE_NAME = "mydb.db"; 29 | 30 | public MyHelper(Context ctx){ 31 | super(ctx, ctx.getDatabasePath(DATABASE_NAME).getAbsolutePath(), "secret", null, 1, 1, null, null, false); 32 | } 33 | public void onConfigure(SQLiteDatabase db){ 34 | db.enableWriteAheadLogging(); 35 | } 36 | public void onCreate(SQLiteDatabase db){ 37 | db.execSQL("CREATE TABLE t1(x)"); 38 | } 39 | public void onUpgrade(SQLiteDatabase db, int iOld, int iNew){ 40 | } 41 | } 42 | 43 | 44 | /** 45 | * Created by dan on 5/3/17. 46 | */ 47 | @RunWith(AndroidJUnit4.class) 48 | public class SQLCipherWALTestScenario { 49 | private Context mContext; 50 | 51 | /* 52 | ** Test if the database at path is encrypted or not. The db 53 | ** is assumed to be encrypted if the first 6 bytes are anything 54 | ** other than "SQLite". 55 | ** 56 | ** If the test reveals that the db is encrypted, return the string 57 | ** "encrypted". Otherwise, "unencrypted". 58 | */ 59 | public String db_is_encrypted(String path) throws Exception { 60 | FileInputStream in = new FileInputStream(mContext.getDatabasePath(path)); 61 | 62 | byte[] buffer = new byte[6]; 63 | in.read(buffer, 0, 6); 64 | 65 | String res = "encrypted"; 66 | if( Arrays.equals(buffer, (new String("SQLite")).getBytes()) ){ 67 | res = "unencrypted"; 68 | } 69 | return res; 70 | } 71 | 72 | @Before 73 | public void setup() throws Exception { 74 | 75 | System.loadLibrary("sqlcipher"); 76 | 77 | mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 78 | 79 | // delete any existing database 80 | File databaseFile = mContext.getDatabasePath(MyHelper.DATABASE_NAME); 81 | databaseFile.mkdirs(); 82 | if (databaseFile.exists()) { 83 | databaseFile.delete(); 84 | } 85 | } 86 | 87 | @Test 88 | @SuppressWarnings("deprecation") 89 | public void testEncryptedWalMode() throws Exception { 90 | // create database 91 | final MyHelper helper = new MyHelper(mContext); 92 | helper.getWritableDatabase(); 93 | 94 | // verify that WAL journal mode is set 95 | final Cursor pragmaCursor = helper.getWritableDatabase().rawQuery("PRAGMA journal_mode", null); 96 | pragmaCursor.moveToFirst(); 97 | assertEquals("wal", pragmaCursor.getString(pragmaCursor.getColumnIndex("journal_mode"))); 98 | pragmaCursor.close(); 99 | 100 | // start long running transaction 101 | AsyncTask.execute(new Runnable() { 102 | @Override 103 | public void run() { 104 | helper.getWritableDatabase().beginTransactionNonExclusive(); 105 | 106 | // simulate long insert 107 | try { 108 | Thread.sleep(5000); 109 | } catch (InterruptedException e) { 110 | e.printStackTrace(); 111 | } 112 | 113 | helper.getWritableDatabase().setTransactionSuccessful(); 114 | helper.getWritableDatabase().endTransaction(); 115 | } 116 | }); 117 | 118 | // wait a short time until the long transaction starts 119 | Thread.sleep(300); 120 | 121 | long startTime = System.currentTimeMillis(); 122 | 123 | //try to read something from the database while the slow transaction is running 124 | helper.getWritableDatabase().execSQL("SELECT * FROM t1"); 125 | 126 | //verify that the operation didn't wait until the 3000ms long operation finished 127 | if (System.currentTimeMillis() - startTime > 3000) { 128 | throw new Exception("WAL mode isn't working corectly - read operation was blocked"); 129 | } 130 | 131 | if( SQLiteConnection.hasCodec() ){ 132 | Assert.assertEquals("encrypted", db_is_encrypted(MyHelper.DATABASE_NAME)); 133 | } else { 134 | Assert.assertEquals("unencrypted", db_is_encrypted(MyHelper.DATABASE_NAME)); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/database_cts/README: -------------------------------------------------------------------------------- 1 | 2 | The tests in this directory are modified versions of the SQLite tests in the 3 | Android CTS (Compatibility Test Suite) project. This project is stored in 4 | a git repository here: 5 | 6 | https://android.googlesource.com/platform/cts 7 | 8 | The snapshot was taken from master branch commit id: 9 | 10 | 0794ecacb76b54eeee881762d0edfc83209ab05f 11 | 12 | https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f 13 | 14 | Within that project, the tests are in directory: 15 | 16 | ./tests/tests/database/src/android/database/cts/ 17 | 18 | So: 19 | 20 | https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f/tests/tests/database/src/android/database/cts 21 | 22 | Only those tests that use SQLite objects have been copied over. 23 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/CipherCompatibilityTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.Matchers.greaterThan; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import android.database.Cursor; 7 | import android.util.Log; 8 | 9 | import net.zetetic.database.sqlcipher.SQLiteConnection; 10 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 11 | import net.zetetic.database.sqlcipher.SQLiteDatabaseHook; 12 | 13 | import org.junit.Test; 14 | 15 | import java.io.File; 16 | import java.io.IOException; 17 | 18 | public class CipherCompatibilityTest extends AndroidSQLCipherTestCase { 19 | 20 | @Test 21 | public void shouldOpenSQLCipher3Database() { 22 | int count = 0; 23 | database.close(); 24 | File file = null; 25 | try { 26 | file = extractAssetToDatabaseDirectory("sqlcipher-3.0-testkey.db"); 27 | database = SQLiteDatabase.openDatabase(file.getAbsolutePath(), "testkey", null, SQLiteDatabase.OPEN_READWRITE, hook); 28 | Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM sqlite_master;", null); 29 | if(cursor != null && cursor.moveToFirst()){ 30 | count = cursor.getInt(0); 31 | cursor.close(); 32 | } 33 | assertThat(count, greaterThan(0)); 34 | } finally { 35 | delete(file); 36 | } 37 | } 38 | 39 | SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { 40 | public void preKey(SQLiteConnection connection) {} 41 | public void postKey(SQLiteConnection connection) { 42 | connection.execute("PRAGMA cipher_compatibility = 3;", null, null); 43 | } 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/ImportUnencryptedDatabaseTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import android.database.Cursor; 7 | 8 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 9 | 10 | import org.junit.Test; 11 | 12 | import java.io.File; 13 | 14 | public class ImportUnencryptedDatabaseTest extends AndroidSQLCipherTestCase { 15 | 16 | @Test 17 | public void shouldImportUnencryptedPlaintextDatabase(){ 18 | String a = null, b = null; 19 | File unencryptedDatabasePath = null; 20 | File encryptedDatabasePath = context.getDatabasePath("encrypted.db"); 21 | try { 22 | database.close(); 23 | unencryptedDatabasePath = extractAssetToDatabaseDirectory("unencrypted.db"); 24 | database = SQLiteDatabase.openDatabase(unencryptedDatabasePath.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY); 25 | database.execSQL("ATTACH DATABASE ? as encrypted KEY ?;", 26 | new Object[]{encryptedDatabasePath.getAbsolutePath(), "foo"}); 27 | database.rawExecSQL("SELECT sqlcipher_export('encrypted');"); 28 | database.execSQL("DETACH DATABASE encrypted;"); 29 | database.close(); 30 | database = SQLiteDatabase.openDatabase(encryptedDatabasePath.getAbsolutePath(), "foo", 31 | null, SQLiteDatabase.OPEN_READWRITE, null); 32 | Cursor cursor = database.rawQuery("select * from t1", new String[]{}); 33 | if(cursor != null && cursor.moveToFirst()) { 34 | a = cursor.getString(0); 35 | b = cursor.getString(1); 36 | cursor.close(); 37 | } 38 | database.close(); 39 | } catch (Exception ex){ 40 | throw ex; 41 | } finally { 42 | assertThat(a, is("one for the money")); 43 | assertThat(b, is("two for the show")); 44 | delete(unencryptedDatabasePath); 45 | delete(encryptedDatabasePath); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/JsonCastTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import android.database.Cursor; 7 | 8 | import org.junit.Test; 9 | 10 | public class JsonCastTest extends AndroidSQLCipherTestCase { 11 | 12 | @Test 13 | public void shouldExtractUsernameFromQuery(){ 14 | String name = "Bob Smith", queryName = ""; 15 | String query = String.format("select cast(json_extract('{\"user\":\"%s\"}','$.user') as TEXT);", name); 16 | Cursor cursor = database.rawQuery(query); 17 | if(cursor != null && cursor.moveToFirst()){ 18 | queryName = cursor.getString(0); 19 | cursor.close(); 20 | } 21 | assertThat(queryName, is(name)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/MigrateDatabaseFrom1xFormatToCurrentFormatTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.Matchers.is; 4 | import static org.junit.Assert.assertThat; 5 | 6 | import android.database.Cursor; 7 | 8 | import net.zetetic.database.sqlcipher.SQLiteConnection; 9 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 10 | import net.zetetic.database.sqlcipher.SQLiteDatabaseHook; 11 | 12 | import org.junit.Test; 13 | 14 | import java.io.File; 15 | 16 | public class MigrateDatabaseFrom1xFormatToCurrentFormatTest extends AndroidSQLCipherTestCase { 17 | 18 | @Test 19 | public void shouldPerformMigrationFrom1xFileFormatToCurrent() { 20 | File oldDatabaseFileFormatPath = null; 21 | try { 22 | database.close(); 23 | String password = "test", a = "", b = ""; 24 | oldDatabaseFileFormatPath = extractAssetToDatabaseDirectory("sqlcipher-1.x-test.db"); 25 | SQLiteDatabaseHook hook = new SQLiteDatabaseHook() { 26 | public void preKey(SQLiteConnection database) { } 27 | public void postKey(SQLiteConnection database) { 28 | long result = database.executeForLong("PRAGMA cipher_migrate;", null, null); 29 | assertThat("PRAGMA cipher_migrate should return 0 for success", result, is(0L)); 30 | } 31 | }; 32 | database = SQLiteDatabase.openDatabase(oldDatabaseFileFormatPath.getPath(), password, null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY, null, hook); 33 | database.close(); 34 | database = SQLiteDatabase.openDatabase(oldDatabaseFileFormatPath.getPath(), password, null, SQLiteDatabase.OPEN_READWRITE, null, null); 35 | Cursor result = database.rawQuery("select * from t1", new String[]{}); 36 | if (result != null && result.moveToFirst()) { 37 | a = result.getString(0); 38 | b = result.getString(1); 39 | result.close(); 40 | } 41 | database.close(); 42 | assertThat(a, is("one for the money")); 43 | assertThat(b, is("two for the show")); 44 | } finally { 45 | delete(oldDatabaseFileFormatPath); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/README: -------------------------------------------------------------------------------- 1 | 2 | The tests in this directory are modified versions of the SQLite tests in the 3 | Android CTS (Compatibility Test Suite) project. This project is stored in 4 | a git repository here: 5 | 6 | https://android.googlesource.com/platform/cts 7 | 8 | The snapshot was taken from master branch commit id: 9 | 10 | 0794ecacb76b54eeee881762d0edfc83209ab05f 11 | 12 | https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f 13 | 14 | Within that project, the tests are in directory: 15 | 16 | ./tests/tests/database/src/android/database/sqlite/cts/ 17 | 18 | So: 19 | 20 | https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f/tests/tests/database/src/android/database/sqlite/cts 21 | 22 | 23 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/RoomUpsertTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.CoreMatchers.is; 4 | import static org.hamcrest.MatcherAssert.assertThat; 5 | 6 | import android.content.Context; 7 | 8 | import androidx.room.Dao; 9 | import androidx.room.Database; 10 | import androidx.room.Entity; 11 | import androidx.room.PrimaryKey; 12 | import androidx.room.Query; 13 | import androidx.room.Room; 14 | import androidx.room.RoomDatabase; 15 | import androidx.room.Upsert; 16 | import androidx.test.core.app.ApplicationProvider; 17 | import androidx.test.ext.junit.runners.AndroidJUnit4; 18 | 19 | import net.zetetic.database.sqlcipher.SupportOpenHelperFactory; 20 | 21 | import org.junit.Before; 22 | import org.junit.Test; 23 | import org.junit.runner.RunWith; 24 | 25 | import java.io.File; 26 | import java.nio.charset.StandardCharsets; 27 | 28 | @RunWith(AndroidJUnit4.class) 29 | public class RoomUpsertTest { 30 | private UserDatabase database; 31 | private UserDao userDao; 32 | 33 | @Before 34 | public void before(){ 35 | Context context = ApplicationProvider.getApplicationContext(); 36 | File databaseFile = context.getDatabasePath("upsert.db"); 37 | if(databaseFile.exists()){ 38 | databaseFile.delete(); 39 | } 40 | System.loadLibrary("sqlcipher"); 41 | final byte[] passphrase = "user".getBytes(StandardCharsets.UTF_8); 42 | SupportOpenHelperFactory factory = new SupportOpenHelperFactory(passphrase); 43 | database = Room.databaseBuilder(context, UserDatabase.class, databaseFile.getName()) 44 | .openHelperFactory(factory).build(); 45 | userDao = database.userDao(); 46 | } 47 | 48 | @Test 49 | public void shouldAllowUpsertBehavior(){ 50 | User user = new User(); 51 | user.name = "Foo Bar"; 52 | user.age = 41; 53 | user.id = userDao.upsert(user); 54 | user.age = 42; 55 | userDao.upsert(user); 56 | User[] searchUser = userDao.findById(user.id); 57 | assertThat(searchUser[0].age , is(42)); 58 | } 59 | 60 | @Entity 61 | public static class User { 62 | @PrimaryKey(autoGenerate = true) long id; 63 | String name; 64 | int age; 65 | } 66 | 67 | @Dao 68 | public static abstract class UserDao { 69 | @Upsert 70 | abstract long upsert(User user); 71 | @Query("SELECT * FROM user WHERE id=:id") 72 | abstract User[] findById(long id); 73 | } 74 | 75 | @Database(entities = {User.class}, version = 1, exportSchema = false) 76 | public static abstract class UserDatabase extends RoomDatabase { 77 | abstract UserDao userDao(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLCipherOpenHelperTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.Matchers.notNullValue; 4 | import static org.hamcrest.core.Is.is; 5 | import static org.junit.Assert.assertThat; 6 | 7 | import android.content.Context; 8 | import android.util.Log; 9 | 10 | import net.zetetic.database.sqlcipher.SQLiteConnection; 11 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 12 | import net.zetetic.database.sqlcipher.SQLiteDatabaseHook; 13 | import net.zetetic.database.sqlcipher.SQLiteOpenHelper; 14 | import net.zetetic.database.sqlcipher.SQLiteStatement; 15 | 16 | import org.junit.Test; 17 | 18 | import java.io.File; 19 | 20 | public class SQLCipherOpenHelperTest extends AndroidSQLCipherTestCase { 21 | 22 | @Test 23 | public void shouldAccessReadOnlyDatabaseFromOpenHelper(){ 24 | database.close(); 25 | SqlCipherOpenHelper helper = new SqlCipherOpenHelper(context); 26 | SQLiteDatabase db = helper.getReadableDatabase(); 27 | SQLiteStatement statement = db.compileStatement("PRAGMA cipher_provider_version;"); 28 | String version = statement.simpleQueryForString(); 29 | Log.i(TAG, String.format("SQLCipher provider version:%s", version)); 30 | assertThat(db, is(notNullValue())); 31 | } 32 | 33 | @Test 34 | public void shouldSpecifyWALModeFromSQLiteOpenHelperConstructor(){ 35 | database.close(); 36 | File file = extractAssetToDatabaseDirectory("sqlcipher-4-wal-journal.db"); 37 | SqlCipherOpenHelper helper = new SqlCipherOpenHelper(context, file, "test", true); 38 | SQLiteDatabase database = helper.getWritableDatabase(); 39 | SQLiteStatement statement = database.compileStatement("PRAGMA journal_mode;"); 40 | String mode = statement.simpleQueryForString(); 41 | statement.close(); 42 | assertThat(mode, is("wal")); 43 | } 44 | 45 | @Test 46 | public void shouldIgnoreWALModeInDatabaseWhenExplicitlyDisableInConstructor(){ 47 | database.close(); 48 | File file = extractAssetToDatabaseDirectory("sqlcipher-4-wal-journal.db"); 49 | SqlCipherOpenHelper helper = new SqlCipherOpenHelper(context, file, "test", false); 50 | SQLiteDatabase database = helper.getWritableDatabase(); 51 | SQLiteStatement statement = database.compileStatement("PRAGMA journal_mode;"); 52 | String mode = statement.simpleQueryForString(); 53 | statement.close(); 54 | assertThat(mode, is("delete")); 55 | } 56 | 57 | @Test 58 | public void shouldAllowWALModeWhenEnabledViaSettingFromHelperInstance(){ 59 | database.close(); 60 | File file = extractAssetToDatabaseDirectory("sqlcipher-4-wal-journal.db"); 61 | SqlCipherOpenHelper helper = new SqlCipherOpenHelper(context, file, "test", false); 62 | helper.setWriteAheadLoggingEnabled(true); 63 | SQLiteDatabase database = helper.getWritableDatabase(); 64 | SQLiteStatement statement = database.compileStatement("PRAGMA journal_mode;"); 65 | String mode = statement.simpleQueryForString(); 66 | statement.close(); 67 | assertThat(mode, is("wal")); 68 | } 69 | 70 | private class SqlCipherOpenHelper extends SQLiteOpenHelper { 71 | 72 | private final String TAG = getClass().getSimpleName(); 73 | 74 | public SqlCipherOpenHelper(Context context) { 75 | super(context, "test.db", "test", null, 1, 1, (sqLiteDatabase, ex) -> Log.e(SQLCipherOpenHelperTest.this.TAG, "onCorruption()"), new SQLiteDatabaseHook() { 76 | @Override 77 | public void preKey(SQLiteConnection sqLiteConnection) { 78 | Log.d(SQLCipherOpenHelperTest.this.TAG, "preKey()"); 79 | } 80 | 81 | @Override 82 | public void postKey(SQLiteConnection sqLiteConnection) { 83 | Log.d(SQLCipherOpenHelperTest.this.TAG, "postKey()"); 84 | } 85 | }, false); 86 | } 87 | 88 | public SqlCipherOpenHelper(Context context, File file, String password, boolean enableWriteAheadLogging){ 89 | super(context, file.getAbsolutePath(), password, null, 1, 1, null, null, enableWriteAheadLogging); 90 | } 91 | 92 | @Override 93 | public void onCreate(SQLiteDatabase db) { 94 | db.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, value TEXT NOT NULL)"); 95 | } 96 | 97 | @Override 98 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 99 | 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLCipherVersionTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.MatcherAssert.assertThat; 4 | import static org.hamcrest.core.StringContains.containsString; 5 | 6 | import android.database.Cursor; 7 | 8 | import org.junit.Test; 9 | 10 | public class SQLCipherVersionTest extends AndroidSQLCipherTestCase { 11 | 12 | @Test 13 | public void shouldExtractLibraryCipherVersion() { 14 | String cipherVersion = ""; 15 | Cursor cursor = database.rawQuery("PRAGMA cipher_version;"); 16 | if(cursor != null && cursor.moveToFirst()){ 17 | cipherVersion = cursor.getString(0); 18 | cursor.close(); 19 | } 20 | assertThat(cipherVersion, containsString("4.6.1")); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteAbortExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteAbortException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteAbortExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteAbortException(); 32 | 33 | new SQLiteAbortException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteClosableTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertTrue; 21 | import static org.junit.Assert.fail; 22 | 23 | import net.zetetic.database.sqlcipher.SQLiteClosable; 24 | 25 | import androidx.test.ext.junit.runners.AndroidJUnit4; 26 | 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | @RunWith(AndroidJUnit4.class) 31 | @SuppressWarnings("deprecation") 32 | public class SQLiteClosableTest { 33 | private class MockSQLiteClosable extends SQLiteClosable { 34 | private boolean mOnAllReferencesReleasedCalled = false; 35 | private boolean mOnAllReferencesReleasedFromContainerCalled = false; 36 | 37 | @Override 38 | protected void onAllReferencesReleased() { 39 | mOnAllReferencesReleasedCalled = true; 40 | } 41 | 42 | protected void onAllReferencesReleasedFromContainer() { 43 | mOnAllReferencesReleasedFromContainerCalled = true; 44 | } 45 | 46 | public boolean isOnAllReferencesReleasedCalled() { 47 | return mOnAllReferencesReleasedCalled; 48 | } 49 | 50 | public boolean isOnAllReferencesReleasedFromContainerCalled() { 51 | return mOnAllReferencesReleasedFromContainerCalled; 52 | } 53 | } 54 | 55 | @Test 56 | public void testAcquireReference() { 57 | MockSQLiteClosable closable = new MockSQLiteClosable(); 58 | 59 | closable.acquireReference(); 60 | closable.releaseReference(); 61 | 62 | assertFalse(closable.isOnAllReferencesReleasedCalled()); 63 | closable.releaseReference(); 64 | // the reference count is 0 now. 65 | assertTrue(closable.isOnAllReferencesReleasedCalled()); 66 | 67 | try { 68 | closable.acquireReference(); 69 | fail("should throw IllegalStateException."); 70 | } catch (IllegalStateException e) { 71 | } 72 | } 73 | 74 | @Test 75 | @SuppressWarnings("deprecation") 76 | public void testReleaseReferenceFromContainer() { 77 | MockSQLiteClosable closable = new MockSQLiteClosable(); 78 | 79 | closable.acquireReference(); 80 | closable.releaseReferenceFromContainer(); 81 | 82 | // the reference count is 1 now. 83 | assertFalse(closable.isOnAllReferencesReleasedFromContainerCalled()); 84 | closable.releaseReferenceFromContainer(); 85 | // the reference count is 0 now. 86 | assertTrue(closable.isOnAllReferencesReleasedFromContainerCalled()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteConstraintExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteConstraintException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteConstraintExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteConstraintException(); 32 | 33 | new SQLiteConstraintException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteDatabaseCorruptExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteDatabaseCorruptException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteDatabaseCorruptExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteDatabaseCorruptException(); 32 | 33 | new SQLiteDatabaseCorruptException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteDiskIOExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteDiskIOException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteDiskIOExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteDiskIOException(); 32 | 33 | new SQLiteDiskIOException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteDoneExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteDoneException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteDoneExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteDoneException(); 32 | 33 | new SQLiteDoneException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteException(); 32 | 33 | new SQLiteException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteFtsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import android.content.ContentValues; 23 | import android.content.Context; 24 | import android.content.res.Resources; 25 | import android.database.Cursor; 26 | import android.test.AndroidTestCase; 27 | 28 | import androidx.test.core.app.ApplicationProvider; 29 | import androidx.test.ext.junit.runners.AndroidJUnit4; 30 | 31 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 32 | 33 | import org.junit.After; 34 | import org.junit.Before; 35 | import org.junit.Test; 36 | import org.junit.runner.RunWith; 37 | 38 | import java.io.File; 39 | 40 | /** 41 | * Tests to verify FTS3/4 SQLite support. 42 | */ 43 | @RunWith(AndroidJUnit4.class) 44 | public class SQLiteFtsTest { 45 | 46 | private static final String TEST_TABLE = "cts_fts"; 47 | 48 | private static final String[] TEST_CONTENT = { 49 | "Any sufficiently advanced TECHnology is indistinguishable from magic.", 50 | "Those who would give up Essential Liberty to purchase a little Temporary Safety, deserve neither Liberty nor Safety.", 51 | "It is poor civic hygiene to install technologies that could someday facilitate a police state.", 52 | }; 53 | 54 | private SQLiteDatabase mDatabase; 55 | private Context mContext; 56 | 57 | @Before 58 | public void setUp() throws Exception { 59 | mContext = ApplicationProvider.getApplicationContext(); 60 | System.loadLibrary("sqlcipher"); 61 | File f = mContext.getDatabasePath("CTS_FTS"); 62 | f.mkdirs(); 63 | if (f.exists()) { f.delete(); } 64 | mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null); 65 | } 66 | 67 | @After 68 | public void tearDown() throws Exception { 69 | final String path = mDatabase.getPath(); 70 | mDatabase.close(); 71 | SQLiteDatabase.deleteDatabase(new File(path)); 72 | } 73 | 74 | @Test 75 | public void testFts3Porter() throws Exception { 76 | prepareFtsTable(TEST_TABLE, "fts3", "tokenize=porter"); 77 | 78 | // Porter should include stemmed words 79 | final Cursor cursor = queryFtsTable(TEST_TABLE, "technology"); 80 | try { 81 | assertEquals(2, cursor.getCount()); 82 | cursor.moveToPosition(0); 83 | assertTrue(cursor.getString(0).contains(">TECHnology<")); 84 | cursor.moveToPosition(1); 85 | assertTrue(cursor.getString(0).contains(">technologies<")); 86 | } finally { 87 | cursor.close(); 88 | } 89 | } 90 | 91 | @Test 92 | public void testFts3Simple() throws Exception { 93 | prepareFtsTable(TEST_TABLE, "fts3", "tokenize=simple"); 94 | 95 | // Simple shouldn't include stemmed words 96 | final Cursor cursor = queryFtsTable(TEST_TABLE, "technology"); 97 | try { 98 | assertEquals(1, cursor.getCount()); 99 | cursor.moveToPosition(0); 100 | assertTrue(cursor.getString(0).contains(">TECHnology<")); 101 | } finally { 102 | cursor.close(); 103 | } 104 | } 105 | 106 | @Test 107 | public void testFts4Simple() throws Exception { 108 | prepareFtsTable(TEST_TABLE, "fts4", "tokenize=simple"); 109 | 110 | // Simple shouldn't include stemmed words 111 | final Cursor cursor = queryFtsTable(TEST_TABLE, "technology"); 112 | try { 113 | assertEquals(1, cursor.getCount()); 114 | cursor.moveToPosition(0); 115 | assertTrue(cursor.getString(0).contains(">TECHnology<")); 116 | } finally { 117 | cursor.close(); 118 | } 119 | } 120 | 121 | private void prepareFtsTable(String table, String ftsType, String options) 122 | throws Exception { 123 | mDatabase.execSQL( 124 | "CREATE VIRTUAL TABLE " + table + " USING " + ftsType 125 | + "(content TEXT, " + options + ");"); 126 | 127 | final Resources res = mContext.getResources(); 128 | final ContentValues values = new ContentValues(); 129 | for (String content : TEST_CONTENT) { 130 | values.clear(); 131 | values.put("content", content); 132 | mDatabase.insert(table, null, values); 133 | } 134 | } 135 | 136 | private Cursor queryFtsTable(String table, String match) { 137 | return mDatabase.query(table, new String[] { "snippet(" + table + ")" }, 138 | "content MATCH ?", new String[] { match }, 139 | null, null, "rowid ASC"); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteFullExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteFullException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteFullExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteFullException(); 32 | 33 | new SQLiteFullException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteMisuseExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import android.database.sqlite.SQLiteMisuseException; 20 | 21 | import androidx.test.ext.junit.runners.AndroidJUnit4; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | 26 | @RunWith(AndroidJUnit4.class) 27 | public class SQLiteMisuseExceptionTest { 28 | 29 | @Test 30 | public void testConstructor() { 31 | new SQLiteMisuseException(); 32 | 33 | new SQLiteMisuseException("error"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteOpenHelperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertNotNull; 21 | import static org.junit.Assert.assertNotSame; 22 | import static org.junit.Assert.assertSame; 23 | import static org.junit.Assert.assertTrue; 24 | import static org.junit.Assert.fail; 25 | 26 | import android.content.Context; 27 | import android.database.Cursor; 28 | 29 | import androidx.test.core.app.ApplicationProvider; 30 | import androidx.test.ext.junit.runners.AndroidJUnit4; 31 | 32 | import net.zetetic.database.sqlcipher.SQLiteCursor; 33 | import net.zetetic.database.sqlcipher.SQLiteCursorDriver; 34 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 35 | import net.zetetic.database.sqlcipher.SQLiteDatabase.CursorFactory; 36 | import net.zetetic.database.sqlcipher.SQLiteOpenHelper; 37 | import net.zetetic.database.sqlcipher.SQLiteQuery; 38 | 39 | import org.junit.Before; 40 | import org.junit.Test; 41 | import org.junit.runner.RunWith; 42 | 43 | /** 44 | * Test {@link SQLiteOpenHelper}. 45 | */ 46 | @RunWith(AndroidJUnit4.class) 47 | public class SQLiteOpenHelperTest { 48 | private static final String TEST_DATABASE_NAME = "database_test.db"; 49 | static String DATABASE_PATH; 50 | private static final int TEST_VERSION = 1; 51 | private static final int TEST_ILLEGAL_VERSION = 0; 52 | private MockOpenHelper mOpenHelper; 53 | private Context mContext; 54 | private SQLiteDatabase.CursorFactory mFactory = new SQLiteDatabase.CursorFactory() { 55 | public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, 56 | String editTable, SQLiteQuery query) { 57 | return new MockCursor(db, masterQuery, editTable, query); 58 | } 59 | }; 60 | 61 | @Before 62 | public void setUp() throws Exception { 63 | System.loadLibrary("sqlcipher"); 64 | mContext = ApplicationProvider.getApplicationContext(); 65 | DATABASE_PATH = mContext.getDatabasePath(TEST_DATABASE_NAME).toString(); 66 | mOpenHelper = getOpenHelper(); 67 | } 68 | 69 | @Test 70 | public void testConstructor() { 71 | new MockOpenHelper(mContext, DATABASE_PATH, mFactory, TEST_VERSION); 72 | 73 | // Test with illegal version number. 74 | try { 75 | new MockOpenHelper(mContext, DATABASE_PATH, mFactory, TEST_ILLEGAL_VERSION); 76 | fail("Constructor of SQLiteOpenHelp should throws a IllegalArgumentException here."); 77 | } catch (IllegalArgumentException e) { 78 | } 79 | 80 | // Test with null factory 81 | new MockOpenHelper(mContext, DATABASE_PATH, null, TEST_VERSION); 82 | } 83 | 84 | @Test 85 | public void testGetDatabase() { 86 | SQLiteDatabase database = null; 87 | assertFalse(mOpenHelper.hasCalledOnOpen()); 88 | // Test getReadableDatabase. 89 | database = mOpenHelper.getReadableDatabase(); 90 | assertNotNull(database); 91 | assertTrue(database.isOpen()); 92 | assertTrue(mOpenHelper.hasCalledOnOpen()); 93 | 94 | // Database has been opened, so onOpen can not be invoked. 95 | mOpenHelper.resetStatus(); 96 | assertFalse(mOpenHelper.hasCalledOnOpen()); 97 | // Test getWritableDatabase. 98 | SQLiteDatabase database2 = mOpenHelper.getWritableDatabase(); 99 | assertSame(database, database2); 100 | assertTrue(database.isOpen()); 101 | assertFalse(mOpenHelper.hasCalledOnOpen()); 102 | 103 | mOpenHelper.close(); 104 | assertFalse(database.isOpen()); 105 | 106 | // After close(), onOpen() will be invoked by getWritableDatabase. 107 | mOpenHelper.resetStatus(); 108 | assertFalse(mOpenHelper.hasCalledOnOpen()); 109 | SQLiteDatabase database3 = mOpenHelper.getWritableDatabase(); 110 | assertNotNull(database); 111 | assertNotSame(database, database3); 112 | assertTrue(mOpenHelper.hasCalledOnOpen()); 113 | assertTrue(database3.isOpen()); 114 | mOpenHelper.close(); 115 | assertFalse(database3.isOpen()); 116 | } 117 | 118 | private MockOpenHelper getOpenHelper() { 119 | return new MockOpenHelper(mContext, DATABASE_PATH, mFactory, TEST_VERSION); 120 | } 121 | 122 | private class MockOpenHelper extends SQLiteOpenHelper { 123 | private boolean mHasCalledOnOpen = false; 124 | 125 | public MockOpenHelper(Context context, String name, CursorFactory factory, int version) { 126 | super(context, name, factory, version); 127 | } 128 | 129 | @Override 130 | public void onCreate(SQLiteDatabase db) { 131 | } 132 | 133 | @Override 134 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 135 | } 136 | 137 | @Override 138 | public void onOpen(SQLiteDatabase db) { 139 | mHasCalledOnOpen = true; 140 | } 141 | 142 | public boolean hasCalledOnOpen() { 143 | return mHasCalledOnOpen; 144 | } 145 | 146 | public void resetStatus() { 147 | mHasCalledOnOpen = false; 148 | } 149 | } 150 | 151 | @SuppressWarnings("deprecation") 152 | private class MockCursor extends SQLiteCursor { 153 | public MockCursor(SQLiteDatabase db, SQLiteCursorDriver driver, String editTable, 154 | SQLiteQuery query) { 155 | super(db, driver, editTable, query); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SQLiteQueryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database.sqlcipher_cts; 18 | 19 | import junit.framework.TestCase; 20 | 21 | public class SQLiteQueryTest extends TestCase { 22 | public void testMethods() { 23 | // cannot obtain an instance of SQLiteQuery 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SupportAPIRoomTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.hamcrest.CoreMatchers.not; 4 | import static org.hamcrest.Matchers.is; 5 | import static org.junit.Assert.assertThat; 6 | 7 | import android.content.Context; 8 | import android.database.Cursor; 9 | 10 | import androidx.room.ColumnInfo; 11 | import androidx.room.Dao; 12 | import androidx.room.Database; 13 | import androidx.room.Delete; 14 | import androidx.room.Entity; 15 | import androidx.room.Insert; 16 | import androidx.room.OnConflictStrategy; 17 | import androidx.room.PrimaryKey; 18 | import androidx.room.Query; 19 | import androidx.room.Room; 20 | import androidx.room.RoomDatabase; 21 | import androidx.sqlite.db.SupportSQLiteDatabase; 22 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 23 | import androidx.test.core.app.ApplicationProvider; 24 | import androidx.test.ext.junit.runners.AndroidJUnit4; 25 | 26 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 27 | import net.zetetic.database.sqlcipher.SupportOpenHelperFactory; 28 | 29 | import org.junit.After; 30 | import org.junit.Before; 31 | import org.junit.Test; 32 | import org.junit.runner.RunWith; 33 | 34 | import java.io.File; 35 | import java.nio.charset.StandardCharsets; 36 | import java.util.List; 37 | import java.util.UUID; 38 | 39 | @RunWith(AndroidJUnit4.class) 40 | public class SupportAPIRoomTest { 41 | 42 | private AppDatabase db; 43 | private UserDao userDao; 44 | private File databaseFile; 45 | 46 | @Before 47 | public void before(){ 48 | Context context = ApplicationProvider.getApplicationContext(); 49 | System.loadLibrary("sqlcipher"); 50 | databaseFile = context.getDatabasePath("users.db"); 51 | if(databaseFile.exists()){ 52 | databaseFile.delete(); 53 | } 54 | SupportOpenHelperFactory factory = new SupportOpenHelperFactory("user".getBytes(StandardCharsets.UTF_8)); 55 | db = Room.databaseBuilder(context, AppDatabase.class, databaseFile.getAbsolutePath()) 56 | .openHelperFactory(factory).build(); 57 | db.clearAllTables(); 58 | userDao = db.userDao(); 59 | } 60 | 61 | @Test 62 | public void shouldInsertDataViaDao(){ 63 | User user = new User("John", "Doe"); 64 | user.uid = userDao.insert(user); 65 | assertThat(user.uid, is(not(0))); 66 | } 67 | 68 | @Test 69 | public void shouldDeleteDataViaDao(){ 70 | User user = new User("foo", "bar"); 71 | user.uid = 1; 72 | SupportSQLiteOpenHelper helper = db.getOpenHelper(); 73 | SupportSQLiteDatabase database = helper.getWritableDatabase(); 74 | database.execSQL("insert into user values(?,?,?);", 75 | new Object[]{user.uid, user.firstName, user.lastName}); 76 | Cursor cursor = database.query("select count(*) from user;"); 77 | cursor.moveToFirst(); 78 | boolean userInserted = cursor.getInt(0) > 0; 79 | assertThat(userInserted, is(true)); 80 | userDao.delete(user); 81 | cursor = database.query("select count(*) from user;"); 82 | cursor.moveToFirst(); 83 | int existingUsers = cursor.getInt(0); 84 | cursor.close(); 85 | assertThat(existingUsers, is(0)); 86 | } 87 | 88 | @Test 89 | public void shouldQueryDataByParametersViaDao(){ 90 | User user = new User("foo", "bar"); 91 | user.uid = 1; 92 | SupportSQLiteOpenHelper helper = db.getOpenHelper(); 93 | SupportSQLiteDatabase database = helper.getWritableDatabase(); 94 | database.execSQL("insert into user values(?,?,?);", 95 | new Object[]{user.uid, user.firstName, user.lastName}); 96 | User foundUser = userDao.findByName(user.firstName, user.lastName); 97 | assertThat(foundUser.uid, is(user.uid)); 98 | assertThat(foundUser.firstName, is(user.firstName)); 99 | assertThat(foundUser.lastName, is(user.lastName)); 100 | } 101 | 102 | @Test 103 | public void shouldSupportChangingPasswordWithRoom(){ 104 | userDao.insert(new User("foo", "bar")); 105 | SQLiteDatabase database = (SQLiteDatabase)db.getOpenHelper().getWritableDatabase(); 106 | database.changePassword(UUID.randomUUID().toString()); 107 | List users = userDao.getAll(); 108 | assertThat(users.size(), is(1)); 109 | } 110 | 111 | @After 112 | public void after(){ 113 | if(db != null){ 114 | db.close(); 115 | if(databaseFile != null){ 116 | databaseFile.delete(); 117 | } 118 | } 119 | } 120 | 121 | @Database(entities = {User.class}, version = 1, exportSchema = false) 122 | public abstract static class AppDatabase extends RoomDatabase { 123 | public abstract UserDao userDao(); 124 | } 125 | 126 | @Entity 127 | public static class User { 128 | @PrimaryKey(autoGenerate = true) 129 | public long uid; 130 | @ColumnInfo(name = "first_name") 131 | public String firstName; 132 | @ColumnInfo(name = "last_name") 133 | public String lastName; 134 | 135 | public User(String firstName, String lastName) { 136 | this.firstName = firstName; 137 | this.lastName = lastName; 138 | } 139 | } 140 | 141 | @Dao 142 | public interface UserDao { 143 | @Query("SELECT * FROM user") 144 | List getAll(); 145 | 146 | @Query("SELECT * FROM user WHERE uid IN (:userIds)") 147 | List loadAllByIds(int[] userIds); 148 | 149 | @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + 150 | "last_name LIKE :last LIMIT 1") 151 | User findByName(String first, String last); 152 | 153 | @Insert(onConflict = OnConflictStrategy.REPLACE) 154 | long insert(User user); 155 | 156 | @Delete 157 | void delete(User user); 158 | 159 | @Query("DELETE FROM user;") 160 | void deleteAll(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /sqlcipher/src/androidTest/java/net/zetetic/database/sqlcipher_cts/SupportHelperTest.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher_cts; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.annotation.CallSuper; 8 | import androidx.annotation.NonNull; 9 | import androidx.sqlite.db.SupportSQLiteDatabase; 10 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 11 | import androidx.test.ext.junit.runners.AndroidJUnit4; 12 | import androidx.test.platform.app.InstrumentationRegistry; 13 | 14 | import net.zetetic.database.sqlcipher.SupportHelper; 15 | 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.junit.runner.RunWith; 19 | 20 | @RunWith(AndroidJUnit4.class) 21 | public class SupportHelperTest { 22 | 23 | private static final String DATABASE_NAME = "DB-Test.db"; 24 | private static final int CREATION_INDEX = 0; 25 | private static final int UPGRADE_INDEX = 1; 26 | 27 | @Before 28 | public void setup() { 29 | Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 30 | System.loadLibrary("sqlcipher"); 31 | for (String databaseName : context.databaseList()) { 32 | context.deleteDatabase(databaseName); 33 | } 34 | } 35 | 36 | @Test 37 | public void shouldCreateDatabaseNormallyWithInitialVersion() { 38 | FakeCallback callbackWrapper = new FakeCallback(1); 39 | 40 | SupportSQLiteOpenHelper.Configuration configuration = createConfiguration(callbackWrapper); 41 | SupportHelper helper = new SupportHelper(configuration, null, null, true); 42 | 43 | helper.getWritableDatabase(); 44 | helper.close(); 45 | 46 | assertEquals(1, callbackWrapper.callbackCount[CREATION_INDEX]); 47 | assertEquals(0, callbackWrapper.callbackCount[UPGRADE_INDEX]); 48 | } 49 | 50 | @Test 51 | public void shouldRunUpgradeFromVersion1ToVersion2WhenMinSupportedVersionIsProvided() { 52 | FakeCallback initialCallback = new FakeCallback(1); 53 | 54 | SupportHelper initialHelper = new SupportHelper(createConfiguration(initialCallback), null, null, true); 55 | 56 | initialHelper.getWritableDatabase(); 57 | initialHelper.close(); 58 | 59 | assertEquals(1, initialCallback.callbackCount[CREATION_INDEX]); 60 | assertEquals(0, initialCallback.callbackCount[UPGRADE_INDEX]); 61 | 62 | FakeCallback callbackWrapper = new FakeCallback(2); 63 | 64 | // minSupportedVersion = 1 65 | SupportHelper helper = new SupportHelper(createConfiguration(callbackWrapper), null, null, true, 1); 66 | 67 | helper.getWritableDatabase(); 68 | helper.close(); 69 | 70 | assertEquals(0, callbackWrapper.callbackCount[CREATION_INDEX]); 71 | assertEquals(1, callbackWrapper.callbackCount[UPGRADE_INDEX]); 72 | } 73 | 74 | @Test 75 | public void shouldRunUpgradeFromVersion1ToVersion2() { 76 | FakeCallback initialCallback = new FakeCallback(1); 77 | 78 | SupportHelper initialHelper = new SupportHelper(createConfiguration(initialCallback), null, null, true); 79 | 80 | initialHelper.getWritableDatabase(); 81 | initialHelper.close(); 82 | 83 | assertEquals(1, initialCallback.callbackCount[CREATION_INDEX]); 84 | assertEquals(0, initialCallback.callbackCount[UPGRADE_INDEX]); 85 | 86 | FakeCallback callbackWrapper = new FakeCallback(2); 87 | 88 | SupportHelper helper = new SupportHelper(createConfiguration(callbackWrapper), null, null, true); 89 | 90 | helper.getWritableDatabase(); 91 | helper.close(); 92 | 93 | assertEquals(0, callbackWrapper.callbackCount[CREATION_INDEX]); 94 | assertEquals(1, callbackWrapper.callbackCount[UPGRADE_INDEX]); 95 | } 96 | 97 | private SupportSQLiteOpenHelper.Configuration createConfiguration(SupportSQLiteOpenHelper.Callback callback) { 98 | Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 99 | return SupportSQLiteOpenHelper.Configuration.builder(context) 100 | .name(DATABASE_NAME) 101 | .callback(callback) 102 | .build(); 103 | } 104 | 105 | private static class FakeCallback extends SupportSQLiteOpenHelper.Callback { 106 | public final int[] callbackCount = {0, 0}; 107 | 108 | public FakeCallback(int version) { 109 | super(version); 110 | } 111 | 112 | SupportSQLiteOpenHelper.Callback callback = new SupportSQLiteOpenHelper.Callback(version) { 113 | @Override 114 | public void onCreate(@NonNull SupportSQLiteDatabase db) { 115 | callbackCount[CREATION_INDEX] += 1; 116 | } 117 | 118 | @Override 119 | public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion, int newVersion) { 120 | callbackCount[UPGRADE_INDEX] += 1; 121 | } 122 | }; 123 | 124 | @Override 125 | @CallSuper 126 | public void onCreate(@NonNull SupportSQLiteDatabase db) { 127 | callback.onCreate(db); 128 | } 129 | 130 | @Override 131 | @CallSuper 132 | public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion, int newVersion) { 133 | callback.onUpgrade(db, oldVersion, newVersion); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /sqlcipher/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/AbstractWindowedCursor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // modified from original source see README at the top level of this project 17 | 18 | package net.zetetic.database; 19 | 20 | import android.database.CharArrayBuffer; 21 | import android.database.Cursor; 22 | import android.database.StaleDataException; 23 | 24 | /** 25 | * A base class for Cursors that store their data in {@link android.database.CursorWindow}s. 26 | *

27 | * The cursor owns the cursor window it uses. When the cursor is closed, 28 | * its window is also closed. Likewise, when the window used by the cursor is 29 | * changed, its old window is closed. This policy of strict ownership ensures 30 | * that cursor windows are not leaked. 31 | *

32 | * Subclasses are responsible for filling the cursor window with data during 33 | * {@link #onMove(int, int)}, allocating a new cursor window if necessary. 34 | * During {@link #requery()}, the existing cursor window should be cleared and 35 | * filled with new data. 36 | *

37 | * If the contents of the cursor change or become invalid, the old window must be closed 38 | * (because it is owned by the cursor) and set to null. 39 | *

40 | */ 41 | @SuppressWarnings("unused") 42 | public abstract class AbstractWindowedCursor extends AbstractCursor { 43 | /** 44 | * The cursor window owned by this cursor. 45 | */ 46 | protected CursorWindow mWindow; 47 | 48 | @Override 49 | public byte[] getBlob(int columnIndex) { 50 | checkPosition(); 51 | return mWindow.getBlob(mPos, columnIndex); 52 | } 53 | 54 | @Override 55 | public String getString(int columnIndex) { 56 | checkPosition(); 57 | return mWindow.getString(mPos, columnIndex); 58 | } 59 | 60 | @Override 61 | public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { 62 | mWindow.copyStringToBuffer(mPos, columnIndex, buffer); 63 | } 64 | 65 | @Override 66 | public short getShort(int columnIndex) { 67 | checkPosition(); 68 | return mWindow.getShort(mPos, columnIndex); 69 | } 70 | 71 | @Override 72 | public int getInt(int columnIndex) { 73 | checkPosition(); 74 | return mWindow.getInt(mPos, columnIndex); 75 | } 76 | 77 | @Override 78 | public long getLong(int columnIndex) { 79 | checkPosition(); 80 | return mWindow.getLong(mPos, columnIndex); 81 | } 82 | 83 | @Override 84 | public float getFloat(int columnIndex) { 85 | checkPosition(); 86 | return mWindow.getFloat(mPos, columnIndex); 87 | } 88 | 89 | @Override 90 | public double getDouble(int columnIndex) { 91 | checkPosition(); 92 | return mWindow.getDouble(mPos, columnIndex); 93 | } 94 | 95 | @Override 96 | public boolean isNull(int columnIndex) { 97 | return mWindow.getType(mPos, columnIndex) == Cursor.FIELD_TYPE_NULL; 98 | } 99 | 100 | @Override 101 | public int getType(int columnIndex) { 102 | return mWindow.getType(mPos, columnIndex); 103 | } 104 | 105 | @Override 106 | protected void checkPosition() { 107 | super.checkPosition(); 108 | if (mWindow == null) { 109 | throw new StaleDataException("Attempting to access a closed CursorWindow." + 110 | "Most probable cause: cursor is deactivated prior to calling this method."); 111 | } 112 | } 113 | 114 | public CursorWindow getWindow() { 115 | return mWindow; 116 | } 117 | 118 | /** 119 | * Sets a new cursor window for the cursor to use. 120 | *

121 | * The cursor takes ownership of the provided cursor window; the cursor window 122 | * will be closed when the cursor is closed or when the cursor adopts a new 123 | * cursor window. 124 | *

125 | * If the cursor previously had a cursor window, then it is closed when the 126 | * new cursor window is assigned. 127 | *

128 | * 129 | * @param window The new cursor window, typically a remote cursor window. 130 | */ 131 | public void setWindow(CursorWindow window) { 132 | if (window != mWindow) { 133 | closeWindow(); 134 | mWindow = window; 135 | } 136 | } 137 | 138 | /** 139 | * Returns true if the cursor has an associated cursor window. 140 | * 141 | * @return True if the cursor has an associated cursor window. 142 | */ 143 | public boolean hasWindow() { 144 | return mWindow != null; 145 | } 146 | 147 | /** 148 | * Closes the cursor window and sets {@link #mWindow} to null. 149 | * @hide 150 | */ 151 | protected void closeWindow() { 152 | if (mWindow != null) { 153 | mWindow.close(); 154 | mWindow = null; 155 | } 156 | } 157 | 158 | /** 159 | * If there is a window, clear it. Otherwise, creates a new window. 160 | * 161 | * @param name The window name. 162 | * @hide 163 | */ 164 | protected void clearOrCreateWindow(String name) { 165 | if (mWindow == null) { 166 | mWindow = new CursorWindow(name); 167 | } else { 168 | mWindow.clear(); 169 | } 170 | } 171 | 172 | @Override 173 | protected void onDeactivateOrClose() { 174 | super.onDeactivateOrClose(); 175 | closeWindow(); 176 | } 177 | } -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/CursorWindowAllocationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.zetetic.database; 18 | 19 | /** 20 | * This exception is thrown when a CursorWindow couldn't be allocated, 21 | * most probably due to memory not being available. 22 | * 23 | * @hide 24 | */ 25 | public class CursorWindowAllocationException extends RuntimeException { 26 | public CursorWindowAllocationException(String description) { 27 | super(description); 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/DatabaseErrorHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | 22 | package net.zetetic.database; 23 | 24 | import android.database.sqlite.SQLiteException; 25 | 26 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 27 | 28 | /** 29 | * An interface to let apps define an action to take when database corruption is detected. 30 | */ 31 | public interface DatabaseErrorHandler { 32 | 33 | /** 34 | * The method invoked when database corruption is detected. 35 | * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption 36 | * is detected. 37 | * @param exception the exception reported by sqlite that indicated the database was corrupted. 38 | */ 39 | void onCorruption(SQLiteDatabase dbObj, SQLiteException exception); 40 | } 41 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/DefaultDatabaseErrorHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database; 22 | 23 | import java.io.File; 24 | import java.util.List; 25 | 26 | import net.zetetic.database.sqlcipher.SQLiteDatabase; 27 | 28 | import android.database.sqlite.SQLiteException; 29 | import android.util.Pair; 30 | 31 | /** 32 | * Default class used to define the action to take when database corruption is reported 33 | * by sqlite. 34 | *

35 | * An application can specify an implementation of {@link DatabaseErrorHandler} on the 36 | * following: 37 | *

    38 | *
  • {@link SQLiteDatabase#openOrCreateDatabase(String, 39 | * android.database.sqlite.SQLiteDatabase.CursorFactory, DatabaseErrorHandler)}
  • 40 | *
  • {@link SQLiteDatabase#openDatabase(String, 41 | * android.database.sqlite.SQLiteDatabase.CursorFactory, int, DatabaseErrorHandler)}
  • 42 | *
43 | * The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they 44 | * occur. 45 | *

46 | * If null is specified for the DatabaseErrorHandler param in the above calls, this class is used 47 | * as the default {@link DatabaseErrorHandler}. 48 | */ 49 | public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler { 50 | 51 | private static final String TAG = "DefaultDatabaseErrorHandler"; 52 | 53 | /** 54 | * defines the default method to be invoked when database corruption is detected. 55 | * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption 56 | * is detected. 57 | */ 58 | public void onCorruption(SQLiteDatabase dbObj, SQLiteException exception) { 59 | Logger.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath()); 60 | 61 | // If this is a SEE build, do not delete any database files. 62 | // It may be that the user has specified an incorrect password. 63 | if( SQLiteDatabase.hasCodec() ) return; 64 | 65 | // is the corruption detected even before database could be 'opened'? 66 | if (!dbObj.isOpen()) { 67 | // database files are not even openable. delete this database file. 68 | // NOTE if the database has attached databases, then any of them could be corrupt. 69 | // and not deleting all of them could cause corrupted database file to remain and 70 | // make the application crash on database open operation. To avoid this problem, 71 | // the application should provide its own {@link DatabaseErrorHandler} impl class 72 | // to delete ALL files of the database (including the attached databases). 73 | deleteDatabaseFile(dbObj.getPath()); 74 | return; 75 | } 76 | 77 | List> attachedDbs = null; 78 | try { 79 | // Close the database, which will cause subsequent operations to fail. 80 | // before that, get the attached database list first. 81 | try { 82 | attachedDbs = dbObj.getAttachedDbs(); 83 | } catch (SQLiteException e) { 84 | /* ignore */ 85 | } 86 | try { 87 | dbObj.close(); 88 | } catch (SQLiteException e) { 89 | /* ignore */ 90 | } 91 | } finally { 92 | // Delete all files of this corrupt database and/or attached databases 93 | if (attachedDbs != null) { 94 | for (Pair p : attachedDbs) { 95 | deleteDatabaseFile(p.second); 96 | } 97 | } else { 98 | // attachedDbs = null is possible when the database is so corrupt that even 99 | // "PRAGMA database_list;" also fails. delete the main database file 100 | deleteDatabaseFile(dbObj.getPath()); 101 | } 102 | } 103 | } 104 | 105 | private void deleteDatabaseFile(String fileName) { 106 | if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) { 107 | return; 108 | } 109 | Logger.e(TAG, "deleting the database file: " + fileName); 110 | try { 111 | SQLiteDatabase.deleteDatabase(new File(fileName)); 112 | } catch (Exception e) { 113 | /* print warning and ignore exception */ 114 | Logger.w(TAG, "delete failed: " + e.getMessage()); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/LogTarget.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database; 2 | 3 | public interface LogTarget { 4 | boolean isLoggable (String tag, int priority); 5 | void log(int priority, String tag, String message, Throwable throwable); 6 | } -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/LogcatTarget.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database; 2 | 3 | import android.util.Log; 4 | 5 | public class LogcatTarget implements LogTarget { 6 | 7 | @Override 8 | public boolean isLoggable(String tag, int priority) { 9 | return Log.isLoggable(tag, priority); 10 | } 11 | 12 | public void log(int priority, String tag, String message, Throwable throwable){ 13 | switch (priority){ 14 | case Logger.VERBOSE -> Log.v(tag, message, throwable); 15 | case Logger.DEBUG -> Log.d(tag, message, throwable); 16 | case Logger.INFO -> Log.i(tag, message, throwable); 17 | case Logger.WARN -> Log.w(tag, message, throwable); 18 | case Logger.ERROR -> Log.e(tag, message, throwable); 19 | case Logger.ASSERT -> Log.wtf(tag, message, throwable); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/Logger.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database; 2 | 3 | public class Logger { 4 | 5 | static { 6 | setTarget(new LogcatTarget()); 7 | } 8 | 9 | public static final int VERBOSE = 2; 10 | public static final int DEBUG = 3; 11 | public static final int INFO = 4; 12 | public static final int WARN = 5; 13 | public static final int ERROR = 6; 14 | public static final int ASSERT = 7; 15 | 16 | private static LogTarget target; 17 | 18 | public static void setTarget(LogTarget target){ 19 | Logger.target = target; 20 | } 21 | 22 | private static LogTarget getTarget(){ 23 | if(Logger.target == null){ 24 | setTarget(new NoopTarget()); 25 | } 26 | return Logger.target; 27 | } 28 | 29 | public static boolean isLoggable(String tag, int priority){ 30 | return getTarget().isLoggable(tag, priority); 31 | } 32 | 33 | public static void i(String tag, String message) { 34 | getTarget().log(Logger.INFO, tag, message, null); 35 | } 36 | 37 | public static void i(String tag, String message, Throwable throwable) { 38 | getTarget().log(Logger.INFO, tag, message, throwable); 39 | } 40 | 41 | public static void d(String tag, String message) { 42 | getTarget().log(Logger.DEBUG, tag, message, null); 43 | } 44 | 45 | public static void d(String tag, String message, Throwable throwable) { 46 | getTarget().log(Logger.DEBUG, tag, message, throwable); 47 | } 48 | 49 | public static void e(String tag, String message) { 50 | getTarget().log(Logger.ERROR, tag, message, null); 51 | } 52 | 53 | public static void e(String tag, String message, Throwable throwable) { 54 | getTarget().log(Logger.ERROR, tag, message, throwable); 55 | } 56 | 57 | public static void v(String tag, String message) { 58 | getTarget().log(Logger.VERBOSE, tag, message, null); 59 | } 60 | 61 | public static void v(String tag, String message, Throwable throwable) { 62 | getTarget().log(Logger.VERBOSE, tag, message, throwable); 63 | } 64 | 65 | public static void w(String tag, String message) { 66 | getTarget().log(Logger.WARN, tag, message, null); 67 | } 68 | 69 | public static void w(String tag, String message, Throwable throwable) { 70 | getTarget().log(Logger.WARN, tag, message, throwable); 71 | } 72 | 73 | public static void wtf(String tag, String message) { 74 | getTarget().log(Logger.ASSERT, tag, message, null); 75 | } 76 | 77 | public static void wtf(String tag, String message, Throwable throwable) { 78 | getTarget().log(Logger.ASSERT, tag, message, throwable); 79 | } 80 | } -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/NoopTarget.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database; 2 | 3 | public class NoopTarget implements LogTarget { 4 | 5 | @Override 6 | public boolean isLoggable(String tag, int priority) { 7 | return false; 8 | } 9 | 10 | @Override 11 | public void log(int priority, String tag, String message, Throwable throwable) {} 12 | } 13 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/DatabaseObjectNotClosedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | /** 24 | * An exception that indicates that garbage-collector is finalizing a database object 25 | * that is not explicitly closed 26 | * @hide 27 | */ 28 | public class DatabaseObjectNotClosedException extends RuntimeException { 29 | private static final String s = "Application did not close the cursor or database object " + 30 | "that was opened here"; 31 | 32 | public DatabaseObjectNotClosedException() { 33 | super(s); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteClosable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | import java.io.Closeable; 24 | 25 | /** 26 | * An object created from a SQLiteDatabase that can be closed. 27 | * 28 | * This class implements a primitive reference counting scheme for database objects. 29 | */ 30 | public abstract class SQLiteClosable implements Closeable { 31 | private int mReferenceCount = 1; 32 | 33 | /** 34 | * Called when the last reference to the object was released by 35 | * a call to {@link #releaseReference()} or {@link #close()}. 36 | */ 37 | protected abstract void onAllReferencesReleased(); 38 | 39 | /** 40 | * Called when the last reference to the object was released by 41 | * a call to {@link #releaseReferenceFromContainer()}. 42 | * 43 | * @deprecated Do not use. 44 | */ 45 | @Deprecated 46 | protected void onAllReferencesReleasedFromContainer() { 47 | onAllReferencesReleased(); 48 | } 49 | 50 | /** 51 | * Acquires a reference to the object. 52 | * 53 | * @throws IllegalStateException if the last reference to the object has already 54 | * been released. 55 | */ 56 | public void acquireReference() { 57 | synchronized(this) { 58 | if (mReferenceCount <= 0) { 59 | throw new IllegalStateException( 60 | "attempt to re-open an already-closed object: " + this); 61 | } 62 | mReferenceCount++; 63 | } 64 | } 65 | 66 | /** 67 | * Releases a reference to the object, closing the object if the last reference 68 | * was released. 69 | * 70 | * @see #onAllReferencesReleased() 71 | */ 72 | public void releaseReference() { 73 | boolean refCountIsZero = false; 74 | synchronized(this) { 75 | refCountIsZero = --mReferenceCount == 0; 76 | } 77 | if (refCountIsZero) { 78 | onAllReferencesReleased(); 79 | } 80 | } 81 | 82 | /** 83 | * Releases a reference to the object that was owned by the container of the object, 84 | * closing the object if the last reference was released. 85 | * 86 | * @see #onAllReferencesReleasedFromContainer() 87 | * @deprecated Do not use. 88 | */ 89 | @Deprecated 90 | public void releaseReferenceFromContainer() { 91 | boolean refCountIsZero = false; 92 | synchronized(this) { 93 | refCountIsZero = --mReferenceCount == 0; 94 | } 95 | if (refCountIsZero) { 96 | onAllReferencesReleasedFromContainer(); 97 | } 98 | } 99 | 100 | /** 101 | * Releases a reference to the object, closing the object if the last reference 102 | * was released. 103 | * 104 | * Calling this method is equivalent to calling {@link #releaseReference}. 105 | * 106 | * @see #releaseReference() 107 | * @see #onAllReferencesReleased() 108 | */ 109 | public void close() { 110 | releaseReference(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteCursorDriver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | import android.database.Cursor; 24 | import net.zetetic.database.sqlcipher.SQLiteDatabase.CursorFactory; 25 | 26 | /** 27 | * A driver for SQLiteCursors that is used to create them and gets notified 28 | * by the cursors it creates on significant events in their lifetimes. 29 | */ 30 | public interface SQLiteCursorDriver { 31 | /** 32 | * Executes the query returning a Cursor over the result set. 33 | * 34 | * @param factory The CursorFactory to use when creating the Cursors, or 35 | * null if standard SQLiteCursors should be returned. 36 | * @return a Cursor over the result set 37 | */ 38 | Cursor query(CursorFactory factory, String[] bindArgs); 39 | 40 | /** 41 | * Called by a SQLiteCursor when it is released. 42 | */ 43 | void cursorDeactivated(); 44 | 45 | /** 46 | * Called by a SQLiteCursor when it is requeried. 47 | */ 48 | void cursorRequeried(Cursor cursor); 49 | 50 | /** 51 | * Called by a SQLiteCursor when it it closed to destroy this object as well. 52 | */ 53 | void cursorClosed(); 54 | 55 | /** 56 | * Set new bind arguments. These will take effect in cursorRequeried(). 57 | * @param bindArgs the new arguments 58 | */ 59 | public void setBindArguments(String[] bindArgs); 60 | } 61 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteCustomFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | /** 24 | * Describes a custom SQL function. 25 | * 26 | * @hide 27 | */ 28 | public final class SQLiteCustomFunction { 29 | public final String name; 30 | public final int numArgs; 31 | public final SQLiteDatabase.CustomFunction callback; 32 | 33 | /** 34 | * Create custom function. 35 | * 36 | * @param name The name of the sqlite3 function. 37 | * @param numArgs The number of arguments for the function, or -1 to 38 | * support any number of arguments. 39 | * @param callback The callback to invoke when the function is executed. 40 | */ 41 | public SQLiteCustomFunction(String name, int numArgs, 42 | SQLiteDatabase.CustomFunction callback) { 43 | if (name == null) { 44 | throw new IllegalArgumentException("name must not be null."); 45 | } 46 | 47 | this.name = name; 48 | this.numArgs = numArgs; 49 | this.callback = callback; 50 | } 51 | 52 | // Called from native. 53 | @SuppressWarnings("unused") 54 | private void dispatchCallback(String[] args) { 55 | callback.callback(args); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDatabaseHook.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher; 2 | 3 | public interface SQLiteDatabaseHook { 4 | /** 5 | * Called immediately before keying the database. 6 | */ 7 | void preKey(SQLiteConnection connection); 8 | /** 9 | * Called immediately after keying the database. 10 | */ 11 | void postKey(SQLiteConnection connection); 12 | } 13 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDebug.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | import java.util.ArrayList; 24 | 25 | import android.util.Printer; 26 | 27 | import net.zetetic.database.Logger; 28 | 29 | /** 30 | * Provides debugging info about all SQLite databases running in the current process. 31 | * 32 | * {@hide} 33 | */ 34 | public final class SQLiteDebug { 35 | private static native void nativeGetPagerStats(PagerStats stats); 36 | 37 | /** 38 | * Controls the printing of informational SQL log messages. 39 | * 40 | * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE". 41 | */ 42 | public static final boolean DEBUG_SQL_LOG = 43 | Logger.isLoggable("SQLiteLog", Logger.VERBOSE); 44 | 45 | /** 46 | * Controls the printing of SQL statements as they are executed. 47 | * 48 | * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE". 49 | */ 50 | public static final boolean DEBUG_SQL_STATEMENTS = 51 | Logger.isLoggable("SQLiteStatements", Logger.VERBOSE); 52 | 53 | /** 54 | * Controls the printing of wall-clock time taken to execute SQL statements 55 | * as they are executed. 56 | * 57 | * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE". 58 | */ 59 | public static final boolean DEBUG_SQL_TIME = 60 | Logger.isLoggable("SQLiteTime", Logger.VERBOSE); 61 | 62 | /** 63 | * True to enable database performance testing instrumentation. 64 | * @hide 65 | */ 66 | public static final boolean DEBUG_LOG_SLOW_QUERIES = false; 67 | 68 | private SQLiteDebug() { 69 | } 70 | 71 | /** 72 | * Determines whether a query should be logged. 73 | * 74 | * Reads the "db.log.slow_query_threshold" system property, which can be changed 75 | * by the user at any time. If the value is zero, then all queries will 76 | * be considered slow. If the value does not exist or is negative, then no queries will 77 | * be considered slow. 78 | * 79 | * This value can be changed dynamically while the system is running. 80 | * For example, "adb shell setprop db.log.slow_query_threshold 200" will 81 | * log all queries that take 200ms or longer to run. 82 | * @hide 83 | */ 84 | public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) { 85 | int slowQueryMillis = Integer.parseInt( 86 | System.getProperty("db.log.slow_query_threshold", "10000") 87 | ); 88 | return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis; 89 | } 90 | 91 | /** 92 | * Contains statistics about the active pagers in the current process. 93 | * 94 | * @see #nativeGetPagerStats(PagerStats) 95 | */ 96 | public static class PagerStats { 97 | /** the current amount of memory checked out by sqlite using sqlite3_malloc(). 98 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 99 | */ 100 | public int memoryUsed; 101 | 102 | /** the number of bytes of page cache allocation which could not be sattisfied by the 103 | * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc(). 104 | * The returned value includes allocations that overflowed because they where too large 105 | * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations 106 | * that overflowed because no space was left in the page cache. 107 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 108 | */ 109 | public int pageCacheOverflow; 110 | 111 | /** records the largest memory allocation request handed to sqlite3. 112 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 113 | */ 114 | public int largestMemAlloc; 115 | 116 | /** a list of {@link DbStats} - one for each main database opened by the applications 117 | * running on the android device 118 | */ 119 | public ArrayList dbStats; 120 | } 121 | 122 | /** 123 | * contains statistics about a database 124 | */ 125 | public static class DbStats { 126 | /** name of the database */ 127 | public String dbName; 128 | 129 | /** the page size for the database */ 130 | public long pageSize; 131 | 132 | /** the database size */ 133 | public long dbSize; 134 | 135 | /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */ 136 | public int lookaside; 137 | 138 | /** statement cache stats: hits/misses/cachesize */ 139 | public String cache; 140 | 141 | public DbStats(String dbName, long pageCount, long pageSize, int lookaside, 142 | int hits, int misses, int cachesize) { 143 | this.dbName = dbName; 144 | this.pageSize = pageSize / 1024; 145 | dbSize = (pageCount * pageSize) / 1024; 146 | this.lookaside = lookaside; 147 | this.cache = hits + "/" + misses + "/" + cachesize; 148 | } 149 | } 150 | 151 | /** 152 | * return all pager and database stats for the current process. 153 | * @return {@link PagerStats} 154 | */ 155 | public static PagerStats getDatabaseInfo() { 156 | PagerStats stats = new PagerStats(); 157 | nativeGetPagerStats(stats); 158 | stats.dbStats = SQLiteDatabase.getDbStats(); 159 | return stats; 160 | } 161 | 162 | /** 163 | * Dumps detailed information about all databases used by the process. 164 | * @param printer The printer for dumping database state. 165 | * @param args Command-line arguments supplied to dumpsys dbinfo 166 | */ 167 | public static void dump(Printer printer, String[] args) { 168 | boolean verbose = false; 169 | for (String arg : args) { 170 | if (arg.equals("-v")) { 171 | verbose = true; 172 | } 173 | } 174 | 175 | SQLiteDatabase.dumpAll(printer, verbose); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteDirectCursorDriver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | import android.database.Cursor; 24 | import net.zetetic.database.sqlcipher.SQLiteDatabase.CursorFactory; 25 | import android.os.CancellationSignal; 26 | 27 | /** 28 | * A cursor driver that uses the given query directly. 29 | * 30 | * @hide 31 | */ 32 | public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver { 33 | private final SQLiteDatabase mDatabase; 34 | private final String mEditTable; 35 | private final String mSql; 36 | private final CancellationSignal mCancellationSignal; 37 | private SQLiteQuery mQuery; 38 | 39 | public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable, 40 | CancellationSignal cancellationSignal) { 41 | mDatabase = db; 42 | mEditTable = editTable; 43 | mSql = sql; 44 | mCancellationSignal = cancellationSignal; 45 | } 46 | 47 | public Cursor query(CursorFactory factory, String[] selectionArgs) { 48 | final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); 49 | final Cursor cursor; 50 | try { 51 | query.bindAllArgsAsStrings(selectionArgs); 52 | 53 | if (factory == null) { 54 | cursor = new SQLiteCursor(this, mEditTable, query); 55 | } else { 56 | cursor = factory.newCursor(mDatabase, this, mEditTable, query); 57 | } 58 | } catch (RuntimeException ex) { 59 | query.close(); 60 | throw ex; 61 | } 62 | 63 | mQuery = query; 64 | return cursor; 65 | } 66 | 67 | public Cursor query(CursorFactory factory, Object... bindingArgs){ 68 | final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); 69 | final Cursor cursor; 70 | try { 71 | query.bindAllArgs(bindingArgs); 72 | 73 | if (factory == null) { 74 | cursor = new SQLiteCursor(this, mEditTable, query); 75 | } else { 76 | cursor = factory.newCursor(mDatabase, this, mEditTable, query); 77 | } 78 | } catch (RuntimeException ex) { 79 | query.close(); 80 | throw ex; 81 | } 82 | 83 | mQuery = query; 84 | return cursor; 85 | } 86 | 87 | public void cursorClosed() { 88 | // Do nothing 89 | } 90 | 91 | public void setBindArguments(String[] bindArgs) { 92 | mQuery.bindAllArgsAsStrings(bindArgs); 93 | } 94 | 95 | public void cursorDeactivated() { 96 | // Do nothing 97 | } 98 | 99 | public void cursorRequeried(Cursor cursor) { 100 | // Do nothing 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "SQLiteDirectCursorDriver: " + mSql; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteGlobal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | import android.content.res.Resources; 24 | import android.os.StatFs; 25 | 26 | /** 27 | * Provides access to SQLite functions that affect all database connection, 28 | * such as memory management. 29 | * 30 | * The native code associated with SQLiteGlobal is also sets global configuration options 31 | * using sqlite3_config() then calls sqlite3_initialize() to ensure that the SQLite 32 | * library is properly initialized exactly once before any other framework or application 33 | * code has a chance to run. 34 | * 35 | * Verbose SQLite logging is enabled if the "log.tag.SQLiteLog" property is set to "V". 36 | * (per {@link SQLiteDebug#DEBUG_SQL_LOG}). 37 | * 38 | * @hide 39 | */ 40 | public final class SQLiteGlobal { 41 | private static final String TAG = "SQLiteGlobal"; 42 | 43 | private static final Object sLock = new Object(); 44 | private static int sDefaultPageSize = 4096; 45 | private static int sWALConnectionPoolSize = 10; 46 | 47 | private static native int nativeReleaseMemory(); 48 | 49 | private SQLiteGlobal() { 50 | } 51 | 52 | /** 53 | * Attempts to release memory by pruning the SQLite page cache and other 54 | * internal data structures. 55 | * 56 | * @return The number of bytes that were freed. 57 | */ 58 | public static int releaseMemory() { 59 | return nativeReleaseMemory(); 60 | } 61 | 62 | /** 63 | * Gets the default page size to use when creating a database. 64 | */ 65 | @SuppressWarnings("deprecation") 66 | public static int getDefaultPageSize() { 67 | synchronized (sLock) { 68 | if (sDefaultPageSize == 0) { 69 | // If there is an issue accessing /data, something is so seriously 70 | // wrong that we just let the IllegalArgumentException propagate. 71 | sDefaultPageSize = new StatFs("/data").getBlockSize(); 72 | } 73 | return 4096; 74 | } 75 | } 76 | 77 | /** 78 | * Gets the default journal mode when WAL is not in use. 79 | */ 80 | public static String getDefaultJournalMode() { 81 | return "delete"; 82 | } 83 | 84 | /** 85 | * Gets the journal size limit in bytes. 86 | */ 87 | public static int getJournalSizeLimit() { 88 | return 10000; 89 | } 90 | 91 | /** 92 | * Gets the default database synchronization mode when WAL is not in use. 93 | */ 94 | public static String getDefaultSyncMode() { 95 | return "normal"; 96 | } 97 | 98 | /** 99 | * Gets the database synchronization mode when in WAL mode. 100 | */ 101 | public static String getWALSyncMode() { 102 | return "normal"; 103 | } 104 | 105 | /** 106 | * Gets the WAL auto-checkpoint integer in database pages. 107 | */ 108 | public static int getWALAutoCheckpoint() { 109 | int value = 1000; 110 | return Math.max(1, value); 111 | } 112 | 113 | /** 114 | * Sets the connection pool size for WAL mode. 115 | */ 116 | public static void setWALConnectionPoolSize(int value) { 117 | sWALConnectionPoolSize = value; 118 | } 119 | 120 | /** 121 | * Gets the connection pool size when in WAL mode. 122 | */ 123 | public static int getWALConnectionPoolSize() { 124 | return sWALConnectionPoolSize; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteNotADatabaseException.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher; 2 | 3 | import android.database.sqlite.SQLiteException; 4 | 5 | /** 6 | * An exception that is specific to the "SQLITE_NOTADB" error code. 7 | * 8 | * SQLITE_NOTADB 9 | */ 10 | public class SQLiteNotADatabaseException extends SQLiteException { 11 | public SQLiteNotADatabaseException() { 12 | super(); 13 | } 14 | 15 | public SQLiteNotADatabaseException(String error) { 16 | super(error); 17 | } 18 | 19 | public SQLiteNotADatabaseException(String error, Throwable cause) { 20 | super(error, cause); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteQuery.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | import android.database.sqlite.SQLiteDatabaseCorruptException; 24 | import android.database.sqlite.SQLiteException; 25 | import android.os.CancellationSignal; 26 | import android.os.OperationCanceledException; 27 | 28 | import net.zetetic.database.CursorWindow; 29 | import net.zetetic.database.Logger; 30 | 31 | /** 32 | * Represents a query that reads the resulting rows into a {@link SQLiteQuery}. 33 | * This class is used by {@link SQLiteCursor} and isn't useful itself. 34 | *

35 | * This class is not thread-safe. 36 | *

37 | */ 38 | public final class SQLiteQuery extends SQLiteProgram { 39 | private static final String TAG = "SQLiteQuery"; 40 | 41 | private final CancellationSignal mCancellationSignal; 42 | 43 | SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) { 44 | super(db, query, null, cancellationSignal); 45 | 46 | mCancellationSignal = cancellationSignal; 47 | } 48 | 49 | /** 50 | * Reads rows into a buffer. 51 | * 52 | * @param window The window to fill into 53 | * @param startPos The start position for filling the window. 54 | * @param requiredPos The position of a row that MUST be in the window. 55 | * If it won't fit, then the query should discard part of what it filled. 56 | * @param countAllRows True to count all rows that the query would 57 | * return regardless of whether they fit in the window. 58 | * @return Number of rows that were enumerated. Might not be all rows 59 | * unless countAllRows is true. 60 | * 61 | * @throws SQLiteException if an error occurs. 62 | * @throws OperationCanceledException if the operation was canceled. 63 | */ 64 | int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) { 65 | acquireReference(); 66 | try { 67 | window.acquireReference(); 68 | try { 69 | int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(), 70 | window, startPos, requiredPos, countAllRows, getConnectionFlags(), 71 | mCancellationSignal); 72 | return numRows; 73 | } catch (SQLiteDatabaseCorruptException ex) { 74 | onCorruption(ex); 75 | throw ex; 76 | } catch (SQLiteException ex) { 77 | Logger.e(TAG, "exception: " + ex.getMessage() + "; query: " + getSql()); 78 | throw ex; 79 | } finally { 80 | window.releaseReference(); 81 | } 82 | } finally { 83 | releaseReference(); 84 | } 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return "SQLiteQuery: " + getSql(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteStatementInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | /** 24 | * Describes a SQLite statement. 25 | * 26 | * @hide 27 | */ 28 | public final class SQLiteStatementInfo { 29 | /** 30 | * The number of parameters that the statement has. 31 | */ 32 | public int numParameters; 33 | 34 | /** 35 | * The names of all columns in the result set of the statement. 36 | */ 37 | public String[] columnNames; 38 | 39 | /** 40 | * True if the statement is read-only. 41 | */ 42 | public boolean readOnly; 43 | } 44 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SQLiteTransactionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | package net.zetetic.database.sqlcipher; 22 | 23 | /** 24 | * A listener for transaction events. 25 | */ 26 | public interface SQLiteTransactionListener { 27 | /** 28 | * Called immediately after the transaction begins. 29 | */ 30 | void onBegin(); 31 | 32 | /** 33 | * Called immediately before commiting the transaction. 34 | */ 35 | void onCommit(); 36 | 37 | /** 38 | * Called if the transaction is about to be rolled back. 39 | */ 40 | void onRollback(); 41 | } 42 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SupportHelper.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher; 2 | 3 | import androidx.annotation.Nullable; 4 | import androidx.sqlite.db.SupportSQLiteDatabase; 5 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 6 | 7 | public class SupportHelper implements SupportSQLiteOpenHelper { 8 | 9 | private SQLiteOpenHelper openHelper; 10 | 11 | public SupportHelper(final Configuration configuration, byte[] password, SQLiteDatabaseHook hook, 12 | boolean enableWriteAheadLogging) { 13 | this(configuration, password, hook, enableWriteAheadLogging, 0); 14 | } 15 | 16 | public SupportHelper(final Configuration configuration, byte[] password, SQLiteDatabaseHook hook, 17 | boolean enableWriteAheadLogging, int minimumSupportedVersion) { 18 | openHelper = new SQLiteOpenHelper(configuration.context, configuration.name, password, 19 | null, configuration.callback.version, minimumSupportedVersion, null, hook, enableWriteAheadLogging) { 20 | @Override 21 | public void onCreate(SQLiteDatabase db) { 22 | configuration.callback.onCreate(db); 23 | } 24 | 25 | @Override 26 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 27 | configuration.callback.onUpgrade(db, oldVersion, newVersion); 28 | } 29 | 30 | @Override 31 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 32 | configuration.callback.onDowngrade(db, oldVersion, newVersion); 33 | } 34 | 35 | @Override 36 | public void onOpen(SQLiteDatabase db) { 37 | configuration.callback.onOpen(db); 38 | } 39 | 40 | @Override 41 | public void onConfigure(SQLiteDatabase db) { 42 | configuration.callback.onConfigure(db); 43 | } 44 | }; 45 | } 46 | 47 | @Nullable 48 | @Override 49 | public String getDatabaseName() { 50 | return openHelper.getDatabaseName(); 51 | } 52 | 53 | @Override 54 | public void setWriteAheadLoggingEnabled(boolean enabled) { 55 | openHelper.setWriteAheadLoggingEnabled(enabled); 56 | } 57 | 58 | @Override 59 | public SupportSQLiteDatabase getWritableDatabase() { 60 | return openHelper.getWritableDatabase(); 61 | } 62 | 63 | @Override 64 | public SupportSQLiteDatabase getReadableDatabase() { 65 | return openHelper.getReadableDatabase(); 66 | } 67 | 68 | @Override 69 | public void close() { 70 | openHelper.close(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /sqlcipher/src/main/java/net/zetetic/database/sqlcipher/SupportOpenHelperFactory.java: -------------------------------------------------------------------------------- 1 | package net.zetetic.database.sqlcipher; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 5 | 6 | public class SupportOpenHelperFactory implements SupportSQLiteOpenHelper.Factory { 7 | 8 | private static final int UNCHANGED = -1; 9 | 10 | private final byte[] password; 11 | private final SQLiteDatabaseHook hook; 12 | private final boolean enableWriteAheadLogging; 13 | 14 | private final int minimumSupportedVersion; 15 | 16 | public SupportOpenHelperFactory(byte[] password){ 17 | this(password, null, false); 18 | } 19 | 20 | public SupportOpenHelperFactory(byte[] password, SQLiteDatabaseHook hook, boolean enableWriteAheadLogging) { 21 | this(password, hook, enableWriteAheadLogging, UNCHANGED); 22 | } 23 | 24 | public SupportOpenHelperFactory(byte[] password, SQLiteDatabaseHook hook, 25 | boolean enableWriteAheadLogging, int minimumSupportedVersion) { 26 | this.password = password; 27 | this.hook = hook; 28 | this.enableWriteAheadLogging = enableWriteAheadLogging; 29 | this.minimumSupportedVersion = minimumSupportedVersion; 30 | } 31 | 32 | @NonNull 33 | @Override 34 | public SupportSQLiteOpenHelper create(@NonNull SupportSQLiteOpenHelper.Configuration configuration) { 35 | if (minimumSupportedVersion == UNCHANGED) { 36 | return new SupportHelper(configuration, this.password, this.hook, enableWriteAheadLogging); 37 | } else { 38 | return new SupportHelper(configuration, this.password, this.hook, 39 | enableWriteAheadLogging, minimumSupportedVersion); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | 2 | LOCAL_PATH:= $(call my-dir) 3 | include $(LOCAL_PATH)/sqlcipher/Android.mk 4 | 5 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_STL:=c++_static 2 | APP_LDFLAGS += -Wl,--exclude-libs,ALL 3 | APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true 4 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/ALog-priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | #ifndef NATIVEHELPER_ALOGPRIV_H_ 22 | #define NATIVEHELPER_ALOGPRIV_H_ 23 | 24 | #include 25 | 26 | #ifndef LOG_NDEBUG 27 | #ifdef NDEBUG 28 | #define LOG_NDEBUG 1 29 | #else 30 | #define LOG_NDEBUG 0 31 | #endif 32 | #endif 33 | 34 | 35 | /* 36 | * Basic log message macros intended to emulate the behavior of log/log.h 37 | * in system core. This should be dependent only on ndk exposed logging 38 | * functionality. 39 | */ 40 | 41 | #ifndef ALOG 42 | #define ALOG(priority, tag, fmt...) \ 43 | __android_log_print(ANDROID_##priority, tag, fmt) 44 | #endif 45 | 46 | #ifndef ALOGV 47 | #if defined(LOG_NDEBUG) || defined(SQLCIPHER_OMIT_LOG) 48 | #define ALOGV(...) ((void)0) 49 | #else 50 | #define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) 51 | #endif 52 | #endif 53 | 54 | #ifndef ALOGD 55 | #if defined(LOG_NDEBUG) || defined(SQLCIPHER_OMIT_LOG) 56 | #define ALOGD(...) ((void)0) 57 | #else 58 | #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) 59 | #endif 60 | #endif 61 | 62 | #ifndef ALOGI 63 | #if defined(LOG_NDEBUG) || defined(SQLCIPHER_OMIT_LOG) 64 | #define ALOGI(...) ((void)0) 65 | #else 66 | #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) 67 | #endif 68 | #endif 69 | 70 | #ifndef ALOGW 71 | #if defined(SQLCIPHER_OMIT_LOG) 72 | #define ALOGW(...) ((void)0) 73 | #else 74 | #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) 75 | #endif 76 | #endif 77 | 78 | #ifndef ALOGE 79 | #if defined(SQLCIPHER_OMIT_LOG) 80 | #define ALOGE(...) ((void)0) 81 | #else 82 | #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) 83 | #endif 84 | #endif 85 | 86 | /* 87 | ** Not quite the same as the core android LOG_FATAL_IF (which also 88 | ** sends a SIGTRAP), but close enough. 89 | */ 90 | #define LOG_FATAL_IF(bCond, zErr) if( bCond ) ALOGE(zErr); 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | ifdef $SSQLCIPHER_CFLAGS 5 | LOCAL_CFLAGS += ${SQLCIPHER_CFLAGS} 6 | else 7 | LOCAL_CFLAGS += -DSQLITE_HAS_CODEC -DSQLCIPHER_CRYPTO_OPENSSL -DSQLITE_TEMP_STORE=2 \ 8 | -DSQLITE_THREADSAFE=1 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_FTS3_PARENTHESIS \ 9 | -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_UNICODE61 -DSQLITE_ENABLE_FTS5 \ 10 | -DSQLITE_ENABLE_MEMORY_MANAGEMENT -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_ENABLE_RTREE \ 11 | -DSQLITE_SOUNDEX -DHAVE_USLEEP -DSQLITE_ENABLE_LOAD_EXTENSION -DSQLITE_ENABLE_STAT3 \ 12 | -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ 13 | -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_MAX_VARIABLE_NUMBER=99999 \ 14 | -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 -DSQLITE_ENABLE_SESSION \ 15 | -DSQLITE_ENABLE_PREUPDATE_HOOK -DSQLITE_ENABLE_DBSTAT_VTAB 16 | endif 17 | 18 | LOCAL_CPPFLAGS += -Wno-conversion-null 19 | 20 | $(info SQLCipher LOCAL_CFLAGS:${LOCAL_CFLAGS}) 21 | 22 | LOCAL_SRC_FILES:= \ 23 | android_database_SQLiteCommon.cpp \ 24 | android_database_SQLiteConnection.cpp \ 25 | android_database_CursorWindow.cpp \ 26 | android_database_SQLiteGlobal.cpp \ 27 | android_database_SQLiteDebug.cpp \ 28 | JNIHelp.cpp \ 29 | JniConstants.cpp \ 30 | JNIString.cpp \ 31 | CursorWindow.cpp 32 | 33 | LOCAL_SRC_FILES += sqlite3.c 34 | 35 | LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/nativehelper/ $(LOCAL_PATH)/android-libs/include/ 36 | 37 | LOCAL_MODULE:= libsqlcipher 38 | LOCAL_LDLIBS += -ldl -llog 39 | LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 40 | LOCAL_STATIC_LIBRARIES += static-libcrypto 41 | 42 | include $(BUILD_SHARED_LIBRARY) 43 | 44 | include $(CLEAR_VARS) 45 | LOCAL_MODULE := static-libcrypto 46 | LOCAL_SRC_FILES := $(LOCAL_PATH)/android-libs/$(TARGET_ARCH_ABI)/libcrypto.a 47 | include $(PREBUILT_STATIC_LIBRARY) 48 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/CursorWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // modified from original source see README at the top level of this project 17 | 18 | #ifndef _ANDROID__DATABASE_WINDOW_H 19 | #define _ANDROID__DATABASE_WINDOW_H 20 | 21 | #include "ALog-priv.h" 22 | #include 23 | #include 24 | 25 | #include "Errors.h" 26 | 27 | namespace android { 28 | 29 | /** 30 | * This class stores a set of rows from a database in a buffer. The beginning of the 31 | * window has first chunk of RowSlots, which are offsets to the row directory, followed by 32 | * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case 33 | * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a 34 | * FieldSlot per column, which has the size, offset, and type of the data for that field. 35 | * Note that the data types come from sqlite3.h. 36 | * 37 | * Strings are stored in UTF-8. 38 | */ 39 | class CursorWindow { 40 | CursorWindow(const char* name, void* data, size_t size, bool readOnly); 41 | 42 | public: 43 | /* Field types. */ 44 | enum { 45 | FIELD_TYPE_NULL = 0, 46 | FIELD_TYPE_INTEGER = 1, 47 | FIELD_TYPE_FLOAT = 2, 48 | FIELD_TYPE_STRING = 3, 49 | FIELD_TYPE_BLOB = 4, 50 | }; 51 | 52 | /* Opaque type that describes a field slot. */ 53 | struct FieldSlot { 54 | private: 55 | int32_t type; 56 | union { 57 | double d; 58 | int64_t l; 59 | struct { 60 | uint32_t offset; 61 | uint32_t size; 62 | } buffer; 63 | } data; 64 | 65 | friend class CursorWindow; 66 | } __attribute((packed)); 67 | 68 | ~CursorWindow(); 69 | 70 | static status_t create(const char* name, size_t size, CursorWindow** outCursorWindow); 71 | 72 | inline const char* name() { return mName; } 73 | inline size_t size() const { return mSize; } 74 | inline size_t freeSpace() { return mSize - mHeader->freeOffset; } 75 | inline uint32_t getNumRows() { return mHeader->numRows; } 76 | inline uint32_t getNumColumns() { return mHeader->numColumns; } 77 | 78 | status_t clear(); 79 | status_t setNumColumns(uint32_t numColumns); 80 | 81 | /** 82 | * Allocate a row slot and its directory. 83 | * The row is initialized will null entries for each field. 84 | */ 85 | status_t allocRow(); 86 | status_t freeLastRow(); 87 | status_t maybeInflate(); 88 | 89 | status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size); 90 | status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull); 91 | status_t putLong(uint32_t row, uint32_t column, int64_t value); 92 | status_t putDouble(uint32_t row, uint32_t column, double value); 93 | status_t putNull(uint32_t row, uint32_t column); 94 | 95 | /** 96 | * Gets the field slot at the specified row and column. 97 | * Returns null if the requested row or column is not in the window. 98 | */ 99 | FieldSlot* getFieldSlot(uint32_t row, uint32_t column); 100 | 101 | static inline int32_t getFieldSlotType(FieldSlot* fieldSlot) { 102 | return fieldSlot->type; 103 | } 104 | 105 | static inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) { 106 | return fieldSlot->data.l; 107 | } 108 | 109 | static inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) { 110 | return fieldSlot->data.d; 111 | } 112 | 113 | inline const char* getFieldSlotValueString(FieldSlot* fieldSlot, 114 | size_t* outSizeIncludingNull) { 115 | *outSizeIncludingNull = fieldSlot->data.buffer.size; 116 | return static_cast(offsetToPtr(fieldSlot->data.buffer.offset)); 117 | } 118 | 119 | inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) { 120 | *outSize = fieldSlot->data.buffer.size; 121 | return offsetToPtr(fieldSlot->data.buffer.offset); 122 | } 123 | 124 | private: 125 | static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100; 126 | 127 | struct Header { 128 | // Offset of the lowest unused byte in the window. 129 | uint32_t freeOffset; 130 | 131 | // Offset of the first row slot chunk. 132 | uint32_t firstChunkOffset; 133 | 134 | uint32_t numRows; 135 | uint32_t numColumns; 136 | }; 137 | 138 | struct RowSlot { 139 | uint32_t offset; 140 | }; 141 | 142 | struct RowSlotChunk { 143 | RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS]; 144 | uint32_t nextChunkOffset; 145 | }; 146 | 147 | char* mName; 148 | void* mData; 149 | size_t mSize; 150 | bool mReadOnly; 151 | Header* mHeader; 152 | 153 | inline void* offsetToPtr(uint32_t offset) { 154 | return static_cast(mData) + offset; 155 | } 156 | 157 | inline uint32_t offsetFromPtr(void* ptr) { 158 | return static_cast(ptr) - static_cast(mData); 159 | } 160 | 161 | /** 162 | * Allocate a portion of the window. Returns the offset 163 | * of the allocation, or 0 if there isn't enough space. 164 | * If aligned is true, the allocation gets 4 byte alignment. 165 | */ 166 | uint32_t alloc(size_t size, bool aligned = false); 167 | 168 | RowSlot* getRowSlot(uint32_t row); 169 | RowSlot* allocRowSlot(); 170 | 171 | status_t putBlobOrString(uint32_t row, uint32_t column, 172 | const void* value, size_t size, int32_t type); 173 | }; 174 | 175 | } // namespace android 176 | 177 | #endif 178 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/Errors.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef ANDROID_ERRORS_H 18 | #define ANDROID_ERRORS_H 19 | 20 | #include 21 | #include 22 | 23 | namespace android { 24 | 25 | // use this type to return error codes 26 | #ifdef HAVE_MS_C_RUNTIME 27 | typedef int status_t; 28 | #else 29 | typedef int32_t status_t; 30 | #endif 31 | 32 | /* the MS C runtime lacks a few error codes */ 33 | 34 | /* 35 | * Error codes. 36 | * All error codes are negative values. 37 | */ 38 | 39 | // Win32 #defines NO_ERROR as well. It has the same value, so there's no 40 | // real conflict, though it's a bit awkward. 41 | #ifdef _WIN32 42 | # undef NO_ERROR 43 | #endif 44 | 45 | enum { 46 | OK = 0, // Everything's swell. 47 | NO_ERROR = 0, // No errors. 48 | 49 | UNKNOWN_ERROR = 0x80000000, 50 | 51 | NO_MEMORY = -ENOMEM, 52 | INVALID_OPERATION = -ENOSYS, 53 | BAD_VALUE = -EINVAL, 54 | BAD_TYPE = 0x80000001, 55 | NAME_NOT_FOUND = -ENOENT, 56 | PERMISSION_DENIED = -EPERM, 57 | NO_INIT = -ENODEV, 58 | ALREADY_EXISTS = -EEXIST, 59 | DEAD_OBJECT = -EPIPE, 60 | FAILED_TRANSACTION = 0x80000002, 61 | JPARKS_BROKE_IT = -EPIPE, 62 | #if !defined(HAVE_MS_C_RUNTIME) 63 | BAD_INDEX = -EOVERFLOW, 64 | NOT_ENOUGH_DATA = -ENODATA, 65 | WOULD_BLOCK = -EWOULDBLOCK, 66 | TIMED_OUT = -ETIMEDOUT, 67 | UNKNOWN_TRANSACTION = -EBADMSG, 68 | #else 69 | BAD_INDEX = -E2BIG, 70 | NOT_ENOUGH_DATA = 0x80000003, 71 | WOULD_BLOCK = 0x80000004, 72 | TIMED_OUT = 0x80000005, 73 | UNKNOWN_TRANSACTION = 0x80000006, 74 | #endif 75 | FDS_NOT_ALLOWED = 0x80000007, 76 | }; 77 | 78 | // Restore define; enumeration is in "android" namespace, so the value defined 79 | // there won't work for Win32 code in a different namespace. 80 | #ifdef _WIN32 81 | # define NO_ERROR 0L 82 | #endif 83 | 84 | }; // namespace android 85 | 86 | // --------------------------------------------------------------------------- 87 | 88 | #endif // ANDROID_ERRORS_H 89 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/JNIString.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | // Note this code is adapted from AOSP implementation of String, now located at 19 | // https://android.googlesource.com/platform/libcore/+/master/libart/src/main/java/java/lang/StringFactory.java 20 | 21 | #include 22 | 23 | #define REPLACEMENT_CHAR 0xfffd; 24 | 25 | namespace android { 26 | 27 | jsize utf8ToJavaCharArray(const char* d, jchar v[], jint byteCount) { 28 | jint idx = 0; 29 | jint last = byteCount; 30 | jint s = 0; 31 | outer: 32 | while (idx < last) { 33 | jbyte b0 = d[idx++]; 34 | if ((b0 & 0x80) == 0) { 35 | // 0xxxxxxx 36 | // Range: U-00000000 - U-0000007F 37 | jint val = b0 & 0xff; 38 | v[s++] = (jchar) val; 39 | } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) || 40 | ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) { 41 | jint utfCount = 1; 42 | if ((b0 & 0xf0) == 0xe0) utfCount = 2; 43 | else if ((b0 & 0xf8) == 0xf0) utfCount = 3; 44 | else if ((b0 & 0xfc) == 0xf8) utfCount = 4; 45 | else if ((b0 & 0xfe) == 0xfc) utfCount = 5; 46 | 47 | // 110xxxxx (10xxxxxx)+ 48 | // Range: U-00000080 - U-000007FF (count == 1) 49 | // Range: U-00000800 - U-0000FFFF (count == 2) 50 | // Range: U-00010000 - U-001FFFFF (count == 3) 51 | // Range: U-00200000 - U-03FFFFFF (count == 4) 52 | // Range: U-04000000 - U-7FFFFFFF (count == 5) 53 | 54 | if (idx + utfCount > last) { 55 | v[s++] = REPLACEMENT_CHAR; 56 | continue; 57 | } 58 | 59 | // Extract usable bits from b0 60 | jint val = b0 & (0x1f >> (utfCount - 1)); 61 | for (int i = 0; i < utfCount; ++i) { 62 | jbyte b = d[idx++]; 63 | if ((b & 0xc0) != 0x80) { 64 | v[s++] = REPLACEMENT_CHAR; 65 | idx--; // Put the input char back 66 | goto outer; 67 | } 68 | // Push new bits in from the right side 69 | val <<= 6; 70 | val |= b & 0x3f; 71 | } 72 | 73 | // Note: Java allows overlong char 74 | // specifications To disallow, check that val 75 | // is greater than or equal to the minimum 76 | // value for each count: 77 | // 78 | // count min value 79 | // ----- ---------- 80 | // 1 0x80 81 | // 2 0x800 82 | // 3 0x10000 83 | // 4 0x200000 84 | // 5 0x4000000 85 | 86 | // Allow surrogate values (0xD800 - 0xDFFF) to 87 | // be specified using 3-byte UTF values only 88 | if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) { 89 | v[s++] = REPLACEMENT_CHAR; 90 | continue; 91 | } 92 | 93 | // Reject chars greater than the Unicode maximum of U+10FFFF. 94 | if (val > 0x10FFFF) { 95 | v[s++] = REPLACEMENT_CHAR; 96 | continue; 97 | } 98 | 99 | // Encode chars from U+10000 up as surrogate pairs 100 | if (val < 0x10000) { 101 | v[s++] = (jchar) val; 102 | } else { 103 | int x = val & 0xffff; 104 | int u = (val >> 16) & 0x1f; 105 | int w = (u - 1) & 0xffff; 106 | int hi = 0xd800 | (w << 6) | (x >> 10); 107 | int lo = 0xdc00 | (x & 0x3ff); 108 | v[s++] = (jchar) hi; 109 | v[s++] = (jchar) lo; 110 | } 111 | } else { 112 | // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff 113 | v[s++] = REPLACEMENT_CHAR; 114 | } 115 | } 116 | return s; 117 | } 118 | } -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/JniConstants.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define LOG_TAG "JniConstants" 18 | 19 | #include "ALog-priv.h" 20 | #include "JniConstants.h" 21 | #include "ScopedLocalRef.h" 22 | 23 | #include 24 | 25 | jclass JniConstants::bidiRunClass; 26 | jclass JniConstants::bigDecimalClass; 27 | jclass JniConstants::booleanClass; 28 | jclass JniConstants::byteArrayClass; 29 | jclass JniConstants::byteClass; 30 | jclass JniConstants::calendarClass; 31 | jclass JniConstants::characterClass; 32 | jclass JniConstants::charsetICUClass; 33 | jclass JniConstants::constructorClass; 34 | jclass JniConstants::deflaterClass; 35 | jclass JniConstants::doubleClass; 36 | jclass JniConstants::errnoExceptionClass; 37 | jclass JniConstants::fieldClass; 38 | jclass JniConstants::fieldPositionIteratorClass; 39 | jclass JniConstants::fileDescriptorClass; 40 | jclass JniConstants::floatClass; 41 | jclass JniConstants::gaiExceptionClass; 42 | jclass JniConstants::inet6AddressClass; 43 | jclass JniConstants::inetAddressClass; 44 | jclass JniConstants::inetSocketAddressClass; 45 | jclass JniConstants::inetUnixAddressClass; 46 | jclass JniConstants::inflaterClass; 47 | jclass JniConstants::inputStreamClass; 48 | jclass JniConstants::integerClass; 49 | jclass JniConstants::localeDataClass; 50 | jclass JniConstants::longClass; 51 | jclass JniConstants::methodClass; 52 | jclass JniConstants::mutableIntClass; 53 | jclass JniConstants::mutableLongClass; 54 | jclass JniConstants::objectClass; 55 | jclass JniConstants::objectArrayClass; 56 | jclass JniConstants::outputStreamClass; 57 | jclass JniConstants::parsePositionClass; 58 | jclass JniConstants::patternSyntaxExceptionClass; 59 | jclass JniConstants::realToStringClass; 60 | jclass JniConstants::referenceClass; 61 | jclass JniConstants::shortClass; 62 | jclass JniConstants::socketClass; 63 | jclass JniConstants::socketImplClass; 64 | jclass JniConstants::stringClass; 65 | jclass JniConstants::structAddrinfoClass; 66 | jclass JniConstants::structFlockClass; 67 | jclass JniConstants::structGroupReqClass; 68 | jclass JniConstants::structLingerClass; 69 | jclass JniConstants::structPasswdClass; 70 | jclass JniConstants::structPollfdClass; 71 | jclass JniConstants::structStatClass; 72 | jclass JniConstants::structStatVfsClass; 73 | jclass JniConstants::structTimevalClass; 74 | jclass JniConstants::structUcredClass; 75 | jclass JniConstants::structUtsnameClass; 76 | 77 | static jclass findClass(JNIEnv* env, const char* name) { 78 | ScopedLocalRef localClass(env, env->FindClass(name)); 79 | jclass result = reinterpret_cast(env->NewGlobalRef(localClass.get())); 80 | if (result == NULL) { 81 | ALOGE("failed to find class '%s'", name); 82 | abort(); 83 | } 84 | return result; 85 | } 86 | 87 | void JniConstants::init(JNIEnv* env) { 88 | bidiRunClass = findClass(env, "java/text/Bidi$Run"); 89 | bigDecimalClass = findClass(env, "java/math/BigDecimal"); 90 | booleanClass = findClass(env, "java/lang/Boolean"); 91 | byteClass = findClass(env, "java/lang/Byte"); 92 | byteArrayClass = findClass(env, "[B"); 93 | calendarClass = findClass(env, "java/util/Calendar"); 94 | characterClass = findClass(env, "java/lang/Character"); 95 | charsetICUClass = findClass(env, "java/nio/charset/CharsetICU"); 96 | constructorClass = findClass(env, "java/lang/reflect/Constructor"); 97 | floatClass = findClass(env, "java/lang/Float"); 98 | deflaterClass = findClass(env, "java/util/zip/Deflater"); 99 | doubleClass = findClass(env, "java/lang/Double"); 100 | errnoExceptionClass = findClass(env, "libcore/io/ErrnoException"); 101 | fieldClass = findClass(env, "java/lang/reflect/Field"); 102 | fieldPositionIteratorClass = findClass(env, "libcore/icu/NativeDecimalFormat$FieldPositionIterator"); 103 | fileDescriptorClass = findClass(env, "java/io/FileDescriptor"); 104 | gaiExceptionClass = findClass(env, "libcore/io/GaiException"); 105 | inet6AddressClass = findClass(env, "java/net/Inet6Address"); 106 | inetAddressClass = findClass(env, "java/net/InetAddress"); 107 | inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress"); 108 | inetUnixAddressClass = findClass(env, "java/net/InetUnixAddress"); 109 | inflaterClass = findClass(env, "java/util/zip/Inflater"); 110 | inputStreamClass = findClass(env, "java/io/InputStream"); 111 | integerClass = findClass(env, "java/lang/Integer"); 112 | localeDataClass = findClass(env, "libcore/icu/LocaleData"); 113 | longClass = findClass(env, "java/lang/Long"); 114 | methodClass = findClass(env, "java/lang/reflect/Method"); 115 | mutableIntClass = findClass(env, "libcore/util/MutableInt"); 116 | mutableLongClass = findClass(env, "libcore/util/MutableLong"); 117 | objectClass = findClass(env, "java/lang/Object"); 118 | objectArrayClass = findClass(env, "[Ljava/lang/Object;"); 119 | outputStreamClass = findClass(env, "java/io/OutputStream"); 120 | parsePositionClass = findClass(env, "java/text/ParsePosition"); 121 | patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException"); 122 | realToStringClass = findClass(env, "java/lang/RealToString"); 123 | referenceClass = findClass(env, "java/lang/ref/Reference"); 124 | shortClass = findClass(env, "java/lang/Short"); 125 | socketClass = findClass(env, "java/net/Socket"); 126 | socketImplClass = findClass(env, "java/net/SocketImpl"); 127 | stringClass = findClass(env, "java/lang/String"); 128 | structAddrinfoClass = findClass(env, "libcore/io/StructAddrinfo"); 129 | structFlockClass = findClass(env, "libcore/io/StructFlock"); 130 | structGroupReqClass = findClass(env, "libcore/io/StructGroupReq"); 131 | structLingerClass = findClass(env, "libcore/io/StructLinger"); 132 | structPasswdClass = findClass(env, "libcore/io/StructPasswd"); 133 | structPollfdClass = findClass(env, "libcore/io/StructPollfd"); 134 | structStatClass = findClass(env, "libcore/io/StructStat"); 135 | structStatVfsClass = findClass(env, "libcore/io/StructStatVfs"); 136 | structTimevalClass = findClass(env, "libcore/io/StructTimeval"); 137 | structUcredClass = findClass(env, "libcore/io/StructUcred"); 138 | structUtsnameClass = findClass(env, "libcore/io/StructUtsname"); 139 | } 140 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/README: -------------------------------------------------------------------------------- 1 | 2 | All the files in this directory are copied from stock android. The following 3 | files: 4 | 5 | JniConstants.cpp 6 | JNIHelp.cpp 7 | ALog-priv.h 8 | 9 | are copied in from Android's libnativehelper module (altogether less than 1000 10 | lines of code). The remainder are from the core framework (directory 11 | /frameworks/base/core/jni). 12 | 13 | Notes on changes: 14 | 15 | The ashmem_XXX() interfaces are used for the various "xxxForBlobDescriptor()" 16 | API functions. The code in libcutils for this seems to be platform 17 | dependent - some platforms have kernel support, others have a user space 18 | implementation. So these functions are not supported for now. 19 | 20 | The original SQLiteConnection.cpp uses AndroidRuntime::genJNIEnv() to obtain a 21 | pointer to the current threads environment. Changed to store a pointer to the 22 | process JavaVM (Android allows only one) as a global variable. Then retrieve 23 | the JNIEnv as needed using GetEnv(). 24 | 25 | Replaced uses of class String8 with std::string in SQLiteConnection.cpp and a 26 | few other places. 27 | 28 | The stock Android code to populate CursorWindow containers with the results of 29 | a SELECT statement uses a C++ interface that is not available to NDK builds. So 30 | this code is rewritten to call the CursorWindow java interface via JNI methods. 31 | This is the largest source code change. See function 32 | nativeExecuteForCursorWindow() in file android_database_SQLiteConnection.cpp 33 | for details. 34 | 35 | The "LOCALIZED" collation and some miscellaneous user-functions added by the 36 | sqlite3_android.cpp module are not included. A collation called LOCALIZED 37 | that is equivalent to BINARY is added instead to keep various things working. 38 | This should not cause serious problems - class SQLiteConnection always 39 | runs "REINDEX LOCALIZED" immediately after opening a connection. 40 | 41 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteCommon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | #include "android_database_SQLiteCommon.h" 22 | 23 | namespace android { 24 | 25 | /* throw a SQLiteException with a message appropriate for the error in handle */ 26 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { 27 | throw_sqlite3_exception(env, handle, nullptr); 28 | } 29 | 30 | /* throw a SQLiteException with the given message */ 31 | void throw_sqlite3_exception(JNIEnv* env, const char* message) { 32 | throw_sqlite3_exception(env, nullptr, message); 33 | } 34 | 35 | /* throw a SQLiteException with a message appropriate for the error in handle 36 | concatenated with the given message 37 | */ 38 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) { 39 | if (handle) { 40 | // get the error code and message from the SQLite connection 41 | // the error message may contain more information than the error code 42 | // because it is based on the extended error code rather than the simplified 43 | // error code that SQLite normally returns. 44 | throw_sqlite3_exception(env, sqlite3_extended_errcode(handle), 45 | sqlite3_errmsg(handle), message); 46 | } else { 47 | // we use SQLITE_OK so that a generic SQLiteException is thrown; 48 | // any code not specified in the switch statement below would do. 49 | throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message); 50 | } 51 | } 52 | 53 | /* throw a SQLiteException for a given error code 54 | * should only be used when the database connection is not available because the 55 | * error information will not be quite as rich */ 56 | void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) { 57 | throw_sqlite3_exception(env, errcode, "unknown error", message); 58 | } 59 | 60 | /* throw a SQLiteException for a given error code, sqlite3message, and 61 | user message 62 | */ 63 | void throw_sqlite3_exception(JNIEnv* env, int errcode, 64 | const char* sqlite3Message, const char* message) { 65 | const char* exceptionClass; 66 | switch (errcode & 0xff) { /* mask off extended error code */ 67 | case SQLITE_IOERR: 68 | exceptionClass = "android/database/sqlite/SQLiteDiskIOException"; 69 | break; 70 | case SQLITE_CORRUPT: 71 | exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException"; 72 | break; 73 | case SQLITE_NOTADB: 74 | /* Upstream treats treat "unsupported file format" error as corruption (SQLiteDatabaseCorruptException). 75 | However, SQLITE_NOTADB can occur with mismatched keys, which is not a corruption case, so SQLCipher 76 | treats this as a general exception */ 77 | exceptionClass = "net/zetetic/database/sqlcipher/SQLiteNotADatabaseException"; 78 | break; 79 | case SQLITE_CONSTRAINT: 80 | exceptionClass = "android/database/sqlite/SQLiteConstraintException"; 81 | break; 82 | case SQLITE_ABORT: 83 | exceptionClass = "android/database/sqlite/SQLiteAbortException"; 84 | break; 85 | case SQLITE_DONE: 86 | exceptionClass = "android/database/sqlite/SQLiteDoneException"; 87 | sqlite3Message = nullptr; // SQLite error message is irrelevant in this case 88 | break; 89 | case SQLITE_FULL: 90 | exceptionClass = "android/database/sqlite/SQLiteFullException"; 91 | break; 92 | case SQLITE_MISUSE: 93 | exceptionClass = "android/database/sqlite/SQLiteMisuseException"; 94 | break; 95 | case SQLITE_PERM: 96 | exceptionClass = "android/database/sqlite/SQLiteAccessPermException"; 97 | break; 98 | case SQLITE_BUSY: 99 | exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException"; 100 | break; 101 | case SQLITE_LOCKED: 102 | exceptionClass = "android/database/sqlite/SQLiteTableLockedException"; 103 | break; 104 | case SQLITE_READONLY: 105 | exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException"; 106 | break; 107 | case SQLITE_CANTOPEN: 108 | exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException"; 109 | break; 110 | case SQLITE_TOOBIG: 111 | exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException"; 112 | break; 113 | case SQLITE_RANGE: 114 | exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; 115 | break; 116 | case SQLITE_NOMEM: 117 | exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException"; 118 | break; 119 | case SQLITE_MISMATCH: 120 | exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException"; 121 | break; 122 | case SQLITE_INTERRUPT: 123 | exceptionClass = "android/os/OperationCanceledException"; 124 | break; 125 | default: 126 | exceptionClass = "android/database/sqlite/SQLiteException"; 127 | break; 128 | } 129 | 130 | if (sqlite3Message) { 131 | char *zFullmsg = sqlite3_mprintf( 132 | "%s (code %d)%s%s", sqlite3Message, errcode, 133 | (message ? ": " : ""), (message ? message : "") 134 | ); 135 | jniThrowException(env, exceptionClass, zFullmsg); 136 | sqlite3_free(zFullmsg); 137 | } else { 138 | jniThrowException(env, exceptionClass, message); 139 | } 140 | } 141 | 142 | 143 | } // namespace android 144 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteCommon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | #ifndef _ANDROID_DATABASE_SQLITE_COMMON_H 22 | #define _ANDROID_DATABASE_SQLITE_COMMON_H 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | // Special log tags defined in SQLiteDebug.java. 30 | #define SQLITE_LOG_TAG "SQLiteLog" 31 | #define SQLITE_TRACE_TAG "SQLiteStatements" 32 | #define SQLITE_PROFILE_TAG "SQLiteTime" 33 | 34 | namespace android { 35 | 36 | /* throw a SQLiteException with a message appropriate for the error in handle */ 37 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle); 38 | 39 | /* throw a SQLiteException with the given message */ 40 | void throw_sqlite3_exception(JNIEnv* env, const char* message); 41 | 42 | /* throw a SQLiteException with a message appropriate for the error in handle 43 | concatenated with the given message 44 | */ 45 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message); 46 | 47 | /* throw a SQLiteException for a given error code */ 48 | void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message); 49 | 50 | void throw_sqlite3_exception(JNIEnv* env, int errcode, 51 | const char* sqlite3Message, const char* message); 52 | 53 | } 54 | 55 | #endif // _ANDROID_DATABASE_SQLITE_COMMON_H 56 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteDebug.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | #define LOG_TAG "SQLiteDebug" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | namespace android { 35 | 36 | static struct { 37 | jfieldID memoryUsed; 38 | jfieldID pageCacheOverflow; 39 | jfieldID largestMemAlloc; 40 | } gSQLiteDebugPagerStatsClassInfo; 41 | 42 | static void nativeGetPagerStats(JNIEnv *env, jobject clazz, jobject statsObj) 43 | { 44 | int memoryUsed; 45 | int pageCacheOverflow; 46 | int largestMemAlloc; 47 | int unused; 48 | 49 | sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0); 50 | sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0); 51 | sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflow, &unused, 0); 52 | env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.memoryUsed, memoryUsed); 53 | env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, 54 | pageCacheOverflow); 55 | env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, largestMemAlloc); 56 | } 57 | 58 | /* 59 | * JNI registration. 60 | */ 61 | 62 | static JNINativeMethod gMethods[] = 63 | { 64 | { "nativeGetPagerStats", "(Lnet/zetetic/database/sqlcipher/SQLiteDebug$PagerStats;)V", 65 | (void*) nativeGetPagerStats }, 66 | }; 67 | 68 | #define FIND_CLASS(var, className) \ 69 | var = env->FindClass(className); \ 70 | LOG_FATAL_IF(! var, "Unable to find class " className); 71 | 72 | #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 73 | var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 74 | LOG_FATAL_IF(! var, "Unable to find field " fieldName); 75 | 76 | int register_android_database_SQLiteDebug(JNIEnv *env) 77 | { 78 | jclass clazz; 79 | FIND_CLASS(clazz, "net/zetetic/database/sqlcipher/SQLiteDebug$PagerStats") 80 | 81 | GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.memoryUsed, clazz, 82 | "memoryUsed", "I") 83 | GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, clazz, 84 | "largestMemAlloc", "I") 85 | GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, clazz, 86 | "pageCacheOverflow", "I") 87 | 88 | return jniRegisterNativeMethods(env, "net/zetetic/database/sqlcipher/SQLiteDebug", 89 | gMethods, NELEM(gMethods)); 90 | } 91 | 92 | } // namespace android 93 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/android_database_SQLiteGlobal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | /* 17 | ** Modified to support SQLite extensions by the SQLite developers: 18 | ** sqlite-dev@sqlite.org. 19 | */ 20 | 21 | #define LOG_TAG "SQLiteGlobal" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "android_database_SQLiteCommon.h" 29 | #include "ALog-priv.h" 30 | 31 | namespace android { 32 | 33 | // Limit heap to 8MB for now. This is 4 times the maximum cursor window 34 | // size, as has been used by the original code in SQLiteDatabase for 35 | // a long time. 36 | static const int SOFT_HEAP_LIMIT = 8 * 1024 * 1024; 37 | 38 | #undef LOG_TAG 39 | #define LOG_TAG SQLITE_LOG_TAG 40 | 41 | // Called each time a message is logged. 42 | static void sqliteLogCallback(void* data, int err, const char* msg) { 43 | bool verboseLog = !!data; 44 | int errType = err & 255; 45 | if (errType == 0 || errType == SQLITE_CONSTRAINT || errType == SQLITE_SCHEMA 46 | || errType == SQLITE_NOTICE || err == SQLITE_WARNING_AUTOINDEX) { 47 | if (verboseLog) { 48 | ALOGV("(%d) %s\n", err, msg); 49 | } 50 | } else if (errType == SQLITE_WARNING) { 51 | ALOGW("(%d) %s\n", err, msg); 52 | } else { 53 | ALOGE("(%d) %s\n", err, msg); 54 | } 55 | } 56 | 57 | // Sets the global SQLite configuration. 58 | // This must be called before any other SQLite functions are called. 59 | static void sqliteInitialize() { 60 | // Enable multi-threaded mode. In this mode, SQLite is safe to use by multiple 61 | // threads as long as no two threads use the same database connection at the same 62 | // time (which we guarantee in the SQLite database wrappers). 63 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); 64 | 65 | // Redirect SQLite log messages to the Android log. 66 | bool verboseLog = false; 67 | sqlite3_config(SQLITE_CONFIG_LOG, &sqliteLogCallback, verboseLog ? (void*)1 : NULL); 68 | 69 | // The soft heap limit prevents the page cache allocations from growing 70 | // beyond the given limit, no matter what the max page cache sizes are 71 | // set to. The limit does not, as of 3.5.0, affect any other allocations. 72 | sqlite3_soft_heap_limit(SOFT_HEAP_LIMIT); 73 | 74 | // Initialize SQLite. 75 | sqlite3_initialize(); 76 | } 77 | 78 | static jint nativeReleaseMemory(JNIEnv* env, jclass clazz) { 79 | return sqlite3_release_memory(SOFT_HEAP_LIMIT); 80 | } 81 | 82 | static const JNINativeMethod sMethods[] = 83 | { 84 | /* name, signature, funcPtr */ 85 | { "nativeReleaseMemory", "()I", (void*)nativeReleaseMemory }, 86 | }; 87 | 88 | int register_android_database_SQLiteGlobal(JNIEnv *env) 89 | { 90 | sqliteInitialize(); 91 | 92 | return jniRegisterNativeMethods(env, "net/zetetic/database/sqlcipher/SQLiteGlobal", 93 | sMethods, NELEM(sMethods)); 94 | } 95 | 96 | } // namespace android 97 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/nativehelper/JniConstants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef JNI_CONSTANTS_H_included 18 | #define JNI_CONSTANTS_H_included 19 | 20 | #include "JNIHelp.h" 21 | 22 | /** 23 | * A cache to avoid calling FindClass at runtime. 24 | * 25 | * Class lookup is relatively expensive (2.5us on passion-eng at the time of writing), so we do 26 | * all such lookups eagerly at startup. This means that code that never uses, say, 27 | * java.util.zip.Deflater still has to pay for the lookup, but it means that on a device the cost 28 | * is definitely paid during boot and amortized. A central cache also removes the temptation to 29 | * dynamically call FindClass rather than add a small cache to each file that needs one. Another 30 | * cost is that each class cached here requires a global reference, though in practice we save 31 | * enough by not having a global reference for each file that uses a class such as java.lang.String 32 | * which is used in several files. 33 | * 34 | * FindClass is still called in a couple of situations: when throwing exceptions, and in some of 35 | * the serialization code. The former is clearly not a performance case, and we're currently 36 | * assuming that neither is the latter. 37 | * 38 | * TODO: similar arguments hold for field and method IDs; we should cache them centrally too. 39 | */ 40 | struct JniConstants { 41 | static void init(JNIEnv* env); 42 | 43 | static jclass bidiRunClass; 44 | static jclass bigDecimalClass; 45 | static jclass booleanClass; 46 | static jclass byteArrayClass; 47 | static jclass byteClass; 48 | static jclass calendarClass; 49 | static jclass characterClass; 50 | static jclass charsetICUClass; 51 | static jclass constructorClass; 52 | static jclass deflaterClass; 53 | static jclass doubleClass; 54 | static jclass errnoExceptionClass; 55 | static jclass fieldClass; 56 | static jclass fieldPositionIteratorClass; 57 | static jclass fileDescriptorClass; 58 | static jclass floatClass; 59 | static jclass gaiExceptionClass; 60 | static jclass inet6AddressClass; 61 | static jclass inetAddressClass; 62 | static jclass inetSocketAddressClass; 63 | static jclass inetUnixAddressClass; 64 | static jclass inflaterClass; 65 | static jclass inputStreamClass; 66 | static jclass integerClass; 67 | static jclass localeDataClass; 68 | static jclass longClass; 69 | static jclass methodClass; 70 | static jclass mutableIntClass; 71 | static jclass mutableLongClass; 72 | static jclass objectClass; 73 | static jclass objectArrayClass; 74 | static jclass outputStreamClass; 75 | static jclass parsePositionClass; 76 | static jclass patternSyntaxExceptionClass; 77 | static jclass realToStringClass; 78 | static jclass referenceClass; 79 | static jclass shortClass; 80 | static jclass socketClass; 81 | static jclass socketImplClass; 82 | static jclass stringClass; 83 | static jclass structAddrinfoClass; 84 | static jclass structFlockClass; 85 | static jclass structGroupReqClass; 86 | static jclass structLingerClass; 87 | static jclass structPasswdClass; 88 | static jclass structPollfdClass; 89 | static jclass structStatClass; 90 | static jclass structStatVfsClass; 91 | static jclass structTimevalClass; 92 | static jclass structUcredClass; 93 | static jclass structUtsnameClass; 94 | }; 95 | 96 | #define NATIVE_METHOD(className, functionName, signature) \ 97 | { #functionName, signature, reinterpret_cast(className ## _ ## functionName) } 98 | 99 | #endif // JNI_CONSTANTS_H_included 100 | -------------------------------------------------------------------------------- /sqlcipher/src/main/jni/sqlcipher/nativehelper/ScopedLocalRef.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef SCOPED_LOCAL_REF_H_included 18 | #define SCOPED_LOCAL_REF_H_included 19 | 20 | #include "jni.h" 21 | 22 | #include 23 | 24 | // A smart pointer that deletes a JNI local reference when it goes out of scope. 25 | template 26 | class ScopedLocalRef { 27 | public: 28 | ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) { 29 | } 30 | 31 | ~ScopedLocalRef() { 32 | reset(); 33 | } 34 | 35 | void reset(T ptr = NULL) { 36 | if (ptr != mLocalRef) { 37 | if (mLocalRef != NULL) { 38 | mEnv->DeleteLocalRef(mLocalRef); 39 | } 40 | mLocalRef = ptr; 41 | } 42 | } 43 | 44 | T release() __attribute__((warn_unused_result)) { 45 | T localRef = mLocalRef; 46 | mLocalRef = NULL; 47 | return localRef; 48 | } 49 | 50 | T get() const { 51 | return mLocalRef; 52 | } 53 | 54 | private: 55 | JNIEnv* mEnv; 56 | T mLocalRef; 57 | 58 | // Disallow copy and assignment. 59 | ScopedLocalRef(const ScopedLocalRef&); 60 | void operator=(const ScopedLocalRef&); 61 | }; 62 | 63 | #endif // SCOPED_LOCAL_REF_H_included 64 | -------------------------------------------------------------------------------- /sqlcipher/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Database 3 | 4 | -------------------------------------------------------------------------------- /sqlciphertest/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 30 5 | 6 | defaultConfig { 7 | applicationId "net.zetetic.customsqlciphertest" 8 | minSdkVersion 16 9 | targetSdkVersion 30 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | ndkVersion "21.3.6528147" 21 | 22 | useLibrary 'android.test.base' // for android.test.AndroidTestCase 23 | useLibrary 'android.test.runner' // for android.test.ApplicationTestCase extends AndroidTestCase 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(include: ['*.jar'], dir: 'libs') 28 | testImplementation 'junit:junit:4.13' 29 | implementation 'androidx.appcompat:appcompat:1.1.0' 30 | implementation project(':sqlcipher') 31 | } 32 | -------------------------------------------------------------------------------- /sqlciphertest/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/dan/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sqlciphertest/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /sqlciphertest/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 22 | 23 | 27 | 28 |