├── .editorconfig ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── config.yml │ └── issue.md ├── PULL_REQUEST_TEMPLATE.md └── stale.yml ├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── SQLCIPHER_LICENSE ├── android-database-sqlcipher ├── build-openssl-libraries.sh ├── build.gradle ├── maven.gradle ├── native.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── net │ │ └── sqlcipher │ │ └── IContentObserver.aidl │ ├── cpp │ ├── Android.mk │ ├── Application32.mk │ ├── Application64.mk │ ├── CursorWindow.cpp │ ├── CursorWindow.h │ ├── jni_elements.h │ ├── jni_exception.cpp │ ├── jni_exception.h │ ├── log.h │ ├── net_sqlcipher_CursorWindow.cpp │ ├── net_sqlcipher_database_SQLiteCompiledSql.cpp │ ├── net_sqlcipher_database_SQLiteDatabase.cpp │ ├── net_sqlcipher_database_SQLiteDebug.cpp │ ├── net_sqlcipher_database_SQLiteProgram.cpp │ ├── net_sqlcipher_database_SQLiteQuery.cpp │ ├── net_sqlcipher_database_SQLiteStatement.cpp │ ├── sqlcipher_loading.h │ └── sqlite3_exception.h │ ├── java │ └── net │ │ └── sqlcipher │ │ ├── AbstractCursor.java │ │ ├── AbstractWindowedCursor.java │ │ ├── BulkCursorNative.java │ │ ├── BulkCursorToCursorAdaptor.java │ │ ├── CrossProcessCursorWrapper.java │ │ ├── Cursor.java │ │ ├── CursorIndexOutOfBoundsException.java │ │ ├── CursorWindow.java │ │ ├── CursorWindowAllocation.java │ │ ├── CursorWrapper.java │ │ ├── CustomCursorWindowAllocation.java │ │ ├── DatabaseErrorHandler.java │ │ ├── DatabaseUtils.java │ │ ├── DefaultCursorWindowAllocation.java │ │ ├── DefaultDatabaseErrorHandler.java │ │ ├── IBulkCursor.java │ │ ├── InvalidRowColumnException.java │ │ ├── MatrixCursor.java │ │ ├── RowAllocationException.java │ │ ├── StaleDataException.java │ │ ├── UnknownTypeException.java │ │ ├── database │ │ ├── DatabaseObjectNotClosedException.java │ │ ├── SQLiteClosable.java │ │ ├── SQLiteCompiledSql.java │ │ ├── SQLiteContentHelper.java │ │ ├── SQLiteCursor.java │ │ ├── SQLiteCursorDriver.java │ │ ├── SQLiteDatabase.java │ │ ├── SQLiteDatabaseHook.java │ │ ├── SQLiteDebug.java │ │ ├── SQLiteDirectCursorDriver.java │ │ ├── SQLiteOpenHelper.java │ │ ├── SQLiteProgram.java │ │ ├── SQLiteQuery.java │ │ ├── SQLiteQueryBuilder.java │ │ ├── SQLiteQueryStats.java │ │ ├── SQLiteStatement.java │ │ ├── SQLiteTransactionListener.java │ │ ├── SqliteWrapper.java │ │ ├── SupportFactory.java │ │ ├── SupportHelper.java │ │ └── package-info.java │ │ └── package-info.java │ └── res │ └── values │ └── android_database_sqlcipher_strings.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | charset = utf-8 4 | 5 | [*.{java,cpp,h,aidl,xml,md}] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | end_of_line = lf 11 | 12 | [*.gradle] 13 | indent_style = space 14 | indent_size = 2 15 | trim_trailing_whitespace = true 16 | insert_final_newline = true 17 | end_of_line = lf 18 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | We ask that you submit a contributor agreement in order for us to accept this request. More information about this agreement can be found [here](https://www.zetetic.net/contributions/). Pull requests are merged into the `master` branch. Please let us know if you have any further questions. Thanks! 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💬 Community support 4 | url: https://discuss.zetetic.net/c/sqlcipher/5 5 | about: Integration problem or question about SQLCipher for Android, feel free to ask here. 6 | - name: 🔨 Build issue 7 | url: https://discuss.zetetic.net/c/sqlcipher/5 8 | about: Experience an issue building SQLCipher for Android? Start here. 9 | - name: 📃 SQLCipher documentation 10 | url: https://www.zetetic.net/sqlcipher/sqlcipher-api/ 11 | about: SQLCipher documentation can be found here. 12 | - name: 📖 Contribution instructions 13 | url: https://www.zetetic.net/contributions/ 14 | about: Want to contribute to SQLCipher for Android? Start here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🛠️ Bug report 3 | about: Create a report about a software defect 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ### Expected Behavior 10 | 11 | ### Actual Behavior 12 | 13 | ### Steps to Reproduce 14 | 15 | SQLCipher version (can be identified by executing `PRAGMA cipher_version;`): 16 | 17 | SQLCipher for Android version: 18 | 19 | Are you able to reproduce this issue within the SQLCipher for Android [test suite](https://github.com/sqlcipher/sqlcipher-android-tests)? 20 | 21 | *Note:* If you are not posting a specific issue for the SQLCipher library, please post your question to the SQLCipher [discuss site](https://discuss.zetetic.net/c/sqlcipher). Thanks! 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Changes proposed in this pull request: 2 | - 3 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | # Number of days of inactivity before an issue becomes stale 3 | daysUntilStale: 14 4 | # Number of days of inactivity before a stale issue is closed 5 | daysUntilClose: 14 6 | # Issues with these labels will never be considered stale 7 | exemptLabels: 8 | - bug 9 | - enhancement 10 | - security 11 | # Label to use when marking an issue as stale 12 | staleLabel: stale 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | Hello, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. 16 | You may also label this issue as "bug", "enhancement", or "security" and I will leave it open. 17 | Thank you for your contributions. 18 | # Comment to post when closing a stale issue. Set to `false` to disable 19 | closeComment: > 20 | Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to reopen with up-to-date information. 21 | only: issues 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build 3 | .DS_Store 4 | android-database-sqlcipher/src/main/external/sqlcipher 5 | android-database-sqlcipher/src/main/external/openssl 6 | android-database-sqlcipher/src/main/external/android-libs/ 7 | android-database-sqlcipher/.externalNativeBuild/ 8 | android-database-sqlcipher/src/main/libs* 9 | android-database-sqlcipher/src/main/obj 10 | android-database-sqlcipher/src/main/external/openssl-*/ 11 | android-database-sqlcipher/src/main/cpp/sqlite3.[c,h] 12 | .idea/ 13 | *.iml 14 | local.properties -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/android-database-sqlcipher/b3df67d8731bd0ebd987acb561a2c3fdd8e17082/.gitmodules -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | .PHONY: init clean distclean build-openssl build publish-local-snapshot \ 3 | publish-local-release publish-remote-snapshot public-remote-release check 4 | GRADLE = ./gradlew 5 | 6 | clean: 7 | $(GRADLE) clean 8 | 9 | distclean: 10 | $(GRADLE) distclean \ 11 | -PsqlcipherRoot="$(SQLCIPHER_ROOT)" 12 | 13 | build-openssl: 14 | $(GRADLE) buildOpenSSL 15 | 16 | check: 17 | $(GRADLE) check 18 | 19 | format: 20 | $(GRADLE) editorconfigFormat 21 | 22 | build-debug: 23 | $(GRADLE) android-database-sqlcipher:bundleDebugAar \ 24 | -PdebugBuild=true \ 25 | -PsqlcipherRoot="$(SQLCIPHER_ROOT)" \ 26 | -PopensslRoot="$(OPENSSL_ROOT)" \ 27 | -PopensslAndroidNativeRoot="$(OPENSSL_ANDROID_LIB_ROOT)" \ 28 | -PsqlcipherCFlags="$(SQLCIPHER_CFLAGS)" \ 29 | -PsqlcipherAndroidClientVersion="$(SQLCIPHER_ANDROID_VERSION)" 30 | 31 | build-release: 32 | $(GRADLE) android-database-sqlcipher:bundleReleaseAar \ 33 | -PdebugBuild=false \ 34 | -PsqlcipherRoot="$(SQLCIPHER_ROOT)" \ 35 | -PopensslRoot="$(OPENSSL_ROOT)" \ 36 | -PopensslAndroidNativeRoot="$(OPENSSL_ANDROID_LIB_ROOT)" \ 37 | -PsqlcipherCFlags="$(SQLCIPHER_CFLAGS)" \ 38 | -PsqlcipherAndroidClientVersion="$(SQLCIPHER_ANDROID_VERSION)" 39 | 40 | publish-local-snapshot: 41 | @ $(collect-signing-info) \ 42 | $(GRADLE) \ 43 | -PpublishSnapshot=true \ 44 | -PpublishLocal=true \ 45 | -PsigningKeyId="$$gpgKeyId" \ 46 | -PsigningKeyRingFile="$$gpgKeyRingFile" \ 47 | -PsigningKeyPassword="$$gpgPassword" \ 48 | uploadArchives 49 | 50 | publish-local-release: 51 | @ $(collect-signing-info) \ 52 | $(GRADLE) \ 53 | -PpublishSnapshot=false \ 54 | -PpublishLocal=true \ 55 | -PsigningKeyId="$$gpgKeyId" \ 56 | -PsigningKeyRingFile="$$gpgKeyRingFile" \ 57 | -PsigningKeyPassword="$$gpgPassword" \ 58 | uploadArchives 59 | 60 | publish-remote-snapshot: 61 | @ $(collect-signing-info) \ 62 | $(collect-nexus-info) \ 63 | $(GRADLE) \ 64 | -PpublishSnapshot=true \ 65 | -PpublishLocal=false \ 66 | -PsigningKeyId="$$gpgKeyId" \ 67 | -PsigningKeyRingFile="$$gpgKeyRingFile" \ 68 | -PsigningKeyPassword="$$gpgPassword" \ 69 | -PnexusUsername="$$nexusUsername" \ 70 | -PnexusPassword="$$nexusPassword" \ 71 | uploadArchives 72 | 73 | publish-remote-release: 74 | @ $(collect-signing-info) \ 75 | $(collect-nexus-info) \ 76 | $(GRADLE) \ 77 | -PpublishSnapshot=false \ 78 | -PpublishLocal=false \ 79 | -PdebugBuild=false \ 80 | -PsigningKeyId="$$gpgKeyId" \ 81 | -PsigningKeyRingFile="$$gpgKeyRingFile" \ 82 | -PsigningKeyPassword="$$gpgPassword" \ 83 | -PnexusUsername="$$nexusUsername" \ 84 | -PnexusPassword="$$nexusPassword" \ 85 | -PsqlcipherRoot="$(SQLCIPHER_ROOT)" \ 86 | -PopensslRoot="$(OPENSSL_ROOT)" \ 87 | -PopensslAndroidLibRoot="$(OPENSSL_ANDROID_LIB_ROOT)" \ 88 | -PsqlcipherCFlags="$(SQLCIPHER_CFLAGS)" \ 89 | -PsqlcipherAndroidClientVersion="$(SQLCIPHER_ANDROID_VERSION)" \ 90 | android-database-sqlcipher:publish 91 | 92 | collect-nexus-info := \ 93 | read -p "Enter Nexus username:" nexusUsername; \ 94 | stty -echo; read -p "Enter Nexus password:" nexusPassword; stty echo; 95 | 96 | collect-signing-info := \ 97 | read -p "Enter GPG signing key id:" gpgKeyId; \ 98 | read -p "Enter full path to GPG keyring file \ 99 | (possibly ${HOME}/.gnupg/secring.gpg)" gpgKeyRingFile; \ 100 | stty -echo; read -p "Enter GPG password:" gpgPassword; stty echo; 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Deprecated Library 2 | 3 | The `android-database-sqlcipher` project has been [officially deprecated](https://www.zetetic.net/blog/2023/08/31/sqlcipher-4.5.5-release#sqlcipher-android-455). The long-term replacement is [`sqlcipher-android`](https://github.com/sqlcipher/sqlcipher-android). Instructions for migrating from `android-database-sqlcipher` to `sqlcipher-android`may be found [here](https://www.zetetic.net/sqlcipher/sqlcipher-for-android-migration/). 4 | 5 | 6 | ### Download Source and Binaries 7 | 8 | The latest AAR binary package information can be [here](https://www.zetetic.net/sqlcipher/open-source), the source can be found [here](https://github.com/sqlcipher/android-database-sqlcipher). 9 |

10 | 11 | ### Compatibility 12 | 13 | SQLCipher for Android runs on Android from 5.0 (API 21), for `armeabi-v7a`, `x86`, `x86_64`, and `arm64_v8a` architectures. 14 | 15 | ### Contributions 16 | 17 | 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. 18 | 19 | ### An Illustrative Terminal Listing 20 | 21 | A typical SQLite database in unencrypted, and visually parseable even as encoded text. The following example shows the difference between hexdumps of a standard SQLite database and one implementing SQLCipher. 22 | 23 | ``` 24 | ~ sjlombardo$ hexdump -C sqlite.db 25 | 00000000 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 |SQLite format 3.| 26 | … 27 | 000003c0 65 74 32 74 32 03 43 52 45 41 54 45 20 54 41 42 |et2t2.CREATE TAB| 28 | 000003d0 4c 45 20 74 32 28 61 2c 62 29 24 01 06 17 11 11 |LE t2(a,b)$…..| 29 | … 30 | 000007e0 20 74 68 65 20 73 68 6f 77 15 01 03 01 2f 01 6f | the show…./.o| 31 | 000007f0 6e 65 20 66 6f 72 20 74 68 65 20 6d 6f 6e 65 79 |ne for the money| 32 | 33 | ~ $ sqlite3 sqlcipher.db 34 | sqlite> PRAGMA KEY=’test123′; 35 | sqlite> CREATE TABLE t1(a,b); 36 | sqlite> INSERT INTO t1(a,b) VALUES (‘one for the money’, ‘two for the show’); 37 | sqlite> .quit 38 | 39 | ~ $ hexdump -C sqlcipher.db 40 | 00000000 84 d1 36 18 eb b5 82 90 c4 70 0d ee 43 cb 61 87 |.?6.?..?p.?C?a.| 41 | 00000010 91 42 3c cd 55 24 ab c6 c4 1d c6 67 b4 e3 96 bb |.B?..?| 42 | 00000bf0 8e 99 ee 28 23 43 ab a4 97 cd 63 42 8a 8e 7c c6 |..?(#C??.?cB..|?| 43 | 44 | ~ $ sqlite3 sqlcipher.db 45 | sqlite> SELECT * FROM t1; 46 | Error: file is encrypted or is not a database 47 | ``` 48 | (example courtesy of SQLCipher) 49 | 50 | ### Application Integration 51 | 52 | You have a two main options for using SQLCipher for Android in your app: 53 | 54 | - Using it with Room or other consumers of the `androidx.sqlite` API 55 | 56 | - Using the native SQLCipher for Android classes 57 | 58 | In both cases, you will need to add a dependency on `net.zetetic:android-database-sqlcipher`, 59 | such as having the following line in your module's `build.gradle` `dependencies` 60 | closure: 61 | 62 | ```gradle 63 | implementation "net.zetetic:android-database-sqlcipher:4.5.3" 64 | implementation "androidx.sqlite:sqlite:2.1.0" 65 | ``` 66 | 67 | (replacing `4.5.3` with the version you want) 68 | 69 | 70 | 71 | #### Using SQLCipher for Android With Room 72 | 73 | SQLCipher for Android has a `SupportFactory` class in the `net.sqlcipher.database` package 74 | that can be used to configure Room to use SQLCipher for Android. 75 | 76 | There are three `SupportFactory` constructors: 77 | 78 | - `SupportFactory(byte[] passphrase)` 79 | - `SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook)` 80 | - `SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook, boolean clearPassphrase)` 81 | 82 | All three take a `byte[]` to use as the passphrase (if you have a `char[]`, use 83 | `SQLiteDatabase.getBytes()` to get a suitable `byte[]` to use). 84 | 85 | Two offer a `SQLiteDatabaseHook` parameter that you can use 86 | for executing SQL statements before or after the passphrase is used to key 87 | the database. 88 | 89 | The three-parameter constructor also offers `clearPassphrase`, which defaults 90 | to `true` in the other two constructors. If `clearPassphrase` is set to `true`, 91 | this will zero out the bytes of the `byte[]` after we open the database. This 92 | is safest from a security standpoint, but it does mean that the `SupportFactory` 93 | instance is a single-use object. Attempting to reuse the `SupportFactory` 94 | instance later will result in being unable to open the database, because the 95 | passphrase will be wrong. If you think that you might need to reuse the 96 | `SupportFactory` instance, pass `false` for `clearPassphrase`. 97 | 98 | Then, pass your `SupportFactory` to `openHelperFactory()` on your `RoomDatabase.Builder`: 99 | 100 | ```java 101 | final byte[] passphrase = SQLiteDatabase.getBytes(userEnteredPassphrase); 102 | final SupportFactory factory = new SupportFactory(passphrase); 103 | final SomeDatabase room = Room.databaseBuilder(activity, SomeDatabase.class, DB_NAME) 104 | .openHelperFactory(factory) 105 | .build(); 106 | ``` 107 | 108 | Now, Room will make all of its database requests using SQLCipher for Android instead 109 | of the framework copy of SQLCipher. 110 | 111 | Note that `SupportFactory` should work with other consumers of the `androidx.sqlite` API; 112 | Room is merely a prominent example. 113 | 114 | #### Using SQLCipher for Android's Native API 115 | 116 | If you have existing SQLite code using classes like `SQLiteDatabase` and `SQLiteOpenHelper`, 117 | converting your code to use SQLCipher for Android mostly is a three-step process: 118 | 119 | 1. Replace all `android.database.sqlite.*` `import` statements with ones that 120 | use `net.sqlcipher.database.*` (e.g., convert `android.database.sqlite.SQLiteDatabase` 121 | to `net.sqlcipher.database.SQLiteDatabase`) 122 | 123 | 2. Before attempting to open a database, call `SQLiteDatabase.loadLibs()`, passing 124 | in a `Context` (e.g., add this to `onCreate()` of your `Application` subclass, using 125 | the `Application` itself as the `Context`) 126 | 127 | 3. When opening a database (e.g., `SQLiteDatabase.openOrCreateDatabase()`), pass 128 | in the passphrase as a `char[]` or `byte[]` 129 | 130 | The rest of your code may not need any changes. 131 | 132 | An article covering both integration of SQLCipher into an Android application as well as building the source can be found [here](https://www.zetetic.net/sqlcipher/sqlcipher-for-android/). 133 | 134 | ### ProGuard 135 | 136 | For applications which utilize ProGuard, a few additional rules must be included when using SQLCipher for Android. These rules instruct ProGuard to omit the renaming of the internal SQLCipher classes which are used via lookup from the JNI layer. It is worth noting that since SQLCipher or Android is based on open source code there is little value in obfuscating the library anyway. The more important use of ProGuard is to protect your application code and business logic. 137 | 138 | ``` 139 | -keep,includedescriptorclasses class net.sqlcipher.** { *; } 140 | -keep,includedescriptorclasses interface net.sqlcipher.** { *; } 141 | ``` 142 | 143 | ### Building 144 | 145 | In order to build `android-database-sqlcipher` from source you will need both the Android SDK, Gradle, Android NDK, SQLCipher core source directory, and an OpenSSL source directory. We currently recommend using Android NDK LTS version `23.0.7599858`. 146 | 147 | To complete the `make` command, the `ANDROID_NDK_HOME` environment variable must be defined which should point to your NDK root. Once you have cloned the repo, change directory into the root of the repository and run the following commands: 148 | 149 | ``` 150 | SQLCIPHER_ROOT=/some/path/to/sqlcipher-folder \ 151 | OPENSSL_ROOT=/some/path/to/openssl-folder \ 152 | SQLCIPHER_CFLAGS="-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2" \ 153 | SQLCIPHER_ANDROID_VERSION="4.5.3" \ 154 | make build-release 155 | ``` 156 | 157 | You may specify other build flags/features within `SQLCIPHER_CFLAGS`, however, specifying `-DSQLITE_HAS_CODEC` and `-DSQLITE_TEMP_STORE` is necessary in the list of flags. 158 | 159 | ### License 160 | 161 | The Android support libraries are licensed under Apache 2.0, in line with the Android OS code on which they are based. The SQLCipher code itself is licensed under a BSD-style license from Zetetic LLC. Finally, the original SQLite code itself is in the public domain. 162 | -------------------------------------------------------------------------------- /SQLCIPHER_LICENSE: -------------------------------------------------------------------------------- 1 | http://sqlcipher.net 2 | 3 | Copyright (c) 2010 Zetetic LLC 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of the ZETETIC LLC nor the 14 | names of its contributors may be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY 21 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 26 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /android-database-sqlcipher/build-openssl-libraries.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | MINIMUM_ANDROID_SDK_VERSION=$1 4 | MINIMUM_ANDROID_64_BIT_SDK_VERSION=$2 5 | OPENSSL_DIR=$3 6 | ANDROID_LIB_ROOT=$4 7 | 8 | (cd ${OPENSSL_DIR}; 9 | 10 | if [[ ! ${MINIMUM_ANDROID_SDK_VERSION} ]]; then 11 | echo "MINIMUM_ANDROID_SDK_VERSION was not provided, include and rerun" 12 | exit 1 13 | fi 14 | 15 | if [[ ! ${MINIMUM_ANDROID_64_BIT_SDK_VERSION} ]]; then 16 | echo "MINIMUM_ANDROID_64_BIT_SDK_VERSION was not provided, include and rerun" 17 | exit 1 18 | fi 19 | 20 | if [[ ! ${ANDROID_NDK_HOME} ]]; then 21 | echo "ANDROID_NDK_HOME environment variable not set, set and rerun" 22 | exit 1 23 | fi 24 | 25 | HOST_INFO=`uname -a` 26 | case ${HOST_INFO} in 27 | Darwin*) 28 | TOOLCHAIN_SYSTEM=darwin-x86_64 29 | ;; 30 | Linux*) 31 | if [[ "${HOST_INFO}" == *i686* ]] 32 | then 33 | TOOLCHAIN_SYSTEM=linux-x86 34 | else 35 | TOOLCHAIN_SYSTEM=linux-x86_64 36 | fi 37 | ;; 38 | *) 39 | echo "Toolchain unknown for host system" 40 | exit 1 41 | ;; 42 | esac 43 | 44 | NDK_TOOLCHAIN_VERSION=4.9 45 | OPENSSL_CONFIGURE_OPTIONS="-fPIC -fstack-protector-all no-idea no-camellia \ 46 | no-seed no-bf no-cast no-rc2 no-rc4 no-rc5 no-md2 \ 47 | no-md4 no-ecdh no-sock no-ssl3 \ 48 | no-dsa no-dh no-ec no-ecdsa no-tls1 \ 49 | no-rfc3779 no-whirlpool no-srp \ 50 | no-mdc2 no-ecdh no-engine \ 51 | no-srtp" 52 | 53 | rm -rf ${ANDROID_LIB_ROOT} 54 | 55 | for SQLCIPHER_TARGET_PLATFORM in armeabi-v7a x86 x86_64 arm64-v8a 56 | do 57 | echo "Building libcrypto.a for ${SQLCIPHER_TARGET_PLATFORM}" 58 | case "${SQLCIPHER_TARGET_PLATFORM}" in 59 | armeabi-v7a) 60 | CONFIGURE_ARCH="android-arm -march=armv7-a" 61 | ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION} 62 | OFFSET_BITS=32 63 | ;; 64 | x86) 65 | CONFIGURE_ARCH=android-x86 66 | ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION} 67 | OFFSET_BITS=32 68 | ;; 69 | x86_64) 70 | CONFIGURE_ARCH=android64-x86_64 71 | ANDROID_API_VERSION=${MINIMUM_ANDROID_64_BIT_SDK_VERSION} 72 | OFFSET_BITS=64 73 | ;; 74 | arm64-v8a) 75 | CONFIGURE_ARCH=android-arm64 76 | ANDROID_API_VERSION=${MINIMUM_ANDROID_64_BIT_SDK_VERSION} 77 | OFFSET_BITS=64 78 | ;; 79 | *) 80 | echo "Unsupported build platform:${SQLCIPHER_TARGET_PLATFORM}" 81 | exit 1 82 | esac 83 | TOOLCHAIN_BIN_PATH=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/${TOOLCHAIN_SYSTEM}/bin 84 | PATH=${TOOLCHAIN_BIN_PATH}:${PATH} \ 85 | ./Configure ${CONFIGURE_ARCH} \ 86 | -D__ANDROID_API__=${ANDROID_API_VERSION} \ 87 | -D_FILE_OFFSET_BITS=${OFFSET_BITS} \ 88 | ${OPENSSL_CONFIGURE_OPTIONS} 89 | 90 | if [[ $? -ne 0 ]]; then 91 | echo "Error executing:./Configure ${CONFIGURE_ARCH} ${OPENSSL_CONFIGURE_OPTIONS}" 92 | exit 1 93 | fi 94 | 95 | make clean 96 | PATH=${TOOLCHAIN_BIN_PATH}:${PATH} \ 97 | make build_libs 98 | 99 | if [[ $? -ne 0 ]]; then 100 | echo "Error executing make for platform:${SQLCIPHER_TARGET_PLATFORM}" 101 | exit 1 102 | fi 103 | mkdir -p ${ANDROID_LIB_ROOT}/${SQLCIPHER_TARGET_PLATFORM} 104 | mv libcrypto.a ${ANDROID_LIB_ROOT}/${SQLCIPHER_TARGET_PLATFORM} 105 | done 106 | ) 107 | -------------------------------------------------------------------------------- /android-database-sqlcipher/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.library" 2 | apply plugin: "org.ec4j.editorconfig" 3 | apply from: "native.gradle" 4 | apply from: "maven.gradle" 5 | 6 | android { 7 | 8 | compileSdkVersion "${compileAndroidSdkVersion}" as Integer 9 | 10 | defaultConfig { 11 | versionName "${clientVersionNumber}" 12 | minSdkVersion "${minimumAndroidSdkVersion}" 13 | targetSdkVersion "${targetAndroidSdkVersion}" 14 | versionCode 1 15 | versionName "${clientVersionNumber}" 16 | archivesBaseName = "${archivesBaseName}-${versionName}" 17 | } 18 | 19 | editorconfig { 20 | includes = ["src/**", "*.gradle"] 21 | excludes = ["src/main/external/sqlcipher/**", "src/main/external/openssl-*/**"] 22 | } 23 | 24 | buildTypes { 25 | debug { 26 | debuggable true 27 | buildConfigField("String", "VERSION_NAME", "\"${clientVersionNumber}\"") 28 | } 29 | release { 30 | debuggable false 31 | minifyEnabled false 32 | buildConfigField("String", "VERSION_NAME", "\"${clientVersionNumber}\"") 33 | } 34 | } 35 | 36 | sourceSets { 37 | main { 38 | jniLibs.srcDirs "${rootProject.ext.nativeRootOutputDir}/libs" 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation "androidx.sqlite:sqlite:2.2.0" 44 | } 45 | 46 | editorconfig { 47 | excludes = ['src/main/cpp/sqlite3.*', 48 | 'src/main/external/sqlcipher/**', 49 | 'src/main/external/openssl-*/**'] 50 | } 51 | 52 | clean.dependsOn cleanNative 53 | check.dependsOn editorconfigCheck 54 | buildNative.mustRunAfter buildAmalgamation 55 | buildAmalgamation.mustRunAfter buildOpenSSL 56 | preBuild.dependsOn([buildOpenSSL, buildAmalgamation, copyAmalgamation, buildNative]) 57 | buildNative.mustRunAfter(copyAmalgamation) 58 | } 59 | -------------------------------------------------------------------------------- /android-database-sqlcipher/maven.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "maven-publish" 2 | apply plugin: "signing" 3 | import org.gradle.plugins.signing.Sign 4 | 5 | def isReleaseBuild() { 6 | return mavenVersionName.contains("SNAPSHOT") == false 7 | } 8 | 9 | def getReleaseRepositoryUrl() { 10 | return hasProperty('mavenReleaseRepositoryUrl') ? mavenReleaseRepositoryUrl 11 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 12 | } 13 | 14 | def getSnapshotRepositoryUrl() { 15 | if(hasProperty('mavenLocalRepositoryPrefix')) { 16 | return "${mavenLocalRepositoryPrefix}${buildDir}/${mavenSnapshotRepositoryUrl}" 17 | } else { 18 | return hasProperty('mavenSnapshotRepositoryUrl') ? mavenSnapshotRepositoryUrl 19 | : "https://oss.sonatype.org/content/repositories/snapshots/" 20 | } 21 | } 22 | 23 | def getRepositoryUsername() { 24 | return hasProperty('nexusUsername') ? nexusUsername : "" 25 | } 26 | 27 | def getRepositoryPassword() { 28 | return hasProperty('nexusPassword') ? nexusPassword : "" 29 | } 30 | 31 | gradle.taskGraph.whenReady { taskGraph -> 32 | if (taskGraph.allTasks.any { it instanceof Sign }) { 33 | allprojects { ext."signing.keyId" = "${signingKeyId}" } 34 | allprojects { ext."signing.secretKeyRingFile" = "${signingKeyRingFile}" } 35 | allprojects { ext."signing.password" = "${signingKeyPassword}" } 36 | } 37 | } 38 | 39 | 40 | 41 | afterEvaluate { project -> 42 | publishing { 43 | publications { 44 | mavenJava(MavenPublication) { 45 | from components.release 46 | groupId = mavenGroup 47 | artifactId = mavenArtifactId 48 | version = mavenVersionName 49 | pom { 50 | name = mavenArtifactId 51 | description = mavenPomDescription 52 | url = mavenPomUrl 53 | licenses { 54 | license { 55 | url = mavenLicenseUrl 56 | } 57 | } 58 | developers { 59 | developer { 60 | name = mavenDeveloperName 61 | email = mavenDeveloperEmail 62 | } 63 | } 64 | scm { 65 | connection = mavenScmConnection 66 | developerConnection = mavenScmDeveloperConnection 67 | url = mavenScmUrl 68 | } 69 | } 70 | } 71 | } 72 | repositories { 73 | maven { 74 | def repoUrl = isReleaseBuild() 75 | ? getReleaseRepositoryUrl() 76 | : getSnapshotRepositoryUrl() 77 | url = repoUrl 78 | credentials { 79 | username = getRepositoryUsername() 80 | password = getRepositoryPassword() 81 | } 82 | } 83 | } 84 | } 85 | 86 | signing { 87 | required { isReleaseBuild() && gradle.taskGraph.hasTask("publish") } 88 | sign publishing.publications.mavenJava 89 | } 90 | 91 | task androidSourcesJar(type: Jar) { 92 | classifier = "sources" 93 | from android.sourceSets.main.java.sourceFiles 94 | } 95 | 96 | artifacts { 97 | archives androidSourcesJar 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /android-database-sqlcipher/native.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.internal.logging.text.StyledTextOutputFactory 2 | import static org.gradle.internal.logging.text.StyledTextOutput.Style 3 | 4 | task buildOpenSSL() { 5 | onlyIf { 6 | def armNativeFile = new File("${androidNativeRootDir}/armeabi-v7a/libcrypto.a") 7 | if (armNativeFile.exists()) { 8 | def out = services.get(StyledTextOutputFactory).create("") 9 | out.style(Style.Normal).text("${androidNativeRootDir}/armeabi-v7a/libcrypto.a exists").style(Style.Info).println(' SKIPPED') 10 | } 11 | return !armNativeFile.exists() 12 | } 13 | doLast { 14 | def nativeRootDirectory = new File("${androidNativeRootDir}") 15 | if(!nativeRootDirectory.exists()){ 16 | nativeRootDirectory.mkdirs() 17 | } 18 | exec { 19 | workingDir "${projectDir}" 20 | commandLine "./build-openssl-libraries.sh", 21 | "${minimumAndroidSdkVersion}", 22 | "${minimumAndroid64BitSdkVersion}", 23 | "${opensslDir}", 24 | "${androidNativeRootDir}" 25 | } 26 | } 27 | } 28 | 29 | task buildAmalgamation() { 30 | onlyIf { 31 | def amalgamation = new File("${sqlcipherDir}/sqlite3.c") 32 | return !amalgamation.exists() 33 | } 34 | doLast { 35 | exec { 36 | workingDir "${sqlcipherDir}" 37 | environment("CFLAGS", "${sqlcipherCFlags}") 38 | commandLine "./configure", "--enable-tempstore=yes", "--with-crypto-lib=none" 39 | } 40 | exec { 41 | workingDir "${sqlcipherDir}" 42 | environment("CFLAGS", "${sqlcipherCFlags}") 43 | commandLine "make", "sqlite3.c" 44 | } 45 | } 46 | } 47 | 48 | task copyAmalgamation() { 49 | doLast { 50 | exec { 51 | workingDir "${sqlcipherDir}" 52 | commandLine "cp", "sqlite3.c", "sqlite3.h", "${nativeRootOutputDir}/cpp/" 53 | } 54 | } 55 | } 56 | 57 | task buildNative() { 58 | description "Build the native SQLCipher binaries" 59 | doLast { 60 | executeNdkBuild( 61 | "${nativeRootOutputDir}/libs32", 62 | file("src/main/cpp").absolutePath, 63 | file("src/main/cpp/Application32.mk").absolutePath, 64 | "${sqlcipherCFlags}", "${otherSqlcipherCFlags}", 65 | "${minimumAndroidSdkVersion}") 66 | executeNdkBuild( 67 | "${nativeRootOutputDir}/libs64", 68 | file("src/main/cpp").absolutePath, 69 | file("src/main/cpp/Application64.mk").absolutePath, 70 | "${sqlcipherCFlags}", "${otherSqlcipherCFlags}", 71 | "${minimumAndroid64BitSdkVersion}") 72 | exec { 73 | workingDir "${nativeRootOutputDir}" 74 | commandLine "mkdir", "-p", "libs" 75 | } 76 | copy { 77 | from fileTree("${nativeRootOutputDir}/libs32").include("*/*") 78 | into "${nativeRootOutputDir}/libs" 79 | from fileTree("${nativeRootOutputDir}/libs64").include("*/*") 80 | into "${nativeRootOutputDir}/libs" 81 | } 82 | } 83 | } 84 | 85 | task cleanOpenSSL() { 86 | description "Clean the OpenSSL native libraries" 87 | doLast { 88 | logger.info "Cleaning OpenSSL native libraries" 89 | exec { 90 | workingDir "${androidNativeRootDir}" 91 | ignoreExitValue true 92 | commandLine "rm", "-rf" 93 | } 94 | } 95 | } 96 | 97 | task cleanSQLCipher() { 98 | description "Clean the SQLCipher source" 99 | doLast { 100 | exec { 101 | workingDir "${sqlcipherDir}" 102 | ignoreExitValue true 103 | commandLine "make", "clean" 104 | } 105 | File amalgamationDestinationSource = new File("${nativeRootOutputDir}/cpp/sqlite3.c") 106 | File amalgamationDestinationHeader = new File("${nativeRootOutputDir}/cpp/sqlite3.h") 107 | if (amalgamationDestinationSource.exists()) amalgamationDestinationSource.delete() 108 | if (amalgamationDestinationHeader.exists()) amalgamationDestinationHeader.delete() 109 | } 110 | } 111 | 112 | task cleanNative() { 113 | description "Clean the native (JNI) build artifacts" 114 | doLast { 115 | logger.info "Cleaning native build artifacts" 116 | ["libs", "libs32", "libs64", "obj"].each { 117 | File file = new File("${projectDir}/src/main/${it}") 118 | if (file.exists()) { 119 | file.deleteDir() 120 | } 121 | } 122 | } 123 | } 124 | 125 | task distclean() { 126 | description "Clean build, SQLCipher, and OpenSSL artifacts" 127 | dependsOn clean, cleanSQLCipher, cleanOpenSSL 128 | doLast { 129 | new File("${androidNativeRootDir}/").deleteDir() 130 | } 131 | } 132 | 133 | def gitClean(directory) { 134 | logger.info "Cleaning directory:${directory}" 135 | exec { 136 | workingDir "${directory}" 137 | commandLine "git", "checkout", "-f" 138 | } 139 | exec { 140 | workingDir "${directory}" 141 | commandLine "git", "clean", "-d", "-f" 142 | } 143 | } 144 | 145 | def executeNdkBuild(outputDir, androidMkDirectory, applicationMkFile, 146 | cflags, otherSqlcipherCFlags, androidVersion) { 147 | logger.info "Executing NDK build command" 148 | def out = services.get(StyledTextOutputFactory).create("") 149 | out.style(Style.Normal).text("SQLCIPHER_CFLAGS=").style(Style.Info).println("${cflags}") 150 | out.style(Style.Normal).text("OPENSSL_DIR=").style(Style.Info).println("${opensslDir}") 151 | out.style(Style.Normal).text("SQLCIPHER_DIR=").style(Style.Info).println("${sqlcipherDir}") 152 | out.style(Style.Normal).text("SQLCIPHER_OTHER_CFLAGS=").style(Style.Info).println("${otherSqlcipherCFlags}") 153 | out.style(Style.Normal).text("ANDROID_NATIVE_ROOT_DIR=").style(Style.Info).println("${androidNativeRootDir}") 154 | out.style(Style.Normal).text("NDK_APP_PLATFORM=").style(Style.Info).println("${androidVersion}") 155 | 156 | exec { 157 | def outputDirectory = "NDK_LIBS_OUT=${outputDir}" 158 | def applicationFile = "NDK_APPLICATION_MK=${applicationMkFile}" 159 | def environmentVariables = ["SQLCIPHER_CFLAGS" : "${cflags}", 160 | "OPENSSL_DIR" : "${opensslDir}", 161 | "SQLCIPHER_DIR" : "${sqlcipherDir}", 162 | "SQLCIPHER_OTHER_CFLAGS" : "${otherSqlcipherCFlags}", 163 | "ANDROID_NATIVE_ROOT_DIR": "${androidNativeRootDir}", 164 | "NDK_APP_PLATFORM" : "${androidVersion}"] 165 | environment(environmentVariables) 166 | commandLine "ndk-build", "V=1", "${ndkBuildType}", 167 | "--environment-overrides", outputDirectory, 168 | "-C", androidMkDirectory, applicationFile 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/aidl/net/sqlcipher/IContentObserver.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | ** 3 | ** Copyright 2007, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** 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 | package net.sqlcipher; 19 | 20 | /** 21 | * @hide 22 | */ 23 | interface IContentObserver 24 | { 25 | /** 26 | * This method is called when an update occurs to the cursor that is being 27 | * observed. selfUpdate is true if the update was caused by a call to 28 | * commit on the cursor that is being observed. 29 | */ 30 | oneway void onChange(boolean selfUpdate); 31 | } 32 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | MY_PATH := $(LOCAL_PATH) 3 | include $(CLEAR_VARS) 4 | LOCAL_PATH := $(MY_PATH) 5 | 6 | LOCAL_CFLAGS += $(SQLCIPHER_CFLAGS) $(SQLCIPHER_OTHER_CFLAGS) 7 | LOCAL_C_INCLUDES += $(LOCAL_PATH) 8 | LOCAL_LDLIBS := -llog 9 | LOCAL_LDFLAGS += -L$(ANDROID_NATIVE_ROOT_DIR)/$(TARGET_ARCH_ABI) 10 | LOCAL_STATIC_LIBRARIES += static-libcrypto 11 | LOCAL_MODULE := libsqlcipher 12 | LOCAL_SRC_FILES := sqlite3.c \ 13 | jni_exception.cpp \ 14 | net_sqlcipher_database_SQLiteCompiledSql.cpp \ 15 | net_sqlcipher_database_SQLiteDatabase.cpp \ 16 | net_sqlcipher_database_SQLiteProgram.cpp \ 17 | net_sqlcipher_database_SQLiteQuery.cpp \ 18 | net_sqlcipher_database_SQLiteStatement.cpp \ 19 | net_sqlcipher_CursorWindow.cpp \ 20 | CursorWindow.cpp 21 | 22 | include $(BUILD_SHARED_LIBRARY) 23 | 24 | include $(CLEAR_VARS) 25 | LOCAL_MODULE := static-libcrypto 26 | LOCAL_EXPORT_C_INCLUDES := $(OPENSSL_DIR)/include 27 | LOCAL_SRC_FILES := $(ANDROID_NATIVE_ROOT_DIR)/$(TARGET_ARCH_ABI)/libcrypto.a 28 | include $(PREBUILT_STATIC_LIBRARY) 29 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/Application32.mk: -------------------------------------------------------------------------------- 1 | APP_PROJECT_PATH := $(shell pwd) 2 | APP_ABI := armeabi-v7a x86 3 | APP_PLATFORM := android-$(NDK_APP_PLATFORM) 4 | APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk 5 | APP_STL := c++_static 6 | APP_CFLAGS := -D_FILE_OFFSET_BITS=32 7 | APP_LDFLAGS += -Wl,--exclude-libs,ALL 8 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/Application64.mk: -------------------------------------------------------------------------------- 1 | APP_PROJECT_PATH := $(shell pwd) 2 | APP_ABI := x86_64 arm64-v8a 3 | APP_PLATFORM := android-$(NDK_APP_PLATFORM) 4 | APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk 5 | APP_STL := c++_static 6 | APP_CFLAGS := -D_FILE_OFFSET_BITS=64 7 | APP_LDFLAGS += -Wl,--exclude-libs,ALL 8 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/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 | 17 | #ifndef _ANDROID__DATABASE_WINDOW_H 18 | #define _ANDROID__DATABASE_WINDOW_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "log.h" 27 | #include 28 | 29 | #define ROW_SLOT_CHUNK_NUM_ROWS 128 30 | #define INITIAL_WINDOW_SIZE (1024 * 1024) 31 | #define GROW_WINDOW_SIZE_EXTRA INITIAL_WINDOW_SIZE 32 | #define WINDOW_ALLOCATION_UNBOUNDED 0 33 | 34 | // Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS, 35 | // with an offset after the rows that points to the next chunk 36 | #define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t)) 37 | 38 | #if LOG_NDEBUG 39 | 40 | #define IF_LOG_WINDOW() if (false) 41 | #define LOG_WINDOW(...) 42 | 43 | #else 44 | 45 | #define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow") 46 | #define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__) 47 | 48 | #endif 49 | 50 | // When defined to true strings are stored as UTF8, otherwise they're UTF16 51 | #define WINDOW_STORAGE_UTF8 0 52 | 53 | // When defined to true numberic values are stored inline in the field_slot_t, 54 | // otherwise they're allocated in the window 55 | #define WINDOW_STORAGE_INLINE_NUMERICS 1 56 | 57 | using std::make_pair; 58 | using std::unordered_map; 59 | 60 | namespace sqlcipher { 61 | 62 | typedef struct 63 | { 64 | uint32_t numRows; 65 | uint32_t numColumns; 66 | } window_header_t; 67 | 68 | typedef struct 69 | { 70 | uint32_t offset; 71 | } row_slot_t; 72 | 73 | typedef struct 74 | { 75 | uint8_t type; 76 | union { 77 | double d; 78 | int64_t l; 79 | struct { 80 | uint32_t offset; 81 | uint32_t size; 82 | } buffer; 83 | } data; 84 | } __attribute__((packed)) field_slot_t; 85 | 86 | #define FIELD_TYPE_INTEGER 1 87 | #define FIELD_TYPE_FLOAT 2 88 | #define FIELD_TYPE_STRING 3 89 | #define FIELD_TYPE_BLOB 4 90 | #define FIELD_TYPE_NULL 0 91 | 92 | /** 93 | * This class stores a set of rows from a database in a buffer. The begining of the 94 | * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by 95 | * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case 96 | * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a 97 | * field_slot_t per column, which has the size, offset, and type of the data for that field. 98 | * Note that the data types come from sqlite3.h. 99 | */ 100 | class CursorWindow 101 | { 102 | public: 103 | CursorWindow(size_t initialSize, size_t growthPaddingSize, size_t maxSize); 104 | CursorWindow(){} 105 | ~CursorWindow(); 106 | 107 | bool initBuffer(bool localOnly); 108 | size_t size() {return mSize;} 109 | uint8_t * data() {return mData;} 110 | uint32_t getNumRows() {return mHeader->numRows;} 111 | uint32_t getNumColumns() {return mHeader->numColumns;} 112 | void freeLastRow() { 113 | if (mHeader->numRows > 0) { 114 | mHeader->numRows--; 115 | } 116 | } 117 | bool setNumColumns(uint32_t numColumns) 118 | { 119 | uint32_t cur = mHeader->numColumns; 120 | if (cur > 0 && cur != numColumns) { 121 | LOGE("Trying to go from %d columns to %d", cur, numColumns); 122 | return false; 123 | } 124 | mHeader->numColumns = numColumns; 125 | return true; 126 | } 127 | 128 | int32_t freeSpace(); 129 | 130 | void clear(); 131 | 132 | /** 133 | * Allocate a row slot and its directory. The returned 134 | * pointer points to the begining of the row's directory 135 | * or NULL if there wasn't room. The directory is 136 | * initialied with NULL entries for each field. 137 | */ 138 | field_slot_t * allocRow(); 139 | 140 | /** 141 | * Allocate a portion of the window. Returns the offset 142 | * of the allocation, or 0 if there isn't enough space. 143 | * If aligned is true, the allocation gets 4 byte alignment. 144 | */ 145 | uint32_t alloc(size_t size, bool aligned = false); 146 | 147 | uint32_t read_field_slot(int row, int column, field_slot_t * slot); 148 | 149 | /** 150 | * Copy data into the window at the given offset. 151 | */ 152 | void copyIn(uint32_t offset, uint8_t const * data, size_t size); 153 | void copyIn(uint32_t offset, int64_t data); 154 | void copyIn(uint32_t offset, double data); 155 | 156 | void copyOut(uint32_t offset, uint8_t * data, size_t size); 157 | int64_t copyOutLong(uint32_t offset); 158 | double copyOutDouble(uint32_t offset); 159 | 160 | bool putLong(unsigned int row, unsigned int col, int64_t value); 161 | bool putDouble(unsigned int row, unsigned int col, double value); 162 | bool putNull(unsigned int row, unsigned int col); 163 | 164 | bool getLong(unsigned int row, unsigned int col, int64_t * valueOut); 165 | bool getDouble(unsigned int row, unsigned int col, double * valueOut); 166 | bool getNull(unsigned int row, unsigned int col, bool * valueOut); 167 | 168 | uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;} 169 | 170 | row_slot_t * allocRowSlot(); 171 | 172 | row_slot_t * getRowSlot(int row); 173 | 174 | /** 175 | * return NULL if Failed to find rowSlot or 176 | * Invalid rowSlot 177 | */ 178 | field_slot_t * getFieldSlotWithCheck(int row, int column); 179 | field_slot_t * getFieldSlot(int row, int column) 180 | { 181 | int fieldDirOffset = getRowSlot(row)->offset; 182 | return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column; 183 | } 184 | 185 | private: 186 | uint8_t * mData; 187 | size_t mSize; 188 | size_t mInitialSize; 189 | size_t mGrowthPaddingSize; 190 | size_t mMaxSize; 191 | window_header_t * mHeader; 192 | /** 193 | * Offset of the lowest unused data byte in the array. 194 | */ 195 | uint32_t mFreeOffset; 196 | unordered_map mChunkNumToNextChunkOffset; 197 | int mLastChunkPtrOffset; 198 | }; 199 | 200 | }; // namespace sqlcipher 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/jni_elements.h: -------------------------------------------------------------------------------- 1 | #ifndef NELEM 2 | # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 3 | #endif 4 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/jni_exception.cpp: -------------------------------------------------------------------------------- 1 | #include "jni_exception.h" 2 | 3 | void jniThrowException(JNIEnv* env, const char* exceptionClass, const char* sqlite3Message) { 4 | jclass exClass; 5 | exClass = env->FindClass(exceptionClass); 6 | env->ThrowNew(exClass, sqlite3Message); 7 | } 8 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/jni_exception.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _JNI_EXCEPTION_H 4 | #define _JNI_EXCEPTION_H 5 | void jniThrowException(JNIEnv* env, const char* exceptionClass, const char* sqlite3Message); 6 | #endif 7 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/log.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef LOG_NDEBUG 4 | #define LOGI(...) 5 | #define LOGE(...) 6 | #define LOGV(...) 7 | #define LOGD(...) 8 | #else 9 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) 10 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) 11 | #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) 12 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) 13 | #endif 14 | 15 | #ifndef LOG 16 | #define LOG(priority, tag, ...) \ 17 | LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) 18 | #endif 19 | 20 | #ifndef LOG_PRI 21 | #define LOG_PRI(priority, tag, ...) \ 22 | __android_log_print(priority, tag, __VA_ARGS__) 23 | #endif 24 | 25 | #ifndef LOG_ASSERT 26 | #define LOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) 27 | #endif 28 | 29 | #ifndef LOG_FATAL_IF 30 | #define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) 31 | #endif 32 | 33 | #ifndef LOG_ALWAYS_FATAL_IF 34 | #define LOG_ALWAYS_FATAL_IF(cond, ...) \ 35 | ( (CONDITION(cond)) \ 36 | ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ 37 | : (void)0 ) 38 | #endif 39 | 40 | #ifndef CONDITION 41 | #define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) 42 | #endif 43 | 44 | #define android_printAssert(a, b, ...) printf("%s: ", __VA_ARGS__) 45 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/net_sqlcipher_database_SQLiteCompiledSql.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006-2008 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 | #undef LOG_TAG 18 | #define LOG_TAG "Cursor" 19 | 20 | #include 21 | // #include 22 | // #include 23 | // #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "log.h" 31 | #include "jni_elements.h" 32 | #include "jni_exception.h" 33 | #include "sqlite3_exception.h" 34 | 35 | namespace sqlcipher { 36 | 37 | static jfieldID gHandleField; 38 | static jfieldID gStatementField; 39 | 40 | 41 | #define GET_STATEMENT(env, object) \ 42 | (sqlite3_stmt *)env->GetLongField(object, gStatementField) 43 | #define GET_HANDLE(env, object) \ 44 | (sqlite3 *)env->GetLongField(object, gHandleField) 45 | 46 | 47 | sqlite3_stmt * compile(JNIEnv* env, jobject object, 48 | sqlite3 * handle, jstring sqlString) 49 | { 50 | int err; 51 | jchar const * sql; 52 | jsize sqlLen; 53 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 54 | 55 | // Make sure not to leak the statement if it already exists 56 | if (statement != NULL) { 57 | sqlite3_finalize(statement); 58 | env->SetLongField(object, gStatementField, 0); 59 | } 60 | 61 | // Compile the SQL 62 | sql = env->GetStringChars(sqlString, NULL); 63 | sqlLen = env->GetStringLength(sqlString); 64 | err = sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL); 65 | env->ReleaseStringChars(sqlString, sql); 66 | 67 | if (err == SQLITE_OK) { 68 | // Store the statement in the Java object for future calls 69 | LOGV("Prepared statement %p on %p", statement, handle); 70 | env->SetLongField(object, gStatementField, (intptr_t)statement); 71 | return statement; 72 | } else { 73 | // Error messages like 'near ")": syntax error' are not 74 | // always helpful enough, so construct an error string that 75 | // includes the query itself. 76 | const char *query = env->GetStringUTFChars(sqlString, NULL); 77 | char *message = (char*) malloc(strlen(query) + 50); 78 | if (message) { 79 | strcpy(message, ", while compiling: "); // less than 50 chars 80 | strcat(message, query); 81 | } 82 | env->ReleaseStringUTFChars(sqlString, query); 83 | throw_sqlite3_exception(env, handle, message); 84 | free(message); 85 | return NULL; 86 | } 87 | } 88 | 89 | static void native_compile(JNIEnv* env, jobject object, jstring sqlString) 90 | { 91 | compile(env, object, GET_HANDLE(env, object), sqlString); 92 | } 93 | 94 | static void native_finalize(JNIEnv* env, jobject object) 95 | { 96 | int err; 97 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 98 | 99 | if (statement != NULL) { 100 | sqlite3_finalize(statement); 101 | env->SetLongField(object, gStatementField, 0); 102 | } 103 | } 104 | 105 | static JNINativeMethod sMethods[] = 106 | { 107 | /* name, signature, funcPtr */ 108 | {"native_compile", "(Ljava/lang/String;)V", (void *)native_compile}, 109 | {"native_finalize", "()V", (void *)native_finalize}, 110 | }; 111 | 112 | int register_android_database_SQLiteCompiledSql(JNIEnv * env) 113 | { 114 | jclass clazz; 115 | 116 | clazz = env->FindClass("net/sqlcipher/database/SQLiteCompiledSql"); 117 | if (clazz == NULL) { 118 | LOGE("Can't find net/sqlcipher/database/SQLiteCompiledSql"); 119 | return -1; 120 | } 121 | 122 | gHandleField = env->GetFieldID(clazz, "nHandle", "J"); 123 | gStatementField = env->GetFieldID(clazz, "nStatement", "J"); 124 | 125 | if (gHandleField == NULL || gStatementField == NULL) { 126 | LOGE("Error locating fields"); 127 | return -1; 128 | } 129 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 130 | } 131 | 132 | 133 | 134 | 135 | } // namespace sqlcipher 136 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/net_sqlcipher_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 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | // From mem_mspace.c in libsqlite 30 | extern "C" mspace sqlite3_get_mspace(); 31 | 32 | namespace sqlcipher { 33 | 34 | static jfieldID gMemoryUsedField; 35 | static jfieldID gPageCacheOverfloField; 36 | static jfieldID gLargestMemAllocField; 37 | 38 | 39 | #define USE_MSPACE 0 40 | 41 | static void getPagerStats(JNIEnv *env, jobject clazz, jobject statsObj) 42 | { 43 | int memoryUsed; 44 | int pageCacheOverflo; 45 | int largestMemAlloc; 46 | int unused; 47 | 48 | sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0); 49 | sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0); 50 | sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflo, &unused, 0); 51 | env->SetIntField(statsObj, gMemoryUsedField, memoryUsed); 52 | env->SetIntField(statsObj, gPageCacheOverfloField, pageCacheOverflo); 53 | env->SetIntField(statsObj, gLargestMemAllocField, largestMemAlloc); 54 | } 55 | 56 | static jlong getHeapSize(JNIEnv *env, jobject clazz) 57 | { 58 | #if !NO_MALLINFO 59 | struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace()); 60 | struct mallinfo info = dlmallinfo(); 61 | return (jlong) info.usmblks; 62 | #elif USE_MSPACE 63 | mspace space = sqlite3_get_mspace(); 64 | if (space != 0) { 65 | return mspace_footprint(space); 66 | } else { 67 | return 0; 68 | } 69 | #else 70 | return 0; 71 | #endif 72 | } 73 | 74 | static jlong getHeapAllocatedSize(JNIEnv *env, jobject clazz) 75 | { 76 | #if !NO_MALLINFO 77 | struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace()); 78 | return (jlong) info.uordblks; 79 | #else 80 | return sqlite3_memory_used(); 81 | #endif 82 | } 83 | 84 | static jlong getHeapFreeSize(JNIEnv *env, jobject clazz) 85 | { 86 | #if !NO_MALLINFO 87 | struct mallinfo info = mspace_mallinfo(sqlite3_get_mspace()); 88 | return (jlong) info.fordblks; 89 | #else 90 | return getHeapSize(env, clazz) - sqlite3_memory_used(); 91 | #endif 92 | } 93 | 94 | static int read_mapinfo(FILE *fp, 95 | int *sharedPages, int *privatePages) 96 | { 97 | char line[1024]; 98 | int len; 99 | int skip; 100 | 101 | unsigned start = 0, size = 0, resident = 0; 102 | unsigned shared_clean = 0, shared_dirty = 0; 103 | unsigned private_clean = 0, private_dirty = 0; 104 | unsigned referenced = 0; 105 | 106 | int isAnon = 0; 107 | int isHeap = 0; 108 | 109 | again: 110 | skip = 0; 111 | 112 | if(fgets(line, 1024, fp) == 0) return 0; 113 | 114 | len = strlen(line); 115 | if (len < 1) return 0; 116 | line[--len] = 0; 117 | 118 | /* ignore guard pages */ 119 | if (line[18] == '-') skip = 1; 120 | 121 | start = strtoul(line, 0, 16); 122 | 123 | if (len > 50 && !strncmp(line + 49, "/tmp/sqlite-heap", strlen("/tmp/sqlite-heap"))) { 124 | isHeap = 1; 125 | } 126 | 127 | if (fgets(line, 1024, fp) == 0) return 0; 128 | if (sscanf(line, "Size: %d kB", &size) != 1) return 0; 129 | if (fgets(line, 1024, fp) == 0) return 0; 130 | if (sscanf(line, "Rss: %d kB", &resident) != 1) return 0; 131 | if (fgets(line, 1024, fp) == 0) return 0; 132 | if (sscanf(line, "Shared_Clean: %d kB", &shared_clean) != 1) return 0; 133 | if (fgets(line, 1024, fp) == 0) return 0; 134 | if (sscanf(line, "Shared_Dirty: %d kB", &shared_dirty) != 1) return 0; 135 | if (fgets(line, 1024, fp) == 0) return 0; 136 | if (sscanf(line, "Private_Clean: %d kB", &private_clean) != 1) return 0; 137 | if (fgets(line, 1024, fp) == 0) return 0; 138 | if (sscanf(line, "Private_Dirty: %d kB", &private_dirty) != 1) return 0; 139 | if (fgets(line, 1024, fp) == 0) return 0; 140 | if (sscanf(line, "Referenced: %d kB", &referenced) != 1) return 0; 141 | 142 | if (skip) { 143 | goto again; 144 | } 145 | 146 | if (isHeap) { 147 | *sharedPages += shared_dirty; 148 | *privatePages += private_dirty; 149 | } 150 | return 1; 151 | } 152 | 153 | static void load_maps(int pid, int *sharedPages, int *privatePages) 154 | { 155 | char tmp[128]; 156 | FILE *fp; 157 | 158 | sprintf(tmp, "/proc/%d/smaps", pid); 159 | fp = fopen(tmp, "r"); 160 | if (fp == 0) return; 161 | 162 | while (read_mapinfo(fp, sharedPages, privatePages) != 0) { 163 | // Do nothing 164 | } 165 | fclose(fp); 166 | } 167 | 168 | static void getHeapDirtyPages(JNIEnv *env, jobject clazz, jintArray pages) 169 | { 170 | int _pages[2]; 171 | 172 | _pages[0] = 0; 173 | _pages[1] = 0; 174 | 175 | load_maps(getpid(), &_pages[0], &_pages[1]); 176 | 177 | // Convert from kbytes to 4K pages 178 | _pages[0] /= 4; 179 | _pages[1] /= 4; 180 | 181 | env->SetIntArrayRegion(pages, 0, 2, _pages); 182 | } 183 | 184 | /* 185 | * JNI registration. 186 | */ 187 | 188 | static JNINativeMethod gMethods[] = 189 | { 190 | { "getPagerStats", "(Lnet/sqlcipher/database/SQLiteDebug$PagerStats;)V", 191 | (void*) getPagerStats }, 192 | { "getHeapSize", "()J", (void*) getHeapSize }, 193 | { "getHeapAllocatedSize", "()J", (void*) getHeapAllocatedSize }, 194 | { "getHeapFreeSize", "()J", (void*) getHeapFreeSize }, 195 | { "getHeapDirtyPages", "([I)V", (void*) getHeapDirtyPages }, 196 | }; 197 | 198 | int register_android_database_SQLiteDebug(JNIEnv *env) 199 | { 200 | jclass clazz; 201 | 202 | clazz = env->FindClass("net/sqlcipher/database/SQLiteDebug$PagerStats"); 203 | if (clazz == NULL) { 204 | LOGE("Can't find net/sqlcipher/database/SQLiteDebug$PagerStats"); 205 | return -1; 206 | } 207 | 208 | gMemoryUsedField = env->GetFieldID(clazz, "memoryUsed", "I"); 209 | if (gMemoryUsedField == NULL) { 210 | LOGE("Can't find memoryUsed"); 211 | return -1; 212 | } 213 | 214 | gLargestMemAllocField = env->GetFieldID(clazz, "largestMemAlloc", "I"); 215 | if (gLargestMemAllocField == NULL) { 216 | LOGE("Can't find largestMemAlloc"); 217 | return -1; 218 | } 219 | 220 | gPageCacheOverfloField = env->GetFieldID(clazz, "pageCacheOverflo", "I"); 221 | if (gPageCacheOverfloField == NULL) { 222 | LOGE("Can't find pageCacheOverflo"); 223 | return -1; 224 | } 225 | 226 | return jniRegisterNativeMethods(env, "net/sqlcipher/database/SQLiteDebug", 227 | gMethods, NELEM(gMethods)); 228 | } 229 | 230 | } // namespace sqlcipher 231 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/net_sqlcipher_database_SQLiteProgram.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006-2008 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 | #undef LOG_TAG 18 | #define LOG_TAG "Cursor" 19 | 20 | // #include 21 | // #include 22 | // #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "log.h" 30 | #include "jni_elements.h" 31 | #include "jni_exception.h" 32 | #include "sqlite3_exception.h" 33 | 34 | namespace sqlcipher { 35 | 36 | static jfieldID gHandleField; 37 | static jfieldID gStatementField; 38 | 39 | 40 | #define GET_STATEMENT(env, object) \ 41 | (sqlite3_stmt *)env->GetLongField(object, gStatementField) 42 | #define GET_HANDLE(env, object) \ 43 | (sqlite3 *)env->GetLongField(object, gHandleField) 44 | 45 | static void native_compile(JNIEnv* env, jobject object, jstring sqlString) 46 | { 47 | char buf[65]; 48 | strcpy(buf, "android_database_SQLiteProgram->native_compile() not implemented"); 49 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 50 | return; 51 | } 52 | 53 | static void native_bind_null(JNIEnv* env, jobject object, 54 | jint index) 55 | { 56 | int err; 57 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 58 | 59 | err = sqlite3_bind_null(statement, index); 60 | if (err != SQLITE_OK) { 61 | char buf[32]; 62 | sprintf(buf, "handle %p", statement); 63 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 64 | return; 65 | } 66 | } 67 | 68 | static void native_bind_long(JNIEnv* env, jobject object, 69 | jint index, jlong value) 70 | { 71 | int err; 72 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 73 | 74 | err = sqlite3_bind_int64(statement, index, value); 75 | if (err != SQLITE_OK) { 76 | char buf[32]; 77 | sprintf(buf, "handle %p", statement); 78 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 79 | return; 80 | } 81 | } 82 | 83 | static void native_bind_double(JNIEnv* env, jobject object, 84 | jint index, jdouble value) 85 | { 86 | int err; 87 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 88 | 89 | err = sqlite3_bind_double(statement, index, value); 90 | if (err != SQLITE_OK) { 91 | char buf[32]; 92 | sprintf(buf, "handle %p", statement); 93 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 94 | return; 95 | } 96 | } 97 | 98 | static void native_bind_string(JNIEnv* env, jobject object, 99 | jint index, jstring sqlString) 100 | { 101 | int err; 102 | jchar const * sql; 103 | jsize sqlLen; 104 | sqlite3_stmt * statement= GET_STATEMENT(env, object); 105 | 106 | sql = env->GetStringChars(sqlString, NULL); 107 | sqlLen = env->GetStringLength(sqlString); 108 | err = sqlite3_bind_text16(statement, index, sql, sqlLen * 2, SQLITE_TRANSIENT); 109 | env->ReleaseStringChars(sqlString, sql); 110 | if (err != SQLITE_OK) { 111 | char buf[32]; 112 | sprintf(buf, "handle %p", statement); 113 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 114 | return; 115 | } 116 | } 117 | 118 | static void native_bind_blob(JNIEnv* env, jobject object, 119 | jint index, jbyteArray value) 120 | { 121 | int err; 122 | jchar const * sql; 123 | jsize sqlLen; 124 | sqlite3_stmt * statement= GET_STATEMENT(env, object); 125 | 126 | jint len = env->GetArrayLength(value); 127 | jbyte * bytes = env->GetByteArrayElements(value, NULL); 128 | 129 | err = sqlite3_bind_blob(statement, index, bytes, len, SQLITE_TRANSIENT); 130 | env->ReleaseByteArrayElements(value, bytes, JNI_ABORT); 131 | 132 | if (err != SQLITE_OK) { 133 | char buf[32]; 134 | sprintf(buf, "statement %p", statement); 135 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 136 | return; 137 | } 138 | } 139 | 140 | static void native_clear_bindings(JNIEnv* env, jobject object) 141 | { 142 | int err; 143 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 144 | 145 | err = sqlite3_clear_bindings(statement); 146 | if (err != SQLITE_OK) { 147 | throw_sqlite3_exception(env, GET_HANDLE(env, object)); 148 | return; 149 | } 150 | } 151 | 152 | static void native_finalize(JNIEnv* env, jobject object) 153 | { 154 | char buf[66]; 155 | strcpy(buf, "android_database_SQLiteProgram->native_finalize() not implemented"); 156 | throw_sqlite3_exception(env, GET_HANDLE(env, object), buf); 157 | return; 158 | } 159 | 160 | 161 | static JNINativeMethod sMethods[] = 162 | { 163 | /* name, signature, funcPtr */ 164 | {"native_bind_null", "(I)V", (void *)native_bind_null}, 165 | {"native_bind_long", "(IJ)V", (void *)native_bind_long}, 166 | {"native_bind_double", "(ID)V", (void *)native_bind_double}, 167 | {"native_bind_string", "(ILjava/lang/String;)V", (void *)native_bind_string}, 168 | {"native_bind_blob", "(I[B)V", (void *)native_bind_blob}, 169 | {"native_clear_bindings", "()V", (void *)native_clear_bindings}, 170 | }; 171 | 172 | int register_android_database_SQLiteProgram(JNIEnv * env) 173 | { 174 | jclass clazz; 175 | 176 | clazz = env->FindClass("net/sqlcipher/database/SQLiteProgram"); 177 | if (clazz == NULL) { 178 | LOGE("Can't find net/sqlcipher/database/SQLiteProgram"); 179 | return -1; 180 | } 181 | 182 | gHandleField = env->GetFieldID(clazz, "nHandle", "J"); 183 | gStatementField = env->GetFieldID(clazz, "nStatement", "J"); 184 | 185 | if (gHandleField == NULL || gStatementField == NULL) { 186 | LOGE("Error locating fields"); 187 | return -1; 188 | } 189 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 190 | } 191 | 192 | 193 | } // namespace sqlcipher 194 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/net_sqlcipher_database_SQLiteStatement.cpp: -------------------------------------------------------------------------------- 1 | /* //device/libs/android_runtime/android_database_SQLiteCursor.cpp 2 | ** 3 | ** Copyright 2006, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** 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 | #undef LOG_TAG 19 | #define LOG_TAG "Cursor" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "log.h" 28 | #include "jni_elements.h" 29 | #include "sqlite3_exception.h" 30 | 31 | namespace sqlcipher { 32 | 33 | 34 | sqlite3_stmt * compile(JNIEnv* env, jobject object, 35 | sqlite3 * handle, jstring sqlString); 36 | 37 | static jfieldID gHandleField; 38 | static jfieldID gStatementField; 39 | 40 | 41 | #define GET_STATEMENT(env, object) \ 42 | (sqlite3_stmt *)env->GetLongField(object, gStatementField) 43 | #define GET_HANDLE(env, object) \ 44 | (sqlite3 *)env->GetLongField(object, gHandleField) 45 | 46 | 47 | static void native_execute(JNIEnv* env, jobject object) 48 | { 49 | int err; 50 | sqlite3 * handle = GET_HANDLE(env, object); 51 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 52 | 53 | // Execute the statement 54 | err = sqlite3_step(statement); 55 | 56 | // Throw an exception if an error occured 57 | if (err != SQLITE_DONE) { 58 | throw_sqlite3_exception_errcode(env, err, 59 | sqlite3_extended_errcode(handle), 60 | sqlite3_errmsg(handle)); 61 | } 62 | 63 | // Reset the statment so it's ready to use again 64 | sqlite3_reset(statement); 65 | } 66 | 67 | static jlong native_1x1_long(JNIEnv* env, jobject object) 68 | { 69 | int err; 70 | sqlite3 * handle = GET_HANDLE(env, object); 71 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 72 | jlong value = -1; 73 | 74 | // Execute the statement 75 | err = sqlite3_step(statement); 76 | 77 | // Handle the result 78 | if (err == SQLITE_ROW) { 79 | // No errors, read the data and return it 80 | value = sqlite3_column_int64(statement, 0); 81 | } else { 82 | throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle)); 83 | } 84 | 85 | // Reset the statment so it's ready to use again 86 | sqlite3_reset(statement); 87 | 88 | return value; 89 | } 90 | 91 | static jstring native_1x1_string(JNIEnv* env, jobject object) 92 | { 93 | int err; 94 | sqlite3 * handle = GET_HANDLE(env, object); 95 | sqlite3_stmt * statement = GET_STATEMENT(env, object); 96 | jstring value = NULL; 97 | 98 | // Execute the statement 99 | err = sqlite3_step(statement); 100 | 101 | // Handle the result 102 | if (err == SQLITE_ROW) { 103 | // No errors, read the data and return it 104 | //char const * text = (char const *)sqlite3_column_text(statement, 0); 105 | 106 | const jchar *str = 0; 107 | jint strlength = 0; 108 | str = (const jchar*) sqlite3_column_text16(statement, 0); 109 | strlength = sqlite3_column_bytes16(statement, 0) / sizeof(jchar); 110 | value = str ? env->NewString(str, strlength) : NULL; 111 | } else { 112 | throw_sqlite3_exception_errcode(env, err, sqlite3_errmsg(handle)); 113 | } 114 | 115 | // Reset the statment so it's ready to use again 116 | sqlite3_reset(statement); 117 | 118 | return value; 119 | } 120 | 121 | 122 | static JNINativeMethod sMethods[] = 123 | { 124 | /* name, signature, funcPtr */ 125 | {"native_execute", "()V", (void *)native_execute}, 126 | {"native_1x1_long", "()J", (void *)native_1x1_long}, 127 | {"native_1x1_string", "()Ljava/lang/String;", (void *)native_1x1_string}, 128 | }; 129 | 130 | 131 | int register_android_database_SQLiteStatement(JNIEnv * env) 132 | { 133 | jclass clazz; 134 | 135 | clazz = env->FindClass("net/sqlcipher/database/SQLiteStatement"); 136 | if (clazz == NULL) { 137 | LOGE("Can't find net/sqlcipher/database/SQLiteStatement"); 138 | return -1; 139 | } 140 | 141 | gHandleField = env->GetFieldID(clazz, "nHandle", "J"); 142 | gStatementField = env->GetFieldID(clazz, "nStatement", "J"); 143 | 144 | if (gHandleField == NULL || gStatementField == NULL) { 145 | LOGE("Error locating fields"); 146 | return -1; 147 | } 148 | return env->RegisterNatives(clazz, sMethods, NELEM(sMethods)); 149 | } 150 | 151 | 152 | } // namespace sqlcipher 153 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/sqlcipher_loading.h: -------------------------------------------------------------------------------- 1 | /* //device/libs/include/android_runtime/sqlite3_exception.h 2 | ** 3 | ** Copyright 2007, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** 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 | 19 | #include 20 | /* #include */ 21 | /* #include */ 22 | 23 | #include 24 | 25 | namespace sqlcipher { 26 | 27 | int register_android_database_SQLiteDatabase(JNIEnv *env); 28 | 29 | int register_android_database_SQLiteCompiledSql(JNIEnv * env); 30 | 31 | int register_android_database_SQLiteQuery(JNIEnv * env); 32 | 33 | int register_android_database_SQLiteProgram(JNIEnv * env); 34 | 35 | int register_android_database_SQLiteStatement(JNIEnv * env); 36 | 37 | int register_android_database_SQLiteDebug(JNIEnv *env); 38 | 39 | int register_android_database_CursorWindow(JNIEnv *env); 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/cpp/sqlite3_exception.h: -------------------------------------------------------------------------------- 1 | /* //device/libs/include/android_runtime/sqlite3_exception.h 2 | ** 3 | ** Copyright 2007, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** 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 | #ifndef _SQLITE3_EXCEPTION_H 19 | #define _SQLITE3_EXCEPTION_H 1 20 | 21 | #include 22 | /* #include */ 23 | /* #include */ 24 | 25 | #include 26 | 27 | namespace sqlcipher { 28 | 29 | /* throw a SQLiteException with a message appropriate for the error in handle */ 30 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle); 31 | 32 | /* throw a SQLiteException with the given message */ 33 | void throw_sqlite3_exception(JNIEnv* env, const char* message); 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 | 40 | /* throw a SQLiteException for a given error code */ 41 | void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message); 42 | 43 | void throw_sqlite3_exception(JNIEnv* env, int errcode, 44 | const char* sqlite3Message, const char* message); 45 | 46 | void throw_sqlite3_exception_errcode(JNIEnv* env, 47 | int errcode, 48 | int extended_err_code, 49 | const char* message); 50 | } 51 | #endif // _SQLITE3_EXCEPTION_H 52 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/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 | 17 | package net.sqlcipher; 18 | 19 | import android.database.CharArrayBuffer; 20 | 21 | /** 22 | * A base class for Cursors that store their data in {@link CursorWindow}s. 23 | */ 24 | public abstract class AbstractWindowedCursor extends AbstractCursor 25 | { 26 | @Override 27 | public byte[] getBlob(int columnIndex) 28 | { 29 | checkPosition(); 30 | 31 | synchronized(mUpdatedRows) { 32 | if (isFieldUpdated(columnIndex)) { 33 | return (byte[])getUpdatedField(columnIndex); 34 | } 35 | } 36 | 37 | return mWindow.getBlob(mPos, columnIndex); 38 | } 39 | 40 | @Override 41 | public String getString(int columnIndex) 42 | { 43 | checkPosition(); 44 | 45 | synchronized(mUpdatedRows) { 46 | if (isFieldUpdated(columnIndex)) { 47 | return (String)getUpdatedField(columnIndex); 48 | } 49 | } 50 | 51 | return mWindow.getString(mPos, columnIndex); 52 | } 53 | 54 | @Override 55 | public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) 56 | { 57 | checkPosition(); 58 | 59 | synchronized(mUpdatedRows) { 60 | if (isFieldUpdated(columnIndex)) { 61 | super.copyStringToBuffer(columnIndex, buffer); 62 | } 63 | } 64 | 65 | mWindow.copyStringToBuffer(mPos, columnIndex, buffer); 66 | } 67 | 68 | @Override 69 | public short getShort(int columnIndex) 70 | { 71 | checkPosition(); 72 | 73 | synchronized(mUpdatedRows) { 74 | if (isFieldUpdated(columnIndex)) { 75 | Number value = (Number)getUpdatedField(columnIndex); 76 | return value.shortValue(); 77 | } 78 | } 79 | 80 | return mWindow.getShort(mPos, columnIndex); 81 | } 82 | 83 | @Override 84 | public int getInt(int columnIndex) 85 | { 86 | checkPosition(); 87 | 88 | synchronized(mUpdatedRows) { 89 | if (isFieldUpdated(columnIndex)) { 90 | Number value = (Number)getUpdatedField(columnIndex); 91 | return value.intValue(); 92 | } 93 | } 94 | 95 | return mWindow.getInt(mPos, columnIndex); 96 | } 97 | 98 | @Override 99 | public long getLong(int columnIndex) 100 | { 101 | checkPosition(); 102 | 103 | synchronized(mUpdatedRows) { 104 | if (isFieldUpdated(columnIndex)) { 105 | Number value = (Number)getUpdatedField(columnIndex); 106 | return value.longValue(); 107 | } 108 | } 109 | 110 | return mWindow.getLong(mPos, columnIndex); 111 | } 112 | 113 | @Override 114 | public float getFloat(int columnIndex) 115 | { 116 | checkPosition(); 117 | 118 | synchronized(mUpdatedRows) { 119 | if (isFieldUpdated(columnIndex)) { 120 | Number value = (Number)getUpdatedField(columnIndex); 121 | return value.floatValue(); 122 | } 123 | } 124 | 125 | return mWindow.getFloat(mPos, columnIndex); 126 | } 127 | 128 | @Override 129 | public double getDouble(int columnIndex) 130 | { 131 | checkPosition(); 132 | 133 | synchronized(mUpdatedRows) { 134 | if (isFieldUpdated(columnIndex)) { 135 | Number value = (Number)getUpdatedField(columnIndex); 136 | return value.doubleValue(); 137 | } 138 | } 139 | 140 | return mWindow.getDouble(mPos, columnIndex); 141 | } 142 | 143 | @Override 144 | public boolean isNull(int columnIndex) 145 | { 146 | checkPosition(); 147 | 148 | synchronized(mUpdatedRows) { 149 | if (isFieldUpdated(columnIndex)) { 150 | return getUpdatedField(columnIndex) == null; 151 | } 152 | } 153 | 154 | return mWindow.isNull(mPos, columnIndex); 155 | } 156 | 157 | public boolean isBlob(int columnIndex) 158 | { 159 | checkPosition(); 160 | 161 | synchronized(mUpdatedRows) { 162 | if (isFieldUpdated(columnIndex)) { 163 | Object object = getUpdatedField(columnIndex); 164 | return object == null || object instanceof byte[]; 165 | } 166 | } 167 | 168 | return mWindow.isBlob(mPos, columnIndex); 169 | } 170 | 171 | public boolean isString(int columnIndex) 172 | { 173 | checkPosition(); 174 | 175 | synchronized(mUpdatedRows) { 176 | if (isFieldUpdated(columnIndex)) { 177 | Object object = getUpdatedField(columnIndex); 178 | return object == null || object instanceof String; 179 | } 180 | } 181 | 182 | return mWindow.isString(mPos, columnIndex); 183 | } 184 | 185 | public boolean isLong(int columnIndex) 186 | { 187 | checkPosition(); 188 | 189 | synchronized(mUpdatedRows) { 190 | if (isFieldUpdated(columnIndex)) { 191 | Object object = getUpdatedField(columnIndex); 192 | return object != null && (object instanceof Integer || object instanceof Long); 193 | } 194 | } 195 | 196 | return mWindow.isLong(mPos, columnIndex); 197 | } 198 | 199 | public boolean isFloat(int columnIndex) 200 | { 201 | checkPosition(); 202 | 203 | synchronized(mUpdatedRows) { 204 | if (isFieldUpdated(columnIndex)) { 205 | Object object = getUpdatedField(columnIndex); 206 | return object != null && (object instanceof Float || object instanceof Double); 207 | } 208 | } 209 | 210 | return mWindow.isFloat(mPos, columnIndex); 211 | } 212 | 213 | @Override 214 | public int getType(int columnIndex) { 215 | checkPosition(); 216 | return mWindow.getType(mPos, columnIndex); 217 | } 218 | 219 | @Override 220 | protected void checkPosition() 221 | { 222 | super.checkPosition(); 223 | 224 | if (mWindow == null) { 225 | throw new StaleDataException("Access closed cursor"); 226 | } 227 | } 228 | 229 | @Override 230 | public CursorWindow getWindow() { 231 | return mWindow; 232 | } 233 | 234 | /** 235 | * Set a new cursor window to cursor, usually set a remote cursor window 236 | * @param window cursor window 237 | */ 238 | public void setWindow(CursorWindow window) { 239 | if (mWindow != null) { 240 | mWindow.close(); 241 | } 242 | mWindow = window; 243 | } 244 | 245 | public boolean hasWindow() { 246 | return mWindow != null; 247 | } 248 | 249 | /** 250 | * This needs be updated in {@link #onMove} by subclasses, and 251 | * needs to be set to NULL when the contents of the cursor change. 252 | */ 253 | protected CursorWindow mWindow; 254 | } 255 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorNative.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 | package net.sqlcipher; 18 | 19 | import android.os.Binder; 20 | import android.os.RemoteException; 21 | import android.os.IBinder; 22 | import android.os.Parcel; 23 | import android.os.Bundle; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | 28 | /** 29 | * Native implementation of the bulk cursor. This is only for use in implementing 30 | * IPC, application code should use the Cursor interface. 31 | * 32 | * {@hide} 33 | */ 34 | public abstract class BulkCursorNative extends Binder implements IBulkCursor 35 | { 36 | public BulkCursorNative() 37 | { 38 | attachInterface(this, descriptor); 39 | } 40 | 41 | /** 42 | * Cast a Binder object into a content resolver interface, generating 43 | * a proxy if needed. 44 | */ 45 | static public IBulkCursor asInterface(IBinder obj) 46 | { 47 | if (obj == null) { 48 | return null; 49 | } 50 | IBulkCursor in = (IBulkCursor)obj.queryLocalInterface(descriptor); 51 | if (in != null) { 52 | return in; 53 | } 54 | 55 | return new BulkCursorProxy(obj); 56 | } 57 | 58 | @Override 59 | public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 60 | throws RemoteException { 61 | try { 62 | switch (code) { 63 | case GET_CURSOR_WINDOW_TRANSACTION: { 64 | data.enforceInterface(IBulkCursor.descriptor); 65 | int startPos = data.readInt(); 66 | CursorWindow window = getWindow(startPos); 67 | if (window == null) { 68 | reply.writeInt(0); 69 | return true; 70 | } 71 | reply.writeNoException(); 72 | reply.writeInt(1); 73 | window.writeToParcel(reply, 0); 74 | return true; 75 | } 76 | 77 | case COUNT_TRANSACTION: { 78 | data.enforceInterface(IBulkCursor.descriptor); 79 | int count = count(); 80 | reply.writeNoException(); 81 | reply.writeInt(count); 82 | return true; 83 | } 84 | 85 | case GET_COLUMN_NAMES_TRANSACTION: { 86 | data.enforceInterface(IBulkCursor.descriptor); 87 | String[] columnNames = getColumnNames(); 88 | reply.writeNoException(); 89 | reply.writeInt(columnNames.length); 90 | int length = columnNames.length; 91 | for (int i = 0; i < length; i++) { 92 | reply.writeString(columnNames[i]); 93 | } 94 | return true; 95 | } 96 | 97 | case DEACTIVATE_TRANSACTION: { 98 | data.enforceInterface(IBulkCursor.descriptor); 99 | deactivate(); 100 | reply.writeNoException(); 101 | return true; 102 | } 103 | 104 | case CLOSE_TRANSACTION: { 105 | data.enforceInterface(IBulkCursor.descriptor); 106 | close(); 107 | reply.writeNoException(); 108 | return true; 109 | } 110 | 111 | case REQUERY_TRANSACTION: { 112 | data.enforceInterface(IBulkCursor.descriptor); 113 | IContentObserver observer = 114 | IContentObserver.Stub.asInterface(data.readStrongBinder()); 115 | CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); 116 | int count = requery(observer, window); 117 | reply.writeNoException(); 118 | reply.writeInt(count); 119 | reply.writeBundle(getExtras()); 120 | return true; 121 | } 122 | 123 | case UPDATE_ROWS_TRANSACTION: { 124 | data.enforceInterface(IBulkCursor.descriptor); 125 | // TODO - what ClassLoader should be passed to readHashMap? 126 | // TODO - switch to Bundle 127 | HashMap> values = data.readHashMap(null); 128 | boolean result = updateRows(values); 129 | reply.writeNoException(); 130 | reply.writeInt((result == true ? 1 : 0)); 131 | return true; 132 | } 133 | 134 | case DELETE_ROW_TRANSACTION: { 135 | data.enforceInterface(IBulkCursor.descriptor); 136 | int position = data.readInt(); 137 | boolean result = deleteRow(position); 138 | reply.writeNoException(); 139 | reply.writeInt((result == true ? 1 : 0)); 140 | return true; 141 | } 142 | 143 | case ON_MOVE_TRANSACTION: { 144 | data.enforceInterface(IBulkCursor.descriptor); 145 | int position = data.readInt(); 146 | onMove(position); 147 | reply.writeNoException(); 148 | return true; 149 | } 150 | 151 | case WANTS_ON_MOVE_TRANSACTION: { 152 | data.enforceInterface(IBulkCursor.descriptor); 153 | boolean result = getWantsAllOnMoveCalls(); 154 | reply.writeNoException(); 155 | reply.writeInt(result ? 1 : 0); 156 | return true; 157 | } 158 | 159 | case GET_EXTRAS_TRANSACTION: { 160 | data.enforceInterface(IBulkCursor.descriptor); 161 | Bundle extras = getExtras(); 162 | reply.writeNoException(); 163 | reply.writeBundle(extras); 164 | return true; 165 | } 166 | 167 | case RESPOND_TRANSACTION: { 168 | data.enforceInterface(IBulkCursor.descriptor); 169 | Bundle extras = data.readBundle(getClass().getClassLoader()); 170 | Bundle returnExtras = respond(extras); 171 | reply.writeNoException(); 172 | reply.writeBundle(returnExtras); 173 | return true; 174 | } 175 | } 176 | } catch (Exception e) { 177 | DatabaseUtils.writeExceptionToParcel(reply, e); 178 | return true; 179 | } 180 | 181 | return super.onTransact(code, data, reply, flags); 182 | } 183 | 184 | public IBinder asBinder() 185 | { 186 | return this; 187 | } 188 | } 189 | 190 | 191 | final class BulkCursorProxy implements IBulkCursor { 192 | private IBinder mRemote; 193 | private Bundle mExtras; 194 | 195 | public BulkCursorProxy(IBinder remote) 196 | { 197 | mRemote = remote; 198 | mExtras = null; 199 | } 200 | 201 | public IBinder asBinder() 202 | { 203 | return mRemote; 204 | } 205 | 206 | public CursorWindow getWindow(int startPos) throws RemoteException 207 | { 208 | Parcel data = Parcel.obtain(); 209 | Parcel reply = Parcel.obtain(); 210 | 211 | data.writeInterfaceToken(IBulkCursor.descriptor); 212 | 213 | data.writeInt(startPos); 214 | 215 | mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0); 216 | 217 | DatabaseUtils.readExceptionFromParcel(reply); 218 | 219 | CursorWindow window = null; 220 | if (reply.readInt() == 1) { 221 | window = CursorWindow.newFromParcel(reply); 222 | } 223 | 224 | data.recycle(); 225 | reply.recycle(); 226 | 227 | return window; 228 | } 229 | 230 | public void onMove(int position) throws RemoteException { 231 | Parcel data = Parcel.obtain(); 232 | Parcel reply = Parcel.obtain(); 233 | 234 | data.writeInterfaceToken(IBulkCursor.descriptor); 235 | 236 | data.writeInt(position); 237 | 238 | mRemote.transact(ON_MOVE_TRANSACTION, data, reply, 0); 239 | 240 | DatabaseUtils.readExceptionFromParcel(reply); 241 | 242 | data.recycle(); 243 | reply.recycle(); 244 | } 245 | 246 | public int count() throws RemoteException 247 | { 248 | Parcel data = Parcel.obtain(); 249 | Parcel reply = Parcel.obtain(); 250 | 251 | data.writeInterfaceToken(IBulkCursor.descriptor); 252 | 253 | boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0); 254 | 255 | DatabaseUtils.readExceptionFromParcel(reply); 256 | 257 | int count; 258 | if (result == false) { 259 | count = -1; 260 | } else { 261 | count = reply.readInt(); 262 | } 263 | data.recycle(); 264 | reply.recycle(); 265 | return count; 266 | } 267 | 268 | public String[] getColumnNames() throws RemoteException 269 | { 270 | Parcel data = Parcel.obtain(); 271 | Parcel reply = Parcel.obtain(); 272 | 273 | data.writeInterfaceToken(IBulkCursor.descriptor); 274 | 275 | mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0); 276 | 277 | DatabaseUtils.readExceptionFromParcel(reply); 278 | 279 | String[] columnNames = null; 280 | int numColumns = reply.readInt(); 281 | columnNames = new String[numColumns]; 282 | for (int i = 0; i < numColumns; i++) { 283 | columnNames[i] = reply.readString(); 284 | } 285 | 286 | data.recycle(); 287 | reply.recycle(); 288 | return columnNames; 289 | } 290 | 291 | public void deactivate() throws RemoteException 292 | { 293 | Parcel data = Parcel.obtain(); 294 | Parcel reply = Parcel.obtain(); 295 | 296 | data.writeInterfaceToken(IBulkCursor.descriptor); 297 | 298 | mRemote.transact(DEACTIVATE_TRANSACTION, data, reply, 0); 299 | DatabaseUtils.readExceptionFromParcel(reply); 300 | 301 | data.recycle(); 302 | reply.recycle(); 303 | } 304 | 305 | public void close() throws RemoteException 306 | { 307 | Parcel data = Parcel.obtain(); 308 | Parcel reply = Parcel.obtain(); 309 | 310 | data.writeInterfaceToken(IBulkCursor.descriptor); 311 | 312 | mRemote.transact(CLOSE_TRANSACTION, data, reply, 0); 313 | DatabaseUtils.readExceptionFromParcel(reply); 314 | 315 | data.recycle(); 316 | reply.recycle(); 317 | } 318 | 319 | public int requery(IContentObserver observer, CursorWindow window) throws RemoteException { 320 | Parcel data = Parcel.obtain(); 321 | Parcel reply = Parcel.obtain(); 322 | 323 | data.writeInterfaceToken(IBulkCursor.descriptor); 324 | 325 | data.writeStrongInterface(observer); 326 | window.writeToParcel(data, 0); 327 | 328 | boolean result = mRemote.transact(REQUERY_TRANSACTION, data, reply, 0); 329 | 330 | DatabaseUtils.readExceptionFromParcel(reply); 331 | 332 | int count; 333 | if (!result) { 334 | count = -1; 335 | } else { 336 | count = reply.readInt(); 337 | mExtras = reply.readBundle(getClass().getClassLoader()); 338 | } 339 | 340 | data.recycle(); 341 | reply.recycle(); 342 | 343 | return count; 344 | } 345 | 346 | public boolean updateRows(Map values) throws RemoteException 347 | { 348 | Parcel data = Parcel.obtain(); 349 | Parcel reply = Parcel.obtain(); 350 | 351 | data.writeInterfaceToken(IBulkCursor.descriptor); 352 | 353 | data.writeMap(values); 354 | 355 | mRemote.transact(UPDATE_ROWS_TRANSACTION, data, reply, 0); 356 | 357 | DatabaseUtils.readExceptionFromParcel(reply); 358 | 359 | boolean result = (reply.readInt() == 1 ? true : false); 360 | 361 | data.recycle(); 362 | reply.recycle(); 363 | 364 | return result; 365 | } 366 | 367 | public boolean deleteRow(int position) throws RemoteException 368 | { 369 | Parcel data = Parcel.obtain(); 370 | Parcel reply = Parcel.obtain(); 371 | 372 | data.writeInterfaceToken(IBulkCursor.descriptor); 373 | 374 | data.writeInt(position); 375 | 376 | mRemote.transact(DELETE_ROW_TRANSACTION, data, reply, 0); 377 | 378 | DatabaseUtils.readExceptionFromParcel(reply); 379 | 380 | boolean result = (reply.readInt() == 1 ? true : false); 381 | 382 | data.recycle(); 383 | reply.recycle(); 384 | 385 | return result; 386 | } 387 | 388 | public boolean getWantsAllOnMoveCalls() throws RemoteException { 389 | Parcel data = Parcel.obtain(); 390 | Parcel reply = Parcel.obtain(); 391 | 392 | data.writeInterfaceToken(IBulkCursor.descriptor); 393 | 394 | mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0); 395 | 396 | DatabaseUtils.readExceptionFromParcel(reply); 397 | 398 | int result = reply.readInt(); 399 | data.recycle(); 400 | reply.recycle(); 401 | return result != 0; 402 | } 403 | 404 | public Bundle getExtras() throws RemoteException { 405 | if (mExtras == null) { 406 | Parcel data = Parcel.obtain(); 407 | Parcel reply = Parcel.obtain(); 408 | 409 | data.writeInterfaceToken(IBulkCursor.descriptor); 410 | 411 | mRemote.transact(GET_EXTRAS_TRANSACTION, data, reply, 0); 412 | 413 | DatabaseUtils.readExceptionFromParcel(reply); 414 | 415 | mExtras = reply.readBundle(getClass().getClassLoader()); 416 | data.recycle(); 417 | reply.recycle(); 418 | } 419 | return mExtras; 420 | } 421 | 422 | public Bundle respond(Bundle extras) throws RemoteException { 423 | Parcel data = Parcel.obtain(); 424 | Parcel reply = Parcel.obtain(); 425 | 426 | data.writeInterfaceToken(IBulkCursor.descriptor); 427 | 428 | data.writeBundle(extras); 429 | 430 | mRemote.transact(RESPOND_TRANSACTION, data, reply, 0); 431 | 432 | DatabaseUtils.readExceptionFromParcel(reply); 433 | 434 | Bundle returnExtras = reply.readBundle(getClass().getClassLoader()); 435 | data.recycle(); 436 | reply.recycle(); 437 | return returnExtras; 438 | } 439 | } 440 | 441 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/BulkCursorToCursorAdaptor.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 | package net.sqlcipher; 18 | 19 | import java.util.Map; 20 | 21 | import android.database.CharArrayBuffer; 22 | import android.database.ContentObserver; 23 | import android.database.DataSetObserver; 24 | 25 | import android.os.Bundle; 26 | import android.os.RemoteException; 27 | import android.util.Log; 28 | 29 | /** 30 | * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local 31 | * process. 32 | * 33 | * {@hide} 34 | */ 35 | public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { 36 | private static final String TAG = "BulkCursor"; 37 | 38 | private SelfContentObserver mObserverBridge; 39 | private IBulkCursor mBulkCursor; 40 | private int mCount; 41 | private String[] mColumns; 42 | private boolean mWantsAllOnMoveCalls; 43 | 44 | public void set(IBulkCursor bulkCursor) { 45 | mBulkCursor = bulkCursor; 46 | 47 | try { 48 | mCount = mBulkCursor.count(); 49 | mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls(); 50 | 51 | // Search for the rowID column index and set it for our parent 52 | mColumns = mBulkCursor.getColumnNames(); 53 | mRowIdColumnIndex = findRowIdColumnIndex(mColumns); 54 | } catch (RemoteException ex) { 55 | Log.e(TAG, "Setup failed because the remote process is dead"); 56 | } 57 | } 58 | 59 | /** 60 | * Version of set() that does fewer Binder calls if the caller 61 | * already knows BulkCursorToCursorAdaptor's properties. 62 | */ 63 | public void set(IBulkCursor bulkCursor, int count, int idIndex) { 64 | mBulkCursor = bulkCursor; 65 | mColumns = null; // lazily retrieved 66 | mCount = count; 67 | mRowIdColumnIndex = idIndex; 68 | } 69 | 70 | /** 71 | * Returns column index of "_id" column, or -1 if not found. 72 | */ 73 | public static int findRowIdColumnIndex(String[] columnNames) { 74 | int length = columnNames.length; 75 | for (int i = 0; i < length; i++) { 76 | if (columnNames[i].equals("_id")) { 77 | return i; 78 | } 79 | } 80 | return -1; 81 | } 82 | 83 | /** 84 | * Gets a SelfDataChangeOberserver that can be sent to a remote 85 | * process to receive change notifications over IPC. 86 | * 87 | * @return A SelfContentObserver hooked up to this Cursor 88 | */ 89 | public synchronized IContentObserver getObserver() { 90 | if (mObserverBridge == null) { 91 | mObserverBridge = new SelfContentObserver(this); 92 | } 93 | return null;//mObserverBridge.getContentObserver(); //TODO nf fix this 94 | } 95 | 96 | @Override 97 | public int getCount() { 98 | return mCount; 99 | } 100 | 101 | @Override 102 | public boolean onMove(int oldPosition, int newPosition) { 103 | try { 104 | // Make sure we have the proper window 105 | if (mWindow != null) { 106 | if (newPosition < mWindow.getStartPosition() || 107 | newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { 108 | mWindow = mBulkCursor.getWindow(newPosition); 109 | } else if (mWantsAllOnMoveCalls) { 110 | mBulkCursor.onMove(newPosition); 111 | } 112 | } else { 113 | mWindow = mBulkCursor.getWindow(newPosition); 114 | } 115 | } catch (RemoteException ex) { 116 | // We tried to get a window and failed 117 | Log.e(TAG, "Unable to get window because the remote process is dead"); 118 | return false; 119 | } 120 | 121 | // Couldn't obtain a window, something is wrong 122 | if (mWindow == null) { 123 | return false; 124 | } 125 | 126 | return true; 127 | } 128 | 129 | @Override 130 | public void deactivate() { 131 | // This will call onInvalidated(), so make sure to do it before calling release, 132 | // which is what actually makes the data set invalid. 133 | super.deactivate(); 134 | 135 | try { 136 | mBulkCursor.deactivate(); 137 | } catch (RemoteException ex) { 138 | Log.w(TAG, "Remote process exception when deactivating"); 139 | } 140 | mWindow = null; 141 | } 142 | 143 | @Override 144 | public void close() { 145 | super.close(); 146 | try { 147 | mBulkCursor.close(); 148 | } catch (RemoteException ex) { 149 | Log.w(TAG, "Remote process exception when closing"); 150 | } 151 | mWindow = null; 152 | } 153 | 154 | @Override 155 | public boolean requery() { 156 | try { 157 | int oldCount = mCount; 158 | //TODO get the window from a pool somewhere to avoid creating the memory dealer 159 | mCount = mBulkCursor.requery(getObserver(), new CursorWindow( 160 | false /* the window will be accessed across processes */)); 161 | if (mCount != -1) { 162 | mPos = -1; 163 | mWindow = null; 164 | 165 | // super.requery() will call onChanged. Do it here instead of relying on the 166 | // observer from the far side so that observers can see a correct value for mCount 167 | // when responding to onChanged. 168 | super.requery(); 169 | return true; 170 | } else { 171 | deactivate(); 172 | return false; 173 | } 174 | } catch (Exception ex) { 175 | Log.e(TAG, "Unable to requery because the remote process exception " + ex.getMessage()); 176 | deactivate(); 177 | return false; 178 | } 179 | } 180 | 181 | /** 182 | * @hide 183 | * @deprecated 184 | */ 185 | @Override 186 | public boolean deleteRow() { 187 | try { 188 | boolean result = mBulkCursor.deleteRow(mPos); 189 | if (result != false) { 190 | // The window contains the old value, discard it 191 | mWindow = null; 192 | 193 | // Fix up the position 194 | mCount = mBulkCursor.count(); 195 | if (mPos < mCount) { 196 | int oldPos = mPos; 197 | mPos = -1; 198 | moveToPosition(oldPos); 199 | } else { 200 | mPos = mCount; 201 | } 202 | 203 | // Send the change notification 204 | onChange(true); 205 | } 206 | return result; 207 | } catch (RemoteException ex) { 208 | Log.e(TAG, "Unable to delete row because the remote process is dead"); 209 | return false; 210 | } 211 | } 212 | 213 | @Override 214 | public String[] getColumnNames() { 215 | if (mColumns == null) { 216 | try { 217 | mColumns = mBulkCursor.getColumnNames(); 218 | } catch (RemoteException ex) { 219 | Log.e(TAG, "Unable to fetch column names because the remote process is dead"); 220 | return null; 221 | } 222 | } 223 | return mColumns; 224 | } 225 | 226 | /** 227 | * @hide 228 | * @deprecated 229 | */ 230 | @Override 231 | public boolean commitUpdates(Map> additionalValues) { 233 | if (!supportsUpdates()) { 234 | Log.e(TAG, "commitUpdates not supported on this cursor, did you include the _id column?"); 235 | return false; 236 | } 237 | 238 | synchronized(mUpdatedRows) { 239 | if (additionalValues != null) { 240 | mUpdatedRows.putAll(additionalValues); 241 | } 242 | 243 | if (mUpdatedRows.size() <= 0) { 244 | return false; 245 | } 246 | 247 | try { 248 | boolean result = mBulkCursor.updateRows(mUpdatedRows); 249 | 250 | if (result == true) { 251 | mUpdatedRows.clear(); 252 | 253 | // Send the change notification 254 | onChange(true); 255 | } 256 | return result; 257 | } catch (RemoteException ex) { 258 | Log.e(TAG, "Unable to commit updates because the remote process is dead"); 259 | return false; 260 | } 261 | } 262 | } 263 | 264 | @Override 265 | public Bundle getExtras() { 266 | try { 267 | return mBulkCursor.getExtras(); 268 | } catch (RemoteException e) { 269 | // This should never happen because the system kills processes that are using remote 270 | // cursors when the provider process is killed. 271 | throw new RuntimeException(e); 272 | } 273 | } 274 | 275 | @Override 276 | public Bundle respond(Bundle extras) { 277 | try { 278 | return mBulkCursor.respond(extras); 279 | } catch (RemoteException e) { 280 | // the system kills processes that are using remote cursors when the provider process 281 | // is killed, but this can still happen if this is being called from the system process, 282 | // so, better to log and return an empty bundle. 283 | Log.w(TAG, "respond() threw RemoteException, returning an empty bundle.", e); 284 | return Bundle.EMPTY; 285 | } 286 | } 287 | 288 | @Override 289 | public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { 290 | // TODO Auto-generated method stub 291 | 292 | } 293 | 294 | @Override 295 | public void registerContentObserver(ContentObserver observer) { 296 | // TODO Auto-generated method stub 297 | 298 | } 299 | 300 | @Override 301 | public void registerDataSetObserver(DataSetObserver observer) { 302 | // TODO Auto-generated method stub 303 | 304 | } 305 | 306 | @Override 307 | public void unregisterContentObserver(ContentObserver observer) { 308 | // TODO Auto-generated method stub 309 | 310 | } 311 | 312 | @Override 313 | public void unregisterDataSetObserver(DataSetObserver observer) { 314 | // TODO Auto-generated method stub 315 | 316 | } 317 | 318 | 319 | } 320 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/CrossProcessCursorWrapper.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | import android.database.CrossProcessCursor; 4 | import android.database.CursorWindow; 5 | 6 | public class CrossProcessCursorWrapper extends CursorWrapper implements CrossProcessCursor { 7 | 8 | public CrossProcessCursorWrapper(Cursor cursor) { 9 | super(cursor); 10 | } 11 | 12 | @Override 13 | public CursorWindow getWindow() { 14 | return null; 15 | } 16 | 17 | @Override 18 | public void fillWindow(int position, CursorWindow window) { 19 | DatabaseUtils.cursorFillWindow(this, position, window); 20 | } 21 | 22 | @Override 23 | public boolean onMove(int oldPosition, int newPosition) { 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/Cursor.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 | package net.sqlcipher; 18 | 19 | /** 20 | * Extension of android.database.Cursor to support getType() for API < 11. 21 | */ 22 | public interface Cursor extends android.database.Cursor { 23 | /* 24 | * Values returned by {@link #getType(int)}. 25 | * These should be consistent with the corresponding types defined in CursorWindow.h 26 | */ 27 | /** Value returned by {@link #getType(int)} if the specified column is null */ 28 | static final int FIELD_TYPE_NULL = 0; 29 | 30 | /** Value returned by {@link #getType(int)} if the specified column type is integer */ 31 | static final int FIELD_TYPE_INTEGER = 1; 32 | 33 | /** Value returned by {@link #getType(int)} if the specified column type is float */ 34 | static final int FIELD_TYPE_FLOAT = 2; 35 | 36 | /** Value returned by {@link #getType(int)} if the specified column type is string */ 37 | static final int FIELD_TYPE_STRING = 3; 38 | 39 | /** Value returned by {@link #getType(int)} if the specified column type is blob */ 40 | static final int FIELD_TYPE_BLOB = 4; 41 | 42 | /** 43 | * Returns data type of the given column's value. 44 | * The preferred type of the column is returned but the data may be converted to other types 45 | * as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)} 46 | * etc. 47 | *

48 | * Returned column types are 49 | *

    50 | *
  • {@link #FIELD_TYPE_NULL}
  • 51 | *
  • {@link #FIELD_TYPE_INTEGER}
  • 52 | *
  • {@link #FIELD_TYPE_FLOAT}
  • 53 | *
  • {@link #FIELD_TYPE_STRING}
  • 54 | *
  • {@link #FIELD_TYPE_BLOB}
  • 55 | *
56 | *

57 | * 58 | * @param columnIndex the zero-based index of the target column. 59 | * @return column value type 60 | */ 61 | int getType(int columnIndex); 62 | } 63 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/CursorIndexOutOfBoundsException.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 | package net.sqlcipher; 18 | 19 | /** 20 | * An exception indicating that a cursor is out of bounds. 21 | */ 22 | public class CursorIndexOutOfBoundsException extends IndexOutOfBoundsException { 23 | 24 | public CursorIndexOutOfBoundsException(int index, int size) { 25 | super("Index " + index + " requested, with a size of " + size); 26 | } 27 | 28 | public CursorIndexOutOfBoundsException(String message) { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWindowAllocation.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | public interface CursorWindowAllocation { 4 | long getInitialAllocationSize(); 5 | long getGrowthPaddingSize(); 6 | long getMaxAllocationSize(); 7 | } 8 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/CursorWrapper.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 | package net.sqlcipher; 18 | 19 | /** 20 | * Extension of android.database.CursorWrapper to support getType() for API < 11. 21 | */ 22 | public class CursorWrapper extends android.database.CursorWrapper implements Cursor { 23 | 24 | private final Cursor mCursor; 25 | 26 | public CursorWrapper(Cursor cursor) { 27 | super(cursor); 28 | mCursor = cursor; 29 | } 30 | 31 | public int getType(int columnIndex) { 32 | return mCursor.getType(columnIndex); 33 | } 34 | 35 | public Cursor getWrappedCursor() { 36 | return mCursor; 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/CustomCursorWindowAllocation.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | import net.sqlcipher.CursorWindowAllocation; 4 | 5 | public class CustomCursorWindowAllocation implements CursorWindowAllocation { 6 | 7 | private long initialAllocationSize = 0L; 8 | private long growthPaddingSize = 0L; 9 | private long maxAllocationSize = 0L; 10 | 11 | public CustomCursorWindowAllocation(long initialSize, 12 | long growthPaddingSize, 13 | long maxAllocationSize){ 14 | this.initialAllocationSize = initialSize; 15 | this.growthPaddingSize = growthPaddingSize; 16 | this.maxAllocationSize = maxAllocationSize; 17 | } 18 | 19 | public long getInitialAllocationSize() { 20 | return initialAllocationSize; 21 | } 22 | 23 | public long getGrowthPaddingSize() { 24 | return growthPaddingSize; 25 | } 26 | 27 | public long getMaxAllocationSize() { 28 | return maxAllocationSize; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/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 | package net.sqlcipher; 18 | 19 | import net.sqlcipher.database.SQLiteDatabase; 20 | 21 | /** 22 | * An interface to let the apps define the actions to take when the following errors are detected 23 | * database corruption 24 | */ 25 | public interface DatabaseErrorHandler { 26 | 27 | /** 28 | * defines the method to be invoked when database corruption is detected. 29 | * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption 30 | * is detected. 31 | */ 32 | void onCorruption(SQLiteDatabase dbObj); 33 | } 34 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/DefaultCursorWindowAllocation.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | import net.sqlcipher.CursorWindowAllocation; 4 | 5 | public class DefaultCursorWindowAllocation implements CursorWindowAllocation { 6 | 7 | private long initialAllocationSize = 1024 * 1024; 8 | private long WindowAllocationUnbounded = 0; 9 | 10 | public long getInitialAllocationSize() { 11 | return initialAllocationSize; 12 | } 13 | 14 | public long getGrowthPaddingSize() { 15 | return initialAllocationSize; 16 | } 17 | 18 | public long getMaxAllocationSize() { 19 | return WindowAllocationUnbounded; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/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 | package net.sqlcipher; 18 | 19 | import java.io.File; 20 | import java.util.List; 21 | 22 | import net.sqlcipher.database.SQLiteDatabase; 23 | 24 | import android.database.sqlite.SQLiteException; 25 | import android.util.Log; 26 | import android.util.Pair; 27 | 28 | /** 29 | * Default class used to define the actions to take when the database corruption is reported 30 | * by sqlite. 31 | *

32 | * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used 33 | * as the default {@link DatabaseErrorHandler}. 34 | */ 35 | public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler { 36 | 37 | private final String TAG = getClass().getSimpleName(); 38 | 39 | /** 40 | * defines the default method to be invoked when database corruption is detected. 41 | * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption 42 | * is detected. 43 | */ 44 | public void onCorruption(SQLiteDatabase dbObj) { 45 | // NOTE: Unlike the AOSP, this version does NOT attempt to delete any attached databases. 46 | // TBD: Are we really certain that the attached databases would really be corrupt? 47 | Log.e(TAG, "Corruption reported by sqlite on database, deleting: " + dbObj.getPath()); 48 | 49 | if (dbObj.isOpen()) { 50 | Log.e(TAG, "Database object for corrupted database is already open, closing"); 51 | 52 | try { 53 | dbObj.close(); 54 | } catch (Exception e) { 55 | /* ignored */ 56 | Log.e(TAG, "Exception closing Database object for corrupted database, ignored", e); 57 | } 58 | } 59 | 60 | deleteDatabaseFile(dbObj.getPath()); 61 | } 62 | 63 | private void deleteDatabaseFile(String fileName) { 64 | if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) { 65 | return; 66 | } 67 | Log.e(TAG, "deleting the database file: " + fileName); 68 | try { 69 | new File(fileName).delete(); 70 | } catch (Exception e) { 71 | /* print warning and ignore exception */ 72 | Log.w(TAG, "delete failed: " + e.getMessage()); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/IBulkCursor.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 | package net.sqlcipher; 18 | 19 | import android.os.RemoteException; 20 | import android.os.IBinder; 21 | import android.os.IInterface; 22 | import android.os.Bundle; 23 | 24 | import java.util.Map; 25 | 26 | /** 27 | * This interface provides a low-level way to pass bulk cursor data across 28 | * both process and language boundries. Application code should use the Cursor 29 | * interface directly. 30 | * 31 | * {@hide} 32 | */ 33 | public interface IBulkCursor extends IInterface { 34 | /** 35 | * Returns a BulkCursorWindow, which either has a reference to a shared 36 | * memory segment with the rows, or an array of JSON strings. 37 | */ 38 | public CursorWindow getWindow(int startPos) throws RemoteException; 39 | 40 | public void onMove(int position) throws RemoteException; 41 | 42 | /** 43 | * Returns the number of rows in the cursor. 44 | * 45 | * @return the number of rows in the cursor. 46 | */ 47 | public int count() throws RemoteException; 48 | 49 | /** 50 | * Returns a string array holding the names of all of the columns in the 51 | * cursor in the order in which they were listed in the result. 52 | * 53 | * @return the names of the columns returned in this query. 54 | */ 55 | public String[] getColumnNames() throws RemoteException; 56 | 57 | public boolean updateRows(Map> values) throws RemoteException; 58 | 59 | public boolean deleteRow(int position) throws RemoteException; 60 | 61 | public void deactivate() throws RemoteException; 62 | 63 | public void close() throws RemoteException; 64 | 65 | public int requery(IContentObserver observer, CursorWindow window) throws RemoteException; 66 | 67 | boolean getWantsAllOnMoveCalls() throws RemoteException; 68 | 69 | Bundle getExtras() throws RemoteException; 70 | 71 | Bundle respond(Bundle extras) throws RemoteException; 72 | 73 | /* IPC constants */ 74 | static final String descriptor = "android.content.IBulkCursor"; 75 | 76 | static final int GET_CURSOR_WINDOW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; 77 | static final int COUNT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1; 78 | static final int GET_COLUMN_NAMES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2; 79 | static final int UPDATE_ROWS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3; 80 | static final int DELETE_ROW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4; 81 | static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5; 82 | static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6; 83 | static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 7; 84 | static final int WANTS_ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 8; 85 | static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9; 86 | static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10; 87 | static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11; 88 | } 89 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/InvalidRowColumnException.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | /** 4 | * An exception that indicates there was an error accessing a specific row/column. 5 | */ 6 | public class InvalidRowColumnException extends RuntimeException 7 | { 8 | public InvalidRowColumnException() {} 9 | 10 | public InvalidRowColumnException(String error) 11 | { 12 | super(error); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/MatrixCursor.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 | package net.sqlcipher; 18 | 19 | import java.util.ArrayList; 20 | 21 | import android.database.CharArrayBuffer; 22 | 23 | 24 | /** 25 | * A mutable cursor implementation backed by an array of {@code Object}s. Use 26 | * {@link #newRow()} to add rows. Automatically expands internal capacity 27 | * as needed. 28 | */ 29 | public class MatrixCursor extends AbstractCursor { 30 | 31 | private final String[] columnNames; 32 | private Object[] data; 33 | private int rowCount = 0; 34 | private final int columnCount; 35 | 36 | /** 37 | * Constructs a new cursor with the given initial capacity. 38 | * 39 | * @param columnNames names of the columns, the ordering of which 40 | * determines column ordering elsewhere in this cursor 41 | * @param initialCapacity in rows 42 | */ 43 | public MatrixCursor(String[] columnNames, int initialCapacity) { 44 | this.columnNames = columnNames; 45 | this.columnCount = columnNames.length; 46 | 47 | if (initialCapacity < 1) { 48 | initialCapacity = 1; 49 | } 50 | 51 | this.data = new Object[columnCount * initialCapacity]; 52 | } 53 | 54 | /** 55 | * Constructs a new cursor. 56 | * 57 | * @param columnNames names of the columns, the ordering of which 58 | * determines column ordering elsewhere in this cursor 59 | */ 60 | public MatrixCursor(String[] columnNames) { 61 | this(columnNames, 16); 62 | } 63 | 64 | /** 65 | * Gets value at the given column for the current row. 66 | */ 67 | private Object get(int column) { 68 | if (column < 0 || column >= columnCount) { 69 | throw new CursorIndexOutOfBoundsException("Requested column: " 70 | + column + ", # of columns: " + columnCount); 71 | } 72 | if (mPos < 0) { 73 | throw new CursorIndexOutOfBoundsException("Before first row."); 74 | } 75 | if (mPos >= rowCount) { 76 | throw new CursorIndexOutOfBoundsException("After last row."); 77 | } 78 | return data[mPos * columnCount + column]; 79 | } 80 | 81 | /** 82 | * Adds a new row to the end and returns a builder for that row. Not safe 83 | * for concurrent use. 84 | * 85 | * @return builder which can be used to set the column values for the new 86 | * row 87 | */ 88 | public RowBuilder newRow() { 89 | rowCount++; 90 | int endIndex = rowCount * columnCount; 91 | ensureCapacity(endIndex); 92 | int start = endIndex - columnCount; 93 | return new RowBuilder(start, endIndex); 94 | } 95 | 96 | /** 97 | * Adds a new row to the end with the given column values. Not safe 98 | * for concurrent use. 99 | * 100 | * @throws IllegalArgumentException if {@code columnValues.length != 101 | * columnNames.length} 102 | * @param columnValues in the same order as the the column names specified 103 | * at cursor construction time 104 | */ 105 | public void addRow(Object[] columnValues) { 106 | if (columnValues.length != columnCount) { 107 | throw new IllegalArgumentException("columnNames.length = " 108 | + columnCount + ", columnValues.length = " 109 | + columnValues.length); 110 | } 111 | 112 | int start = rowCount++ * columnCount; 113 | ensureCapacity(start + columnCount); 114 | System.arraycopy(columnValues, 0, data, start, columnCount); 115 | } 116 | 117 | /** 118 | * Adds a new row to the end with the given column values. Not safe 119 | * for concurrent use. 120 | * 121 | * @throws IllegalArgumentException if {@code columnValues.size() != 122 | * columnNames.length} 123 | * @param columnValues in the same order as the the column names specified 124 | * at cursor construction time 125 | */ 126 | public void addRow(Iterable columnValues) { 127 | int start = rowCount * columnCount; 128 | int end = start + columnCount; 129 | ensureCapacity(end); 130 | 131 | if (columnValues instanceof ArrayList) { 132 | addRow((ArrayList) columnValues, start); 133 | return; 134 | } 135 | 136 | int current = start; 137 | Object[] localData = data; 138 | for (Object columnValue : columnValues) { 139 | if (current == end) { 140 | // TODO: null out row? 141 | throw new IllegalArgumentException( 142 | "columnValues.size() > columnNames.length"); 143 | } 144 | localData[current++] = columnValue; 145 | } 146 | 147 | if (current != end) { 148 | // TODO: null out row? 149 | throw new IllegalArgumentException( 150 | "columnValues.size() < columnNames.length"); 151 | } 152 | 153 | // Increase row count here in case we encounter an exception. 154 | rowCount++; 155 | } 156 | 157 | /** Optimization for {@link ArrayList}. */ 158 | private void addRow(ArrayList columnValues, int start) { 159 | int size = columnValues.size(); 160 | if (size != columnCount) { 161 | throw new IllegalArgumentException("columnNames.length = " 162 | + columnCount + ", columnValues.size() = " + size); 163 | } 164 | 165 | rowCount++; 166 | Object[] localData = data; 167 | for (int i = 0; i < size; i++) { 168 | localData[start + i] = columnValues.get(i); 169 | } 170 | } 171 | 172 | /** Ensures that this cursor has enough capacity. */ 173 | private void ensureCapacity(int size) { 174 | if (size > data.length) { 175 | Object[] oldData = this.data; 176 | int newSize = data.length * 2; 177 | if (newSize < size) { 178 | newSize = size; 179 | } 180 | this.data = new Object[newSize]; 181 | System.arraycopy(oldData, 0, this.data, 0, oldData.length); 182 | } 183 | } 184 | 185 | /** 186 | * Builds a row, starting from the left-most column and adding one column 187 | * value at a time. Follows the same ordering as the column names specified 188 | * at cursor construction time. 189 | */ 190 | public class RowBuilder { 191 | 192 | private int index; 193 | private final int endIndex; 194 | 195 | RowBuilder(int index, int endIndex) { 196 | this.index = index; 197 | this.endIndex = endIndex; 198 | } 199 | 200 | /** 201 | * Sets the next column value in this row. 202 | * 203 | * @throws CursorIndexOutOfBoundsException if you try to add too many 204 | * values 205 | * @return this builder to support chaining 206 | */ 207 | public RowBuilder add(Object columnValue) { 208 | if (index == endIndex) { 209 | throw new CursorIndexOutOfBoundsException( 210 | "No more columns left."); 211 | } 212 | 213 | data[index++] = columnValue; 214 | return this; 215 | } 216 | } 217 | 218 | // AbstractCursor implementation. 219 | 220 | @Override 221 | public int getCount() { 222 | return rowCount; 223 | } 224 | 225 | @Override 226 | public String[] getColumnNames() { 227 | return columnNames; 228 | } 229 | 230 | @Override 231 | public String getString(int column) { 232 | Object value = get(column); 233 | if (value == null) return null; 234 | return value.toString(); 235 | } 236 | 237 | @Override 238 | public short getShort(int column) { 239 | Object value = get(column); 240 | if (value == null) return 0; 241 | if (value instanceof Number) return ((Number) value).shortValue(); 242 | return Short.parseShort(value.toString()); 243 | } 244 | 245 | @Override 246 | public int getInt(int column) { 247 | Object value = get(column); 248 | if (value == null) return 0; 249 | if (value instanceof Number) return ((Number) value).intValue(); 250 | return Integer.parseInt(value.toString()); 251 | } 252 | 253 | @Override 254 | public long getLong(int column) { 255 | Object value = get(column); 256 | if (value == null) return 0; 257 | if (value instanceof Number) return ((Number) value).longValue(); 258 | return Long.parseLong(value.toString()); 259 | } 260 | 261 | @Override 262 | public float getFloat(int column) { 263 | Object value = get(column); 264 | if (value == null) return 0.0f; 265 | if (value instanceof Number) return ((Number) value).floatValue(); 266 | return Float.parseFloat(value.toString()); 267 | } 268 | 269 | @Override 270 | public double getDouble(int column) { 271 | Object value = get(column); 272 | if (value == null) return 0.0d; 273 | if (value instanceof Number) return ((Number) value).doubleValue(); 274 | return Double.parseDouble(value.toString()); 275 | } 276 | 277 | @Override 278 | public int getType(int column) { 279 | return DatabaseUtils.getTypeOfObject(get(column)); 280 | } 281 | 282 | @Override 283 | public boolean isNull(int column) { 284 | return get(column) == null; 285 | } 286 | 287 | 288 | } 289 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/RowAllocationException.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | /** 4 | * An exception that indicates there was an error attempting to allocate a row 5 | * for the CursorWindow. 6 | */ 7 | public class RowAllocationException extends RuntimeException 8 | { 9 | public RowAllocationException() {} 10 | 11 | public RowAllocationException(String error) 12 | { 13 | super(error); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/StaleDataException.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 | package net.sqlcipher; 18 | 19 | /** 20 | * This exception is thrown when a Cursor contains stale data and must be 21 | * requeried before being used again. 22 | */ 23 | public class StaleDataException extends java.lang.RuntimeException 24 | { 25 | public StaleDataException() 26 | { 27 | super(); 28 | } 29 | 30 | public StaleDataException(String description) 31 | { 32 | super(description); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/UnknownTypeException.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher; 2 | 3 | /** 4 | * An exception that indicates an unknown type was returned. 5 | */ 6 | public class UnknownTypeException extends RuntimeException 7 | { 8 | public UnknownTypeException() {} 9 | 10 | public UnknownTypeException(String error) 11 | { 12 | super(error); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | 19 | /** 20 | * An exception that indicates that garbage-collector is finalizing a database object 21 | * that is not explicitly closed 22 | * @hide 23 | */ 24 | public class DatabaseObjectNotClosedException extends RuntimeException 25 | { 26 | private static final String s = "Application did not close the cursor or database object " + 27 | "that was opened here"; 28 | 29 | public DatabaseObjectNotClosedException() 30 | { 31 | super(s); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | 19 | import net.sqlcipher.*; 20 | 21 | /** 22 | * An object created from a SQLiteDatabase that can be closed. 23 | */ 24 | public abstract class SQLiteClosable { 25 | private int mReferenceCount = 1; 26 | private Object mLock = new Object(); 27 | 28 | protected abstract void onAllReferencesReleased(); 29 | protected void onAllReferencesReleasedFromContainer() {} 30 | 31 | public void acquireReference() { 32 | synchronized(mLock) { 33 | if (mReferenceCount <= 0) { 34 | throw new IllegalStateException( 35 | "attempt to re-open an already-closed object: " + getObjInfo()); 36 | } 37 | mReferenceCount++; 38 | } 39 | } 40 | 41 | public void releaseReference() { 42 | synchronized(mLock) { 43 | mReferenceCount--; 44 | if (mReferenceCount == 0) { 45 | onAllReferencesReleased(); 46 | } 47 | } 48 | } 49 | 50 | public void releaseReferenceFromContainer() { 51 | synchronized(mLock) { 52 | mReferenceCount--; 53 | if (mReferenceCount == 0) { 54 | onAllReferencesReleasedFromContainer(); 55 | } 56 | } 57 | } 58 | 59 | private String getObjInfo() { 60 | StringBuilder buff = new StringBuilder(); 61 | buff.append(this.getClass().getName()); 62 | buff.append(" ("); 63 | if (this instanceof SQLiteDatabase) { 64 | buff.append("database = "); 65 | buff.append(((SQLiteDatabase)this).getPath()); 66 | } else if (this instanceof SQLiteProgram || this instanceof SQLiteStatement || 67 | this instanceof SQLiteQuery) { 68 | buff.append("mSql = "); 69 | buff.append(((SQLiteProgram)this).mSql); 70 | } 71 | /*else if (this instanceof CursorWindow) { 72 | buff.append("mStartPos = "); 73 | buff.append(((CursorWindow)this).getStartPosition()); 74 | }*/ 75 | buff.append(") "); 76 | return buff.toString(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteCompiledSql.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.sqlcipher.database; 18 | 19 | import android.util.Log; 20 | 21 | /** 22 | * This class encapsulates compilation of sql statement and release of the compiled statement obj. 23 | * Once a sql statement is compiled, it is cached in {@link SQLiteDatabase} 24 | * and it is released in one of the 2 following ways 25 | * 1. when {@link SQLiteDatabase} object is closed. 26 | * 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()} 27 | * releaases this obj. 28 | */ 29 | /* package */ class SQLiteCompiledSql { 30 | 31 | private static final String TAG = "SQLiteCompiledSql"; 32 | 33 | /** The database this program is compiled against. */ 34 | /* package */ SQLiteDatabase mDatabase; 35 | 36 | /** 37 | * Native linkage, do not modify. This comes from the database. 38 | */ 39 | /* package */ long nHandle = 0; 40 | 41 | /** 42 | * Native linkage, do not modify. When non-0 this holds a reference to a valid 43 | * sqlite3_statement object. It is only updated by the native code, but may be 44 | * checked in this class when the database lock is held to determine if there 45 | * is a valid native-side program or not. 46 | */ 47 | /* package */ long nStatement = 0; 48 | 49 | /** the following are for debugging purposes */ 50 | private String mSqlStmt = null; 51 | 52 | /** when in cache and is in use, this member is set */ 53 | private boolean mInUse = false; 54 | 55 | /* package */ SQLiteCompiledSql(SQLiteDatabase db, String sql) { 56 | if (!db.isOpen()) { 57 | throw new IllegalStateException("database " + db.getPath() + " already closed"); 58 | } 59 | mDatabase = db; 60 | mSqlStmt = sql; 61 | this.nHandle = db.mNativeHandle; 62 | compile(sql, true); 63 | } 64 | 65 | /** 66 | * Compiles the given SQL into a SQLite byte code program using sqlite3_prepare_v2(). If 67 | * this method has been called previously without a call to close and forCompilation is set 68 | * to false the previous compilation will be used. Setting forceCompilation to true will 69 | * always re-compile the program and should be done if you pass differing SQL strings to this 70 | * method. 71 | * 72 | *

Note: this method acquires the database lock.

73 | * 74 | * @param sql the SQL string to compile 75 | * @param forceCompilation forces the SQL to be recompiled in the event that there is an 76 | * existing compiled SQL program already around 77 | */ 78 | private void compile(String sql, boolean forceCompilation) { 79 | if (!mDatabase.isOpen()) { 80 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 81 | } 82 | // Only compile if we don't have a valid statement already or the caller has 83 | // explicitly requested a recompile. 84 | if (forceCompilation) { 85 | mDatabase.lock(); 86 | try { 87 | // Note that the native_compile() takes care of destroying any previously 88 | // existing programs before it compiles. 89 | native_compile(sql); 90 | } finally { 91 | mDatabase.unlock(); 92 | } 93 | } 94 | } 95 | 96 | /* package */ synchronized void releaseSqlStatement() { 97 | // Note that native_finalize() checks to make sure that nStatement is 98 | // non-null before destroying it. 99 | if (nStatement != 0) { 100 | if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 101 | Log.v(TAG, "closed and deallocated DbObj (id#" + nStatement +")"); 102 | } 103 | native_finalize(); 104 | nStatement = 0; 105 | } 106 | } 107 | 108 | /** 109 | * returns true if acquire() succeeds. false otherwise. 110 | */ 111 | /* package */ synchronized boolean acquire() { 112 | if (mInUse) { 113 | // someone already has acquired it. 114 | return false; 115 | } 116 | mInUse = true; 117 | if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 118 | Log.v(TAG, "Acquired DbObj (id#" + nStatement + ") from DB cache"); 119 | } 120 | return true; 121 | } 122 | 123 | /* package */ synchronized void release() { 124 | if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 125 | Log.v(TAG, "Released DbObj (id#" + nStatement + ") back to DB cache"); 126 | } 127 | mInUse = false; 128 | } 129 | 130 | /** 131 | * Make sure that the native resource is cleaned up. 132 | */ 133 | @Override 134 | protected void finalize() throws Throwable { 135 | try { 136 | if (nStatement == 0) return; 137 | // finalizer should NEVER get called 138 | if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 139 | Log.v(TAG, "** warning ** Finalized DbObj (id#" + nStatement + ")"); 140 | } 141 | releaseSqlStatement(); 142 | } finally { 143 | super.finalize(); 144 | } 145 | } 146 | 147 | /** 148 | * Compiles SQL into a SQLite program. 149 | * 150 | *

The database lock must be held when calling this method. 151 | * @param sql The SQL to compile. 152 | */ 153 | private final native void native_compile(String sql); 154 | private final native void native_finalize(); 155 | } 156 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteContentHelper.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.sqlcipher.database; 18 | import net.sqlcipher.*; 19 | 20 | import android.content.res.AssetFileDescriptor; 21 | import android.os.MemoryFile; 22 | 23 | import android.database.Cursor; 24 | 25 | import java.io.FileNotFoundException; 26 | import java.io.IOException; 27 | 28 | /** 29 | * Some helper functions for using SQLite database to implement content providers. 30 | * 31 | * @hide 32 | */ 33 | public class SQLiteContentHelper { 34 | 35 | /** 36 | * Runs an SQLite query and returns an AssetFileDescriptor for the 37 | * blob in column 0 of the first row. If the first column does 38 | * not contain a blob, an unspecified exception is thrown. 39 | * 40 | * @param db Handle to a readable database. 41 | * @param sql SQL query, possibly with query arguments. 42 | * @param selectionArgs Query argument values, or {@code null} for no argument. 43 | * @return If no exception is thrown, a non-null AssetFileDescriptor is returned. 44 | * @throws FileNotFoundException If the query returns no results or the 45 | * value of column 0 is NULL, or if there is an error creating the 46 | * asset file descriptor. 47 | */ 48 | public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql, 49 | String[] selectionArgs) throws FileNotFoundException { 50 | android.os.ParcelFileDescriptor fd = null; 51 | 52 | try { 53 | MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs); 54 | if (file == null) { 55 | throw new FileNotFoundException("No results."); 56 | } 57 | Class c = file.getClass(); 58 | try { 59 | java.lang.reflect.Method m = c.getDeclaredMethod("getParcelFileDescriptor"); 60 | m.setAccessible(true); 61 | fd = (android.os.ParcelFileDescriptor)m.invoke(file); 62 | } catch (Exception e) { 63 | android.util.Log.i("SQLiteContentHelper", "SQLiteCursor.java: " + e); 64 | } 65 | AssetFileDescriptor afd = new AssetFileDescriptor(fd, 0, file.length()); 66 | return afd; 67 | } catch (IOException ex) { 68 | throw new FileNotFoundException(ex.toString()); 69 | } 70 | } 71 | 72 | /** 73 | * Runs an SQLite query and returns a MemoryFile for the 74 | * blob in column 0 of the first row. If the first column does 75 | * not contain a blob, an unspecified exception is thrown. 76 | * 77 | * @return A memory file, or {@code null} if the query returns no results 78 | * or the value column 0 is NULL. 79 | * @throws IOException If there is an error creating the memory file. 80 | */ 81 | // TODO: make this native and use the SQLite blob API to reduce copying 82 | private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql, 83 | String[] selectionArgs) throws IOException { 84 | Cursor cursor = db.rawQuery(sql, selectionArgs); 85 | if (cursor == null) { 86 | return null; 87 | } 88 | try { 89 | if (!cursor.moveToFirst()) { 90 | return null; 91 | } 92 | byte[] bytes = cursor.getBlob(0); 93 | if (bytes == null) { 94 | return null; 95 | } 96 | MemoryFile file = new MemoryFile(null, bytes.length); 97 | file.writeBytes(bytes, 0, 0, bytes.length); 98 | 99 | // file.deactivate(); 100 | return file; 101 | } finally { 102 | cursor.close(); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | 19 | import net.sqlcipher.database.SQLiteDatabase.CursorFactory; 20 | import net.sqlcipher.*; 21 | 22 | /** 23 | * A driver for SQLiteCursors that is used to create them and gets notified 24 | * by the cursors it creates on significant events in their lifetimes. 25 | */ 26 | public interface SQLiteCursorDriver { 27 | /** 28 | * Executes the query returning a Cursor over the result set. 29 | * 30 | * @param factory The CursorFactory to use when creating the Cursors, or 31 | * null if standard SQLiteCursors should be returned. 32 | * @return a Cursor over the result set 33 | */ 34 | Cursor query(CursorFactory factory, String[] bindArgs); 35 | 36 | /** 37 | * Called by a SQLiteCursor when it is released. 38 | */ 39 | void cursorDeactivated(); 40 | 41 | /** 42 | * Called by a SQLiteCursor when it is requeryed. 43 | * 44 | * @return The new count value. 45 | */ 46 | void cursorRequeried(android.database.Cursor cursor); 47 | 48 | /** 49 | * Called by a SQLiteCursor when it it closed to destroy this object as well. 50 | */ 51 | void cursorClosed(); 52 | 53 | /** 54 | * Set new bind arguments. These will take effect in cursorRequeried(). 55 | * @param bindArgs the new arguments 56 | */ 57 | public void setBindArguments(String[] bindArgs); 58 | } 59 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteDatabaseHook.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher.database; 2 | 3 | /** 4 | * An interface to perform pre and post key operations against a database. 5 | */ 6 | public interface SQLiteDatabaseHook { 7 | /** 8 | * Called immediately before opening the database. 9 | */ 10 | void preKey(SQLiteDatabase database); 11 | /** 12 | * Called immediately after opening the database. 13 | */ 14 | void postKey(SQLiteDatabase database); 15 | } 16 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | 19 | import java.util.ArrayList; 20 | 21 | import android.util.Log; 22 | 23 | /** 24 | * Provides debugging info about all SQLite databases running in the current process. 25 | * 26 | * {@hide} 27 | */ 28 | public final class SQLiteDebug { 29 | /** 30 | * Controls the printing of SQL statements as they are executed. 31 | */ 32 | public static final boolean DEBUG_SQL_STATEMENTS = 33 | Log.isLoggable("SQLiteStatements", Log.VERBOSE); 34 | 35 | /** 36 | * Controls the printing of wall-clock time taken to execute SQL statements 37 | * as they are executed. 38 | */ 39 | public static final boolean DEBUG_SQL_TIME = 40 | Log.isLoggable("SQLiteTime", Log.VERBOSE); 41 | 42 | /** 43 | * Controls the printing of compiled-sql-statement cache stats. 44 | */ 45 | public static final boolean DEBUG_SQL_CACHE = 46 | Log.isLoggable("SQLiteCompiledSql", Log.VERBOSE); 47 | 48 | /** 49 | * Controls the stack trace reporting of active cursors being 50 | * finalized. 51 | */ 52 | public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION = 53 | Log.isLoggable("SQLiteCursorClosing", Log.VERBOSE); 54 | 55 | /** 56 | * Controls the tracking of time spent holding the database lock. 57 | */ 58 | public static final boolean DEBUG_LOCK_TIME_TRACKING = 59 | Log.isLoggable("SQLiteLockTime", Log.VERBOSE); 60 | 61 | /** 62 | * Controls the printing of stack traces when tracking the time spent holding the database lock. 63 | */ 64 | public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE = 65 | Log.isLoggable("SQLiteLockStackTrace", Log.VERBOSE); 66 | 67 | /** 68 | * Contains statistics about the active pagers in the current process. 69 | * 70 | * @see #getPagerStats(PagerStats) 71 | */ 72 | public static class PagerStats { 73 | /** The total number of bytes in all pagers in the current process 74 | * @deprecated not used any longer 75 | */ 76 | @Deprecated 77 | public long totalBytes; 78 | /** The number of bytes in referenced pages in all pagers in the current process 79 | * @deprecated not used any longer 80 | * */ 81 | @Deprecated 82 | public long referencedBytes; 83 | /** The number of bytes in all database files opened in the current process 84 | * @deprecated not used any longer 85 | */ 86 | @Deprecated 87 | public long databaseBytes; 88 | /** The number of pagers opened in the current process 89 | * @deprecated not used any longer 90 | */ 91 | @Deprecated 92 | public int numPagers; 93 | 94 | /** the current amount of memory checked out by sqlite using sqlite3_malloc(). 95 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 96 | */ 97 | public int memoryUsed; 98 | 99 | /** the number of bytes of page cache allocation which could not be sattisfied by the 100 | * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc(). 101 | * The returned value includes allocations that overflowed because they where too large 102 | * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations 103 | * that overflowed because no space was left in the page cache. 104 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 105 | */ 106 | public int pageCacheOverflo; 107 | 108 | /** records the largest memory allocation request handed to sqlite3. 109 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 110 | */ 111 | public int largestMemAlloc; 112 | 113 | /** a list of {@link DbStats} - one for each main database opened by the applications 114 | * running on the android device 115 | */ 116 | public ArrayList dbStats; 117 | } 118 | 119 | /** 120 | * contains statistics about a database 121 | */ 122 | public static class DbStats { 123 | /** name of the database */ 124 | public String dbName; 125 | 126 | /** the page size for the database */ 127 | public long pageSize; 128 | 129 | /** the database size */ 130 | public long dbSize; 131 | 132 | /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */ 133 | public int lookaside; 134 | 135 | public DbStats(String dbName, long pageCount, long pageSize, int lookaside) { 136 | this.dbName = dbName; 137 | this.pageSize = pageSize; 138 | dbSize = (pageCount * pageSize) / 1024; 139 | this.lookaside = lookaside; 140 | } 141 | } 142 | 143 | /** 144 | * return all pager and database stats for the current process. 145 | * @return {@link PagerStats} 146 | */ 147 | public static PagerStats getDatabaseInfo() { 148 | PagerStats stats = new PagerStats(); 149 | getPagerStats(stats); 150 | stats.dbStats = SQLiteDatabase.getDbStats(); 151 | return stats; 152 | } 153 | 154 | /** 155 | * Gathers statistics about all pagers in the current process. 156 | */ 157 | public static native void getPagerStats(PagerStats stats); 158 | 159 | /** 160 | * Returns the size of the SQLite heap. 161 | * @return The size of the SQLite heap in bytes. 162 | */ 163 | public static native long getHeapSize(); 164 | 165 | /** 166 | * Returns the amount of allocated memory in the SQLite heap. 167 | * @return The allocated size in bytes. 168 | */ 169 | public static native long getHeapAllocatedSize(); 170 | 171 | /** 172 | * Returns the amount of free memory in the SQLite heap. 173 | * @return The freed size in bytes. 174 | */ 175 | public static native long getHeapFreeSize(); 176 | 177 | /** 178 | * Determines the number of dirty belonging to the SQLite 179 | * heap segments of this process. pages[0] returns the number of 180 | * shared pages, pages[1] returns the number of private pages 181 | */ 182 | public static native void getHeapDirtyPages(int[] pages); 183 | 184 | private static int sNumActiveCursorsFinalized = 0; 185 | 186 | /** 187 | * Returns the number of active cursors that have been finalized. This depends on the GC having 188 | * run but is still useful for tests. 189 | */ 190 | public static int getNumActiveCursorsFinalized() { 191 | return sNumActiveCursorsFinalized; 192 | } 193 | 194 | static synchronized void notifyActiveCursorFinalized() { 195 | sNumActiveCursorsFinalized++; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | 19 | import net.sqlcipher.Cursor; 20 | import net.sqlcipher.database.SQLiteDatabase.CursorFactory; 21 | 22 | /** 23 | * A cursor driver that uses the given query directly. 24 | * 25 | * @hide 26 | */ 27 | public class SQLiteDirectCursorDriver implements SQLiteCursorDriver { 28 | private String mEditTable; 29 | private SQLiteDatabase mDatabase; 30 | private Cursor mCursor; 31 | private String mSql; 32 | private SQLiteQuery mQuery; 33 | 34 | public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable) { 35 | mDatabase = db; 36 | mEditTable = editTable; 37 | mSql = sql; 38 | } 39 | 40 | public Cursor query(CursorFactory factory, Object[] args) { 41 | SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, args); 42 | try { 43 | query.bindArguments(args); 44 | if (factory == null) { 45 | mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query); 46 | } else { 47 | mCursor = factory.newCursor(mDatabase, this, mEditTable, query); 48 | } 49 | mQuery = query; 50 | query = null; 51 | return mCursor; 52 | } finally { 53 | // Make sure this object is cleaned up if something happens 54 | if (query != null) query.close(); 55 | } 56 | } 57 | 58 | public Cursor query(CursorFactory factory, String[] selectionArgs) { 59 | // Compile the query 60 | SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs); 61 | 62 | try { 63 | // Arg binding 64 | int numArgs = selectionArgs == null ? 0 : selectionArgs.length; 65 | for (int i = 0; i < numArgs; i++) { 66 | query.bindString(i + 1, selectionArgs[i]); 67 | } 68 | 69 | // Create the cursor 70 | if (factory == null) { 71 | mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query); 72 | 73 | } else { 74 | mCursor = factory.newCursor(mDatabase, this, mEditTable, query); 75 | } 76 | 77 | mQuery = query; 78 | query = null; 79 | return mCursor; 80 | } finally { 81 | // Make sure this object is cleaned up if something happens 82 | if (query != null) query.close(); 83 | } 84 | } 85 | 86 | public void cursorClosed() { 87 | mCursor = null; 88 | } 89 | 90 | public void setBindArguments(String[] bindArgs) { 91 | final int numArgs = bindArgs.length; 92 | for (int i = 0; i < numArgs; i++) { 93 | mQuery.bindString(i + 1, bindArgs[i]); 94 | } 95 | } 96 | 97 | @Override 98 | public void cursorDeactivated() { 99 | // Do nothing 100 | } 101 | 102 | @Override 103 | public void cursorRequeried(android.database.Cursor cursor) { 104 | // Do nothing 105 | } 106 | 107 | @Override 108 | public String toString() { 109 | return "SQLiteDirectCursorDriver: " + mSql; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteProgram.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 | package net.sqlcipher.database; 18 | 19 | import android.util.Log; 20 | import androidx.sqlite.db.SupportSQLiteProgram; 21 | 22 | /** 23 | * A base class for compiled SQLite programs. 24 | * 25 | * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple 26 | * threads should perform its own synchronization when using the SQLiteProgram. 27 | */ 28 | public abstract class SQLiteProgram extends SQLiteClosable implements 29 | SupportSQLiteProgram { 30 | 31 | private static final String TAG = "SQLiteProgram"; 32 | 33 | /** The database this program is compiled against. 34 | * @deprecated do not use this 35 | */ 36 | @Deprecated 37 | protected SQLiteDatabase mDatabase; 38 | 39 | /** The SQL used to create this query */ 40 | /* package */ final String mSql; 41 | 42 | /** 43 | * Native linkage, do not modify. This comes from the database and should not be modified 44 | * in here or in the native code. 45 | * @deprecated do not use this 46 | */ 47 | @Deprecated 48 | protected long nHandle = 0; 49 | 50 | /** 51 | * the SQLiteCompiledSql object for the given sql statement. 52 | */ 53 | private SQLiteCompiledSql mCompiledSql; 54 | 55 | /** 56 | * SQLiteCompiledSql statement id is populated with the corresponding object from the above 57 | * member. This member is used by the native_bind_* methods 58 | * @deprecated do not use this 59 | */ 60 | @Deprecated 61 | protected long nStatement = 0; 62 | 63 | /** 64 | * Indicates whether {@link #close()} has been called. 65 | */ 66 | boolean mClosed = false; 67 | 68 | /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { 69 | mDatabase = db; 70 | mSql = sql.trim(); 71 | db.acquireReference(); 72 | db.addSQLiteClosable(this); 73 | this.nHandle = db.mNativeHandle; 74 | int crudPrefixLength = 6; 75 | 76 | // only cache CRUD statements 77 | String prefixSql = mSql.length() >= crudPrefixLength ? mSql.substring(0, crudPrefixLength) : mSql; 78 | if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") && 79 | !prefixSql.equalsIgnoreCase("REPLAC") && 80 | !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) { 81 | mCompiledSql = new SQLiteCompiledSql(db, sql); 82 | nStatement = mCompiledSql.nStatement; 83 | // since it is not in the cache, no need to acquire() it. 84 | return; 85 | } 86 | 87 | // it is not pragma 88 | mCompiledSql = db.getCompiledStatementForSql(sql); 89 | if (mCompiledSql == null) { 90 | // create a new compiled-sql obj 91 | mCompiledSql = new SQLiteCompiledSql(db, sql); 92 | 93 | // add it to the cache of compiled-sqls 94 | // but before adding it and thus making it available for anyone else to use it, 95 | // make sure it is acquired by me. 96 | mCompiledSql.acquire(); 97 | db.addToCompiledQueries(sql, mCompiledSql); 98 | if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 99 | Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement + 100 | ") for sql: " + sql); 101 | } 102 | } else { 103 | // it is already in compiled-sql cache. 104 | // try to acquire the object. 105 | if (!mCompiledSql.acquire()) { 106 | long last = mCompiledSql.nStatement; 107 | // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object. 108 | // we can't have two different SQLiteProgam objects can't share the same 109 | // CompiledSql object. create a new one. 110 | // finalize it when I am done with it in "this" object. 111 | mCompiledSql = new SQLiteCompiledSql(db, sql); 112 | if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 113 | Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" + 114 | mCompiledSql.nStatement + 115 | ") because the previously created DbObj (id#" + last + 116 | ") was not released for sql:" + sql); 117 | } 118 | // since it is not in the cache, no need to acquire() it. 119 | } 120 | } 121 | nStatement = mCompiledSql.nStatement; 122 | } 123 | 124 | @Override 125 | protected void onAllReferencesReleased() { 126 | releaseCompiledSqlIfNotInCache(); 127 | mDatabase.releaseReference(); 128 | mDatabase.removeSQLiteClosable(this); 129 | } 130 | 131 | @Override 132 | protected void onAllReferencesReleasedFromContainer() { 133 | releaseCompiledSqlIfNotInCache(); 134 | mDatabase.releaseReference(); 135 | } 136 | 137 | private void releaseCompiledSqlIfNotInCache() { 138 | if (mCompiledSql == null) { 139 | return; 140 | } 141 | synchronized(mDatabase.mCompiledQueries) { 142 | if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) { 143 | // it is NOT in compiled-sql cache. i.e., responsibility of 144 | // releasing this statement is on me. 145 | mCompiledSql.releaseSqlStatement(); 146 | mCompiledSql = null; 147 | nStatement = 0; 148 | } else { 149 | // it is in compiled-sql cache. reset its CompiledSql#mInUse flag 150 | mCompiledSql.release(); 151 | } 152 | } 153 | } 154 | 155 | /** 156 | * Returns a unique identifier for this program. 157 | * 158 | * @return a unique identifier for this program 159 | */ 160 | public final long getUniqueId() { 161 | return nStatement; 162 | } 163 | 164 | /* package */ String getSqlString() { 165 | return mSql; 166 | } 167 | 168 | /** 169 | * @deprecated This method is deprecated and must not be used. 170 | * 171 | * @param sql the SQL string to compile 172 | * @param forceCompilation forces the SQL to be recompiled in the event that there is an 173 | * existing compiled SQL program already around 174 | */ 175 | @Deprecated 176 | protected void compile(String sql, boolean forceCompilation) { 177 | // TODO is there a need for this? 178 | } 179 | 180 | /** 181 | * Bind a NULL value to this statement. The value remains bound until 182 | * {@link #clearBindings} is called. 183 | * 184 | * @param index The 1-based index to the parameter to bind null to 185 | */ 186 | @Override 187 | public void bindNull(int index) { 188 | if (mClosed) { 189 | throw new IllegalStateException("program already closed"); 190 | } 191 | if (!mDatabase.isOpen()) { 192 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 193 | } 194 | acquireReference(); 195 | try { 196 | native_bind_null(index); 197 | } finally { 198 | releaseReference(); 199 | } 200 | } 201 | 202 | /** 203 | * Bind a long value to this statement. The value remains bound until 204 | * {@link #clearBindings} is called. 205 | * 206 | * @param index The 1-based index to the parameter to bind 207 | * @param value The value to bind 208 | */ 209 | @Override 210 | public void bindLong(int index, long value) { 211 | if (mClosed) { 212 | throw new IllegalStateException("program already closed"); 213 | } 214 | if (!mDatabase.isOpen()) { 215 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 216 | } 217 | acquireReference(); 218 | try { 219 | native_bind_long(index, value); 220 | } finally { 221 | releaseReference(); 222 | } 223 | } 224 | 225 | /** 226 | * Bind a double value to this statement. The value remains bound until 227 | * {@link #clearBindings} is called. 228 | * 229 | * @param index The 1-based index to the parameter to bind 230 | * @param value The value to bind 231 | */ 232 | @Override 233 | public void bindDouble(int index, double value) { 234 | if (mClosed) { 235 | throw new IllegalStateException("program already closed"); 236 | } 237 | if (!mDatabase.isOpen()) { 238 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 239 | } 240 | acquireReference(); 241 | try { 242 | native_bind_double(index, value); 243 | } finally { 244 | releaseReference(); 245 | } 246 | } 247 | 248 | /** 249 | * Bind a String value to this statement. The value remains bound until 250 | * {@link #clearBindings} is called. 251 | * 252 | * @param index The 1-based index to the parameter to bind 253 | * @param value The value to bind 254 | */ 255 | @Override 256 | public void bindString(int index, String value) { 257 | if (value == null) { 258 | throw new IllegalArgumentException("the bind value at index " + index + " is null"); 259 | } 260 | if (mClosed) { 261 | throw new IllegalStateException("program already closed"); 262 | } 263 | if (!mDatabase.isOpen()) { 264 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 265 | } 266 | acquireReference(); 267 | try { 268 | native_bind_string(index, value); 269 | } finally { 270 | releaseReference(); 271 | } 272 | } 273 | 274 | /** 275 | * Bind a byte array value to this statement. The value remains bound until 276 | * {@link #clearBindings} is called. 277 | * 278 | * @param index The 1-based index to the parameter to bind 279 | * @param value The value to bind 280 | */ 281 | @Override 282 | public void bindBlob(int index, byte[] value) { 283 | if (value == null) { 284 | throw new IllegalArgumentException("the bind value at index " + index + " is null"); 285 | } 286 | if (mClosed) { 287 | throw new IllegalStateException("program already closed"); 288 | } 289 | if (!mDatabase.isOpen()) { 290 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 291 | } 292 | acquireReference(); 293 | try { 294 | native_bind_blob(index, value); 295 | } finally { 296 | releaseReference(); 297 | } 298 | } 299 | 300 | /** 301 | * Clears all existing bindings. Unset bindings are treated as NULL. 302 | */ 303 | @Override 304 | public void clearBindings() { 305 | if (mClosed) { 306 | throw new IllegalStateException("program already closed"); 307 | } 308 | if (!mDatabase.isOpen()) { 309 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 310 | } 311 | acquireReference(); 312 | try { 313 | native_clear_bindings(); 314 | } finally { 315 | releaseReference(); 316 | } 317 | } 318 | 319 | /** 320 | * Release this program's resources, making it invalid. 321 | */ 322 | public void close() { 323 | if (mClosed) { 324 | return; 325 | } 326 | if (!mDatabase.isOpen()) { 327 | return; 328 | } 329 | mDatabase.lock(); 330 | try { 331 | releaseReference(); 332 | } finally { 333 | mDatabase.unlock(); 334 | } 335 | mClosed = true; 336 | } 337 | 338 | /** 339 | * @deprecated This method is deprecated and must not be used. 340 | * Compiles SQL into a SQLite program. 341 | * 342 | *

The database lock must be held when calling this method. 343 | * @param sql The SQL to compile. 344 | */ 345 | @Deprecated 346 | protected final native void native_compile(String sql); 347 | 348 | /** 349 | * @deprecated This method is deprecated and must not be used. 350 | */ 351 | @Deprecated 352 | protected final native void native_finalize(); 353 | 354 | protected final native void native_bind_null(int index); 355 | protected final native void native_bind_long(int index, long value); 356 | protected final native void native_bind_double(int index, double value); 357 | protected final native void native_bind_string(int index, String value); 358 | protected final native void native_bind_blob(int index, byte[] value); 359 | private final native void native_clear_bindings(); 360 | } 361 | 362 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | import net.sqlcipher.*; 19 | 20 | import android.database.sqlite.SQLiteDatabaseCorruptException; 21 | import android.database.sqlite.SQLiteMisuseException; 22 | import android.os.SystemClock; 23 | import android.util.Log; 24 | 25 | /** 26 | * A SQLite program that represents a query that reads the resulting rows into a CursorWindow. 27 | * This class is used by SQLiteCursor and isn't useful itself. 28 | * 29 | * SQLiteQuery is not internally synchronized so code using a SQLiteQuery from multiple 30 | * threads should perform its own synchronization when using the SQLiteQuery. 31 | */ 32 | public class SQLiteQuery extends SQLiteProgram { 33 | private static final String TAG = "Cursor"; 34 | 35 | /** The index of the unbound OFFSET parameter */ 36 | private int mOffsetIndex; 37 | 38 | /** Args to bind on requery */ 39 | private String[] mBindArgs; 40 | private Object[] mObjectBindArgs; 41 | 42 | /** 43 | * Create a persistent query object. 44 | * 45 | * @param db The database that this query object is associated with 46 | * @param query The SQL string for this query. 47 | * @param offsetIndex The 1-based index to the OFFSET parameter, 48 | */ 49 | /* package */ SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, String[] bindArgs) { 50 | super(db, query); 51 | 52 | mOffsetIndex = offsetIndex; 53 | mBindArgs = bindArgs; 54 | } 55 | 56 | SQLiteQuery(SQLiteDatabase db, String query, int offsetIndex, Object[] bindArgs) { 57 | super(db, query); 58 | mOffsetIndex = offsetIndex; 59 | mObjectBindArgs = bindArgs; 60 | int length = mObjectBindArgs != null ? mObjectBindArgs.length : 0; 61 | mBindArgs = new String[length]; 62 | } 63 | 64 | /** 65 | * Reads rows into a buffer. This method acquires the database lock. 66 | * 67 | * @param window The window to fill into 68 | * @return number of total rows in the query 69 | */ 70 | /* package */ 71 | int fillWindow(CursorWindow window, 72 | int maxRead, int lastPos) { 73 | long timeStart = SystemClock.uptimeMillis(); 74 | mDatabase.lock(); 75 | try { 76 | acquireReference(); 77 | try { 78 | window.acquireReference(); 79 | // if the start pos is not equal to 0, then most likely window is 80 | // too small for the data set, loading by another thread 81 | // is not safe in this situation. the native code will ignore maxRead 82 | int numRows = native_fill_window(window, 83 | window.getStartPosition(), 84 | window.getRequiredPosition(), 85 | mOffsetIndex, 86 | maxRead, lastPos); 87 | 88 | // Logging 89 | if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { 90 | Log.d(TAG, "fillWindow(): " + mSql); 91 | } 92 | return numRows; 93 | } catch (IllegalStateException e){ 94 | // simply ignore it 95 | return 0; 96 | } catch (SQLiteDatabaseCorruptException e) { 97 | mDatabase.onCorruption(); 98 | throw e; 99 | } finally { 100 | window.releaseReference(); 101 | } 102 | } finally { 103 | releaseReference(); 104 | mDatabase.unlock(); 105 | } 106 | } 107 | 108 | /** 109 | * Get the column count for the statement. Only valid on query based 110 | * statements. The database must be locked 111 | * when calling this method. 112 | * 113 | * @return The number of column in the statement's result set. 114 | */ 115 | /* package */ int columnCountLocked() { 116 | acquireReference(); 117 | try { 118 | return native_column_count(); 119 | } finally { 120 | releaseReference(); 121 | } 122 | } 123 | 124 | /** 125 | * Retrieves the column name for the given column index. The database must be locked 126 | * when calling this method. 127 | * 128 | * @param columnIndex the index of the column to get the name for 129 | * @return The requested column's name 130 | */ 131 | /* package */ String columnNameLocked(int columnIndex) { 132 | acquireReference(); 133 | try { 134 | return native_column_name(columnIndex); 135 | } finally { 136 | releaseReference(); 137 | } 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | return "SQLiteQuery: " + mSql; 143 | } 144 | 145 | /** 146 | * Called by SQLiteCursor when it is requeried. 147 | */ 148 | /* package */ void requery() { 149 | if (mBindArgs != null) { 150 | int len = mBindArgs.length; 151 | try { 152 | if(mObjectBindArgs != null) { 153 | bindArguments(mObjectBindArgs); 154 | } else { 155 | for (int i = 0; i < len; i++) { 156 | super.bindString(i + 1, mBindArgs[i]); 157 | } 158 | } 159 | } catch (SQLiteMisuseException e) { 160 | StringBuilder errMsg = new StringBuilder("mSql " + mSql); 161 | for (int i = 0; i < len; i++) { 162 | errMsg.append(" "); 163 | errMsg.append(mBindArgs[i]); 164 | } 165 | errMsg.append(" "); 166 | IllegalStateException leakProgram = new IllegalStateException( 167 | errMsg.toString(), e); 168 | throw leakProgram; 169 | } 170 | } 171 | } 172 | 173 | @Override 174 | public void bindNull(int index) { 175 | mBindArgs[index - 1] = null; 176 | if (!mClosed) super.bindNull(index); 177 | } 178 | 179 | @Override 180 | public void bindLong(int index, long value) { 181 | mBindArgs[index - 1] = Long.toString(value); 182 | if (!mClosed) super.bindLong(index, value); 183 | } 184 | 185 | @Override 186 | public void bindDouble(int index, double value) { 187 | mBindArgs[index - 1] = Double.toString(value); 188 | if (!mClosed) super.bindDouble(index, value); 189 | } 190 | 191 | @Override 192 | public void bindString(int index, String value) { 193 | mBindArgs[index - 1] = value; 194 | if (!mClosed) super.bindString(index, value); 195 | } 196 | 197 | public void bindArguments(Object[] args){ 198 | if(args != null && args.length > 0){ 199 | for(int i = 0; i < args.length; i++){ 200 | Object value = args[i]; 201 | if(value == null){ 202 | bindNull(i + 1); 203 | } else if (value instanceof Double) { 204 | bindDouble(i + 1, (Double)value); 205 | } else if (value instanceof Float) { 206 | float number = ((Number)value).floatValue(); 207 | bindDouble(i + 1, Double.valueOf(number)); 208 | } else if (value instanceof Long) { 209 | bindLong(i + 1, (Long)value); 210 | } else if(value instanceof Integer) { 211 | int number = ((Number) value).intValue(); 212 | bindLong(i + 1, Long.valueOf(number)); 213 | } else if (value instanceof Boolean) { 214 | bindLong(i + 1, (Boolean)value ? 1 : 0); 215 | } else if (value instanceof byte[]) { 216 | bindBlob(i + 1, (byte[])value); 217 | } else { 218 | bindString(i + 1, value.toString()); 219 | } 220 | } 221 | } 222 | } 223 | 224 | private final native int native_fill_window(CursorWindow window, 225 | int startPos, int requiredPos, 226 | int offsetParam, int maxRead, 227 | int lastPos); 228 | 229 | private final native int native_column_count(); 230 | 231 | private final native String native_column_name(int columnIndex); 232 | } 233 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteQueryStats.java: -------------------------------------------------------------------------------- 1 | package net.sqlcipher.database; 2 | 3 | public class SQLiteQueryStats { 4 | long totalQueryResultSize = 0L; 5 | long largestIndividualRowSize = 0L; 6 | 7 | public SQLiteQueryStats(long totalQueryResultSize, 8 | long largestIndividualRowSize) { 9 | this.totalQueryResultSize = totalQueryResultSize; 10 | this.largestIndividualRowSize = largestIndividualRowSize; 11 | } 12 | 13 | public long getTotalQueryResultSize(){ 14 | return totalQueryResultSize; 15 | } 16 | 17 | public long getLargestIndividualRowSize(){ 18 | return largestIndividualRowSize; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SQLiteStatement.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 | package net.sqlcipher.database; 18 | 19 | import android.os.SystemClock; 20 | import androidx.sqlite.db.SupportSQLiteStatement; 21 | 22 | /** 23 | * A pre-compiled statement against a {@link SQLiteDatabase} that can be reused. 24 | * The statement cannot return multiple rows, but 1x1 result sets are allowed. 25 | * Don't use SQLiteStatement constructor directly, please use 26 | * {@link SQLiteDatabase#compileStatement(String)} 27 | * 28 | * SQLiteStatement is not internally synchronized so code using a SQLiteStatement from multiple 29 | * threads should perform its own synchronization when using the SQLiteStatement. 30 | */ 31 | public class SQLiteStatement extends SQLiteProgram implements 32 | SupportSQLiteStatement 33 | { 34 | /** 35 | * Don't use SQLiteStatement constructor directly, please use 36 | * {@link SQLiteDatabase#compileStatement(String)} 37 | * @param db 38 | * @param sql 39 | */ 40 | /* package */ SQLiteStatement(SQLiteDatabase db, String sql) { 41 | super(db, sql); 42 | } 43 | 44 | /** 45 | * Execute this SQL statement, if it is not a query. For example, 46 | * CREATE TABLE, DELTE, INSERT, etc. 47 | * 48 | * @throws android.database.SQLException If the SQL string is invalid for 49 | * some reason 50 | */ 51 | @Override 52 | public void execute() { 53 | if (!mDatabase.isOpen()) { 54 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 55 | } 56 | long timeStart = SystemClock.uptimeMillis(); 57 | mDatabase.lock(); 58 | 59 | acquireReference(); 60 | try { 61 | native_execute(); 62 | } finally { 63 | releaseReference(); 64 | mDatabase.unlock(); 65 | } 66 | } 67 | 68 | /** 69 | * Execute this SQL statement and return the ID of the row inserted due to this call. 70 | * The SQL statement should be an INSERT for this to be a useful call. 71 | * 72 | * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise. 73 | * 74 | * @throws android.database.SQLException If the SQL string is invalid for 75 | * some reason 76 | */ 77 | @Override 78 | public long executeInsert() { 79 | if (!mDatabase.isOpen()) { 80 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 81 | } 82 | long timeStart = SystemClock.uptimeMillis(); 83 | mDatabase.lock(); 84 | 85 | acquireReference(); 86 | try { 87 | native_execute(); 88 | return (mDatabase.lastChangeCount() > 0) ? mDatabase.lastInsertRow() : -1; 89 | } finally { 90 | releaseReference(); 91 | mDatabase.unlock(); 92 | } 93 | } 94 | 95 | @Override 96 | public int executeUpdateDelete() { 97 | if (!mDatabase.isOpen()) { 98 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 99 | } 100 | long timeStart = SystemClock.uptimeMillis(); 101 | mDatabase.lock(); 102 | 103 | acquireReference(); 104 | try { 105 | native_execute(); 106 | return mDatabase.lastChangeCount(); 107 | } finally { 108 | releaseReference(); 109 | mDatabase.unlock(); 110 | } 111 | } 112 | 113 | /** 114 | * Execute a statement that returns a 1 by 1 table with a numeric value. 115 | * For example, SELECT COUNT(*) FROM table; 116 | * 117 | * @return The result of the query. 118 | * 119 | * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows 120 | */ 121 | @Override 122 | public long simpleQueryForLong() { 123 | if (!mDatabase.isOpen()) { 124 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 125 | } 126 | long timeStart = SystemClock.uptimeMillis(); 127 | mDatabase.lock(); 128 | 129 | acquireReference(); 130 | try { 131 | long retValue = native_1x1_long(); 132 | return retValue; 133 | } finally { 134 | releaseReference(); 135 | mDatabase.unlock(); 136 | } 137 | } 138 | 139 | /** 140 | * Execute a statement that returns a 1 by 1 table with a text value. 141 | * For example, SELECT COUNT(*) FROM table; 142 | * 143 | * @return The result of the query. 144 | * 145 | * @throws android.database.sqlite.SQLiteDoneException if the query returns zero rows 146 | */ 147 | @Override 148 | public String simpleQueryForString() { 149 | if (!mDatabase.isOpen()) { 150 | throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 151 | } 152 | long timeStart = SystemClock.uptimeMillis(); 153 | mDatabase.lock(); 154 | 155 | acquireReference(); 156 | try { 157 | String retValue = native_1x1_string(); 158 | return retValue; 159 | } finally { 160 | releaseReference(); 161 | mDatabase.unlock(); 162 | } 163 | } 164 | 165 | private final native void native_execute(); 166 | private final native long native_1x1_long(); 167 | private final native String native_1x1_string(); 168 | } 169 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/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 | package net.sqlcipher.database; 18 | 19 | /** 20 | * A listener for transaction events. 21 | */ 22 | public interface SQLiteTransactionListener { 23 | /** 24 | * Called immediately after the transaction begins. 25 | */ 26 | void onBegin(); 27 | 28 | /** 29 | * Called immediately before commiting the transaction. 30 | */ 31 | void onCommit(); 32 | 33 | /** 34 | * Called if the transaction is about to be rolled back. 35 | */ 36 | void onRollback(); 37 | } 38 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SqliteWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 Esmertec AG. 3 | * Copyright (C) 2008 The Android Open Source Project 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * 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 | package net.sqlcipher.database; 19 | 20 | import android.content.ContentResolver; 21 | import android.content.ContentValues; 22 | import android.content.Context; 23 | 24 | import net.sqlcipher.*; 25 | 26 | import android.database.sqlite.SQLiteException; 27 | import android.net.Uri; 28 | import android.util.Log; 29 | import android.widget.Toast; 30 | 31 | /** 32 | * @hide 33 | */ 34 | 35 | public final class SqliteWrapper { 36 | private static final String TAG = "SqliteWrapper"; 37 | private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE 38 | = "unable to open database file"; 39 | 40 | private SqliteWrapper() { 41 | // Forbidden being instantiated. 42 | } 43 | 44 | // FIXME: need to optimize this method. 45 | private static boolean isLowMemory(SQLiteException e) { 46 | return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE); 47 | } 48 | 49 | public static void checkSQLiteException(Context context, SQLiteException e) { 50 | if (isLowMemory(e)) { 51 | Toast.makeText(context, e.getMessage(), 52 | Toast.LENGTH_SHORT).show(); 53 | } else { 54 | throw e; 55 | } 56 | } 57 | 58 | public static Cursor query(Context context, ContentResolver resolver, Uri uri, 59 | String[] projection, String selection, String[] selectionArgs, String sortOrder) { 60 | try { 61 | return (Cursor) resolver.query(uri, projection, selection, selectionArgs, sortOrder); 62 | } catch (SQLiteException e) { 63 | Log.e(TAG, "Catch a SQLiteException when query: ", e); 64 | checkSQLiteException(context, e); 65 | return null; 66 | } 67 | } 68 | 69 | public static boolean requery(Context context, android.database.Cursor cursor) { 70 | try { 71 | return cursor.requery(); 72 | } catch (SQLiteException e) { 73 | Log.e(TAG, "Catch a SQLiteException when requery: ", e); 74 | checkSQLiteException(context, e); 75 | return false; 76 | } 77 | } 78 | public static int update(Context context, ContentResolver resolver, Uri uri, 79 | ContentValues values, String where, String[] selectionArgs) { 80 | try { 81 | return resolver.update(uri, values, where, selectionArgs); 82 | } catch (SQLiteException e) { 83 | Log.e(TAG, "Catch a SQLiteException when update: ", e); 84 | checkSQLiteException(context, e); 85 | return -1; 86 | } 87 | } 88 | 89 | public static int delete(Context context, ContentResolver resolver, Uri uri, 90 | String where, String[] selectionArgs) { 91 | try { 92 | return resolver.delete(uri, where, selectionArgs); 93 | } catch (SQLiteException e) { 94 | Log.e(TAG, "Catch a SQLiteException when delete: ", e); 95 | checkSQLiteException(context, e); 96 | return -1; 97 | } 98 | } 99 | 100 | public static Uri insert(Context context, ContentResolver resolver, 101 | Uri uri, ContentValues values) { 102 | try { 103 | return resolver.insert(uri, values); 104 | } catch (SQLiteException e) { 105 | Log.e(TAG, "Catch a SQLiteException when insert: ", e); 106 | checkSQLiteException(context, e); 107 | return null; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Mark L. Murphy 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.sqlcipher.database; 18 | 19 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 20 | 21 | public class SupportFactory implements SupportSQLiteOpenHelper.Factory { 22 | private final byte[] passphrase; 23 | private final SQLiteDatabaseHook hook; 24 | private final boolean clearPassphrase; 25 | 26 | public SupportFactory(byte[] passphrase) { 27 | this(passphrase, (SQLiteDatabaseHook)null); 28 | } 29 | 30 | public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook) { 31 | this(passphrase, hook, true); 32 | } 33 | 34 | public SupportFactory(byte[] passphrase, SQLiteDatabaseHook hook, 35 | boolean clearPassphrase) { 36 | this.passphrase = passphrase; 37 | this.hook = hook; 38 | this.clearPassphrase = clearPassphrase; 39 | } 40 | 41 | @Override 42 | public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) { 43 | return new SupportHelper(configuration, passphrase, hook, clearPassphrase); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/SupportHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Mark L. Murphy 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.sqlcipher.database; 18 | 19 | import android.database.sqlite.SQLiteException; 20 | import androidx.sqlite.db.SupportSQLiteDatabase; 21 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 22 | 23 | public class SupportHelper implements SupportSQLiteOpenHelper { 24 | private SQLiteOpenHelper standardHelper; 25 | private byte[] passphrase; 26 | private final boolean clearPassphrase; 27 | 28 | SupportHelper(final SupportSQLiteOpenHelper.Configuration configuration, 29 | byte[] passphrase, final SQLiteDatabaseHook hook, 30 | boolean clearPassphrase) { 31 | SQLiteDatabase.loadLibs(configuration.context); 32 | this.passphrase = passphrase; 33 | this.clearPassphrase = clearPassphrase; 34 | 35 | standardHelper = 36 | new SQLiteOpenHelper(configuration.context, configuration.name, 37 | null, configuration.callback.version, hook) { 38 | @Override 39 | public void onCreate(SQLiteDatabase db) { 40 | configuration.callback.onCreate(db); 41 | } 42 | 43 | @Override 44 | public void onUpgrade(SQLiteDatabase db, int oldVersion, 45 | int newVersion) { 46 | configuration.callback.onUpgrade(db, oldVersion, 47 | newVersion); 48 | } 49 | 50 | @Override 51 | public void onDowngrade(SQLiteDatabase db, int oldVersion, 52 | int newVersion) { 53 | configuration.callback.onDowngrade(db, oldVersion, 54 | newVersion); 55 | } 56 | 57 | @Override 58 | public void onOpen(SQLiteDatabase db) { 59 | configuration.callback.onOpen(db); 60 | } 61 | 62 | @Override 63 | public void onConfigure(SQLiteDatabase db) { 64 | configuration.callback.onConfigure(db); 65 | } 66 | }; 67 | } 68 | 69 | @Override 70 | public String getDatabaseName() { 71 | return standardHelper.getDatabaseName(); 72 | } 73 | 74 | @Override 75 | public void setWriteAheadLoggingEnabled(boolean enabled) { 76 | standardHelper.setWriteAheadLoggingEnabled(enabled); 77 | } 78 | 79 | @Override 80 | public SupportSQLiteDatabase getWritableDatabase() { 81 | SQLiteDatabase result; 82 | try { 83 | result = standardHelper.getWritableDatabase(passphrase); 84 | } catch (SQLiteException ex){ 85 | if(passphrase != null){ 86 | boolean isCleared = true; 87 | for(byte b : passphrase){ 88 | isCleared = isCleared && (b == (byte)0); 89 | } 90 | if (isCleared) { 91 | throw new IllegalStateException("The passphrase appears to be cleared. This happens by " + 92 | "default the first time you use the factory to open a database, so we can remove the " + 93 | "cleartext passphrase from memory. If you close the database yourself, please use a " + 94 | "fresh SupportFactory to reopen it. If something else (e.g., Room) closed the " + 95 | "database, and you cannot control that, use SupportFactory boolean constructor option " + 96 | "to opt out of the automatic password clearing step. See the project README for more information.", ex); 97 | } 98 | } 99 | throw ex; 100 | } 101 | if(clearPassphrase && passphrase != null) { 102 | for (int i = 0; i < passphrase.length; i++) { 103 | passphrase[i] = (byte)0; 104 | } 105 | } 106 | return result; 107 | } 108 | 109 | @Override 110 | public SupportSQLiteDatabase getReadableDatabase() { 111 | return getWritableDatabase(); 112 | } 113 | 114 | @Override 115 | public void close() { 116 | standardHelper.close(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/database/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains the SQLCipher database managements classes that an application would use to manage its own private database. 3 | */ 4 | package net.sqlcipher.database; 5 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/java/net/sqlcipher/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Contains classes to explore data returned from a SQLCipher database. 3 | */ 4 | package net.sqlcipher; 5 | -------------------------------------------------------------------------------- /android-database-sqlcipher/src/main/res/values/android_database_sqlcipher_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Zetetic, LLC 4 | https://www.zetetic.net/sqlcipher/ 5 | SQLCipher for Android 6 | Android SQLite API based on SQLCipher 7 | https://www.zetetic.net/sqlcipher/ 8 | ${clientVersionNumber} 9 | true 10 | https://github.com/sqlcipher/android-database-sqlcipher 11 | https://www.zetetic.net/sqlcipher/license/ 12 | 13 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | maven { 6 | url "https://plugins.gradle.org/m2/" 7 | } 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:7.3.1' 11 | classpath "gradle.plugin.org.ec4j.gradle:editorconfig-gradle-plugin:0.0.3" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | ext { 23 | if(project.hasProperty('sqlcipherAndroidClientVersion')) { 24 | clientVersionNumber = "${sqlcipherAndroidClientVersion}" 25 | } else { 26 | clientVersionNumber = "UndefinedBuildNumber" 27 | } 28 | mavenPackaging = "aar" 29 | mavenGroup = "net.zetetic" 30 | mavenArtifactId = "android-database-sqlcipher" 31 | mavenLocalRepositoryPrefix = "file://" 32 | if(project.hasProperty('publishLocal') && publishLocal.toBoolean()){ 33 | mavenSnapshotRepositoryUrl = "outputs/snapshot" 34 | mavenReleaseRepositoryUrl = "outputs/release" 35 | } else { 36 | mavenLocalRepositoryPrefix = "" 37 | mavenSnapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots" 38 | mavenReleaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" 39 | } 40 | if(project.hasProperty('publishSnapshot') && publishSnapshot.toBoolean()){ 41 | mavenVersionName = "${clientVersionNumber}-SNAPSHOT" 42 | } else { 43 | mavenVersionName = "${clientVersionNumber}" 44 | } 45 | if(project.hasProperty('nexusUsername')){ 46 | nexusUsername = "${nexusUsername}" 47 | } 48 | if(project.hasProperty('nexusPassword')){ 49 | nexusPassword = "${nexusPassword}" 50 | } 51 | mavenPomDescription = "SQLCipher for Android is a plugin to SQLite that provides full database encryption." 52 | mavenPomUrl = "https://www.zetetic.net/sqlcipher" 53 | mavenScmUrl = "https://github.com/sqlcipher/android-database-sqlcipher.git" 54 | mavenScmConnection = "scm:git:https://github.com/sqlcipher/android-database-sqlcipher.git" 55 | mavenScmDeveloperConnection = "scm:git:https://github.com/sqlcipher/android-database-sqlcipher.git" 56 | mavenLicenseUrl = "https://www.zetetic.net/sqlcipher/license/" 57 | mavenDeveloperName = "Zetetic Support" 58 | mavenDeveloperEmail = "support@zetetic.net" 59 | mavenDeveloperOrganization = "Zetetic LLC" 60 | mavenDeveloperUrl = "https://www.zetetic.net" 61 | minimumAndroidSdkVersion = 21 62 | minimumAndroid64BitSdkVersion = 21 63 | targetAndroidSdkVersion = 26 64 | compileAndroidSdkVersion = 26 65 | mainProjectName = "android-database-sqlcipher" 66 | nativeRootOutputDir = "${projectDir}/${mainProjectName}/src/main" 67 | if(project.hasProperty('sqlcipherRoot')) { 68 | sqlcipherDir = "${sqlcipherRoot}" 69 | } 70 | if(project.hasProperty('opensslAndroidNativeRoot') && "${opensslAndroidNativeRoot}") { 71 | androidNativeRootDir = "${opensslAndroidNativeRoot}" 72 | } else { 73 | androidNativeRootDir = "${nativeRootOutputDir}/external/android-libs" 74 | } 75 | if(project.hasProperty('opensslRoot')) { 76 | opensslDir = "${opensslRoot}" 77 | } 78 | if(project.hasProperty('debugBuild') && debugBuild.toBoolean()) { 79 | otherSqlcipherCFlags = "-fstack-protector-all" 80 | ndkBuildType="NDK_DEBUG=1" 81 | } else { 82 | otherSqlcipherCFlags = "-DLOG_NDEBUG -fstack-protector-all" 83 | ndkBuildType="NDK_DEBUG=0" 84 | } 85 | if(project.hasProperty('sqlcipherCFlags') 86 | && project.sqlcipherCFlags?.trim() 87 | && project.sqlcipherCFlags?.contains('SQLITE_HAS_CODEC') 88 | && project.sqlcipherCFlags?.contains('SQLITE_TEMP_STORE')) { 89 | sqlcipherCFlags = "${sqlcipherCFlags}" 90 | } else { 91 | if(!project.gradle.startParameter.taskNames.toString().contains('clean')){ 92 | throw new InvalidUserDataException("SQLCIPHER_CFLAGS environment variable must be specified and include at least '-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2'") 93 | } 94 | } 95 | } 96 | 97 | task clean(type: Delete) { 98 | delete rootProject.buildDir 99 | } 100 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sqlcipher/android-database-sqlcipher/b3df67d8731bd0ebd987acb561a2c3fdd8e17082/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':android-database-sqlcipher' 2 | --------------------------------------------------------------------------------