diff --git a/.gitignore b/.gitignore index 590efdcc..baa403e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ TAGS external/libs jni/libs obj/ +.DS_Store +local.properties +build.xml +proguard.cfg +bin/ +gen/ diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a954bbda --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ + +.DEFAULT_GOAL := all + +init: + git submodule update --init + android update project -p . + +all: build-external build-jni build-java + +build-external: + cd external/ && \ + make -f Android.mk build-local-hack && \ + ndk-build && \ + make -f Android.mk copy-libs-hack + +build-jni: + cd jni/ && ndk-build + +build-java: + ant compile && \ + cd bin/classes && \ + jar -cvf sqlcipher.jar . diff --git a/README b/README.org similarity index 64% rename from README rename to README.org index c1bfc26d..6f51ad21 100644 --- a/README +++ b/README.org @@ -1,14 +1,14 @@ Current SDK distro for developers, with the jar’s, .so’s and a quick sample can be found here: - https://github.com/guardianproject/android-database-sqlcipher/downloads + [[https://github.com/guardianproject/android-database-sqlcipher/downloads]] A full demonstration app with the bundled SQLCipher R1 release is here: -https://github.com/guardianproject/notepadbot +[[https://github.com/guardianproject/notepadbot]] SQLCipher for Android project source repo is here: -https://github.com/guardianproject/android-database-sqlcipher +[[https://github.com/guardianproject/android-database-sqlcipher]] -Update May 2011: +*** Update May 2011: After some major breakthroughs during last week’s development sprint, we’re extremely excited to announce SQLCipher for Android, Developer Preview r1. SQLCipher is an SQLite extension that provides transparent 256-bit AES encryption of database files. To date, it has been open-sourced, sponsored and maintained by Zetetic LLC, and we are glad to be able to extend their efforts to a new mobile platform. In the mobile space, SQLCipher has enjoyed widespread use in Apple’s iOS, as well as Nokia / QT for quite some time. Given that Android by default provides integrated support for SQLite databases, our goal was to create an almost identical API for SQLCipher, so that developers of all skill level could use it, without a steep learning curve. @@ -16,37 +16,37 @@ In an environment where mobile data privacy is increasingly in the headlines, th However, while the core SQLCipher database is vetted and market-ready, the Android support libraries in this release are still very much alpha quality, hence the Developer Preview label. This R1 release should not be integrated into critical or production software. Our goal is to give Android developers early access to the technology, so they can provide feedback on our approach, and help us deliver the right offering for securing mobile data. We expect to release a market-ready version this summer, and will be publicly iterating through the codebase until then. -An Illustrative Terminal Listing +*** An Illustrative Terminal Listing 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 db and one implementing SQLCipher. -~ sjlombardo$ hexdump -C sqlite.db -00000000 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 |SQLite format 3.| -… -000003c0 65 74 32 74 32 03 43 52 45 41 54 45 20 54 41 42 |et2t2.CREATE TAB| -000003d0 4c 45 20 74 32 28 61 2c 62 29 24 01 06 17 11 11 |LE t2(a,b)$…..| -… -000007e0 20 74 68 65 20 73 68 6f 77 15 01 03 01 2f 01 6f | the show…./.o| -000007f0 6e 65 20 66 6f 72 20 74 68 65 20 6d 6f 6e 65 79 |ne for the money| - -~ $ sqlite3 sqlcipher.db -sqlite> PRAGMA KEY=’test123′; -sqlite> CREATE TABLE t1(a,b); -sqlite> INSERT INTO t1(a,b) VALUES (‘one for the money’, ‘two for the show’); -sqlite> .quit - -~ $ hexdump -C sqlite.db -00000000 84 d1 36 18 eb b5 82 90 c4 70 0d ee 43 cb 61 87 |.?6.?..?p.?C?a.| -00000010 91 42 3c cd 55 24 ab c6 c4 1d c6 67 b4 e3 96 bb |.B?..?| -00000bf0 8e 99 ee 28 23 43 ab a4 97 cd 63 42 8a 8e 7c c6 |..?(#C??.?cB..|?| - -~ $ sqlite3 sqlcipher.db -sqlite> SELECT * FROM t1; -Error: file is encrypted or is not a database +: ~ sjlombardo$ hexdump -C sqlite.db +: 00000000 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 |SQLite format 3.| +: … +: 000003c0 65 74 32 74 32 03 43 52 45 41 54 45 20 54 41 42 |et2t2.CREATE TAB| +: 000003d0 4c 45 20 74 32 28 61 2c 62 29 24 01 06 17 11 11 |LE t2(a,b)$…..| +: … +: 000007e0 20 74 68 65 20 73 68 6f 77 15 01 03 01 2f 01 6f | the show…./.o| +: 000007f0 6e 65 20 66 6f 72 20 74 68 65 20 6d 6f 6e 65 79 |ne for the money| +: +: ~ $ sqlite3 sqlcipher.db +: sqlite> PRAGMA KEY=’test123′; +: sqlite> CREATE TABLE t1(a,b); +: sqlite> INSERT INTO t1(a,b) VALUES (‘one for the money’, ‘two for the show’); +: sqlite> .quit +: +: ~ $ hexdump -C sqlite.db +: 00000000 84 d1 36 18 eb b5 82 90 c4 70 0d ee 43 cb 61 87 |.?6.?..?p.?C?a.| +: 00000010 91 42 3c cd 55 24 ab c6 c4 1d c6 67 b4 e3 96 bb |.B?..?| +: 00000bf0 8e 99 ee 28 23 43 ab a4 97 cd 63 42 8a 8e 7c c6 |..?(#C??.?cB..|?| +: +: ~ $ sqlite3 sqlcipher.db +: sqlite> SELECT * FROM t1; +: Error: file is encrypted or is not a database (example courtesy of SQLCipher) -Details for Developers +*** Details for Developers We’ve packaged up a very simple SDK for any Android developer to add SQLCipher into their app with the following three steps: @@ -57,7 +57,7 @@ SQLiteDatabase.loadLibs(this); //first init the db libraries with the context SQLiteOpenHelper.getWritableDatabase(“thisismysecret”): *Note: we are working on some dialog builder helper methods for password and PIN input, password caching, and other features that we would like to standardize across all applications that use SQLCipher. -Compatibility +*** Compatibility The Developer Preview implements SQLCipher v1, is compatible with Android 2.2 & 2.3, and works only within one process (you can’t pass a Cursor from a remote Service to an Activity). @@ -65,15 +65,26 @@ Notepad + SQLCipher = Notepadbot Notepadbot is a sample application pulled from the standard Android samples code and updated to use SQLCipher. You can browse the source here and download the apk here. +*** Building -Final Notes +In order to build android-database-sqlcipher from source you will need both the Android SDK as well as Android NDK. Once you have cloned the repo, change directory into the root of the repository and run the following commands: + +: # this only needs to be done once +: make init + +: # to build the source +: make + +Copy =libsqlcipher_android.so= in =external/libs/armeabi= and =libdatabase_sqlcipher.so= in =jni/libs/armeabi= to your application =libs/armeabi= folder. Copy the =sqlcipher.jar= file in =bin/classes= to your =libs= directory. Copy the =icudt44l.zip= file in the =assets= directory to your =assets= directory. Finally, you will need to copy =commons-codec.jar= and =guava-r09.jar= located in the =libs= directory into your application =libs= directory. + +*** Final Notes It’s important to note that this project is not intended to be a distinct, long-term fork of SQLCipher. We’ve been working closely with the SQLCipher team at Zetetic and fully intent to closely maintain the project as SQLCipher evolves, re-integrating changes in upcoming releases such as SQLCipher v2. 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. -Downloads and Source +*** Downloads and Source -SQLCipher for Android project source repo is here: https://github.com/guardianproject/android-database-sqlcipher -Current SDK distro for developers, with the jar’s, .so’s and a quick sample can be found here: https://github.com/guardianproject/android-database-sqlcipher/downloads +SQLCipher for Android project source repo is here: [[https://github.com/guardianproject/android-database-sqlcipher]] +Current SDK distro for developers, with the jar’s, .so’s and a quick sample can be found here: [[https://github.com/guardianproject/android-database-sqlcipher/downloads]] diff --git a/external/Android.mk b/external/Android.mk index 3cbf5d02..9be0a98d 100644 --- a/external/Android.mk +++ b/external/Android.mk @@ -5,28 +5,29 @@ # ndk-build # make -f Android.mk copy-libs-hack -LOCAL_PATH := $(call my-dir) +PROJECT_ROOT_PATH := $(call my-dir) +LOCAL_PATH := $(PROJECT_ROOT_PATH) LOCAL_PRELINK_MODULE := false # how on earth to you make this damn Android build system run cmd line progs?!?! build-local-hack: sqlcipher/sqlite3.c ../obj/local/armeabi/libcrypto.so sqlcipher/sqlite3.c: - cd sqlcipher && ./configure + cd sqlcipher && ./configure --enable-tempstore=yes CFLAGS="-DSQL_HAS_CODEC" LDFLAGS="-lcrypto" make -C sqlcipher sqlite3.c # TODO include this Android.mk to integrate this into the build ../obj/local/armeabi/libcrypto.so: cd openssl && ndk-build -j4 + mkdir -p ../obj/local/armeabi install -p openssl/libs/armeabi/libcrypto.so openssl/libs/armeabi/libssl.so \ ../obj/local/armeabi/ copy-libs-hack: build-local-hack install -p -m644 openssl/libs/armeabi/*.so ../obj/local/armeabi/ install -p -m644 libs/armeabi/*.so ../obj/local/armeabi/ -## install -p -m644 android-2.2/*.so ../obj/local/armeabi/ -project_ldflags:= -Llibs/armeabi/ -Landroid-2.3/ +project_ldflags:= -Llibs/armeabi/ -Landroid-2.1/ #------------------------------------------------------------------------------# # libsqlite3 @@ -35,7 +36,7 @@ project_ldflags:= -Llibs/armeabi/ -Landroid-2.3/ # SQLITE_TEMP_STORE=3 causes all TEMP files to go into RAM. and thats the behavior we want # SQLITE_ENABLE_FTS3 enables usage of FTS3 - NOT FTS1 or 2. # SQLITE_DEFAULT_AUTOVACUUM=1 causes the databases to be subject to auto-vacuum -android_sqlite_cflags := -DHAVE_USLEEP=1 -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 -DSQLITE_THREADSAFE=1 -DNDEBUG=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_BACKWARDS +android_sqlite_cflags := -DHAVE_USLEEP=1 -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 -DSQLITE_THREADSAFE=1 -DNDEBUG=1 -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 -DSQLITE_DEFAULT_AUTOVACUUM=1 -DSQLITE_TEMP_STORE=3 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_BACKWARDS -DSQLITE_ENABLE_LOAD_EXTENSION sqlcipher_files := \ sqlcipher/sqlite3.c @@ -51,18 +52,19 @@ LOCAL_LDLIBS += -lcrypto LOCAL_MODULE := libsqlcipher LOCAL_SRC_FILES := $(sqlcipher_files) -include $(BUILD_SHARED_LIBRARY) +include $(BUILD_STATIC_LIBRARY) #------------------------------------------------------------------------------# # libsqlcipher_android (our version of Android's libsqlite_android) # these are all files from various external git repos libsqlite3_android_local_src_files := \ - android-sqlite/android/sqlite3_android.cpp -# android-sqlite/android/PhonebookIndex.cpp \ -# android-sqlite/android/PhoneNumberUtils.cpp \ -# android-sqlite/android/OldPhoneNumberUtils.cpp \ -# android-sqlite/android/PhoneticStringUtils.cpp \ + android-sqlite/android/sqlite3_android.cpp \ + android-sqlite/android/PhonebookIndex.cpp \ + android-sqlite/android/PhoneNumberUtils.cpp \ + android-sqlite/android/OldPhoneNumberUtils.cpp \ + android-sqlite/android/PhoneticStringUtils.cpp \ + platform-frameworks-base/libs/utils/String8.cpp # android-sqlite/android/PhoneNumberUtilsTest.cpp \ # android-sqlite/android/PhoneticStringUtilsTest.cpp \ @@ -73,20 +75,302 @@ include $(CLEAR_VARS) LOCAL_ALLOW_UNDEFINED_SYMBOLS := false # TODO this needs to depend on libsqlcipher being built, how to do that? +#LOCAL_REQUIRED_MODULES += libsqlcipher libicui18n libicuuc +LOCAL_STATIC_LIBRARIES := libsqlcipher libicui18n libicuuc + +LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlite_cflags) -DOS_PATH_SEPARATOR="'/'" -LOCAL_REQUIRED_MODULES += libsqlcipher -LOCAL_CFLAGS += $(android_sqlite_cflags) $(sqlite_cflags) LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/sqlcipher \ $(LOCAL_PATH)/include \ $(LOCAL_PATH)/icu4c/i18n \ $(LOCAL_PATH)/icu4c/common \ -# $(LOCAL_PATH)/platform-system-core/include \ -# $(LOCAL_PATH)/platform-frameworks-base/include -#LOCAL_LDFLAGS += $(project_ldflags) -LOCAL_LDFLAGS += -L$(LOCAL_PATH)/android-2.3/ -L$(LOCAL_PATH)/libs/armeabi/ -LOCAL_LDLIBS := -lsqlcipher -llog -licuuc -licui18n -lutils -lcutils + $(LOCAL_PATH)/platform-system-core/include \ + $(LOCAL_PATH)/platform-frameworks-base/include + +LOCAL_LDFLAGS += -L$(LOCAL_PATH)/android-2.1/ -L$(LOCAL_PATH)/libs/armeabi/ +LOCAL_LDLIBS := -llog -lutils -lcutils -lcrypto LOCAL_MODULE := libsqlcipher_android +LOCAL_MODULE_FILENAME := libsqlcipher_android LOCAL_SRC_FILES := $(libsqlite3_android_local_src_files) include $(BUILD_SHARED_LIBRARY) + +#------------------------- +# start icu project import +#------------------------- + +#include $(LOCAL_PATH)/icu4c/Android.mk + +#LOCAL_PATH := $(call my-dir) +#include $(CLEAR_VARS) + +#include $(CLEAR_VARS) + +ICU_COMMON_PATH := icu4c/common + +icu_src_files := \ + $(ICU_COMMON_PATH)/cmemory.c $(ICU_COMMON_PATH)/cstring.c \ + $(ICU_COMMON_PATH)/cwchar.c $(ICU_COMMON_PATH)/locmap.c \ + $(ICU_COMMON_PATH)/punycode.c $(ICU_COMMON_PATH)/putil.c \ + $(ICU_COMMON_PATH)/uarrsort.c $(ICU_COMMON_PATH)/ubidi.c \ + $(ICU_COMMON_PATH)/ubidiln.c $(ICU_COMMON_PATH)/ubidi_props.c \ + $(ICU_COMMON_PATH)/ubidiwrt.c $(ICU_COMMON_PATH)/ucase.c \ + $(ICU_COMMON_PATH)/ucasemap.c $(ICU_COMMON_PATH)/ucat.c \ + $(ICU_COMMON_PATH)/uchar.c $(ICU_COMMON_PATH)/ucln_cmn.c \ + $(ICU_COMMON_PATH)/ucmndata.c \ + $(ICU_COMMON_PATH)/ucnv2022.c $(ICU_COMMON_PATH)/ucnv_bld.c \ + $(ICU_COMMON_PATH)/ucnvbocu.c $(ICU_COMMON_PATH)/ucnv.c \ + $(ICU_COMMON_PATH)/ucnv_cb.c $(ICU_COMMON_PATH)/ucnv_cnv.c \ + $(ICU_COMMON_PATH)/ucnvdisp.c $(ICU_COMMON_PATH)/ucnv_err.c \ + $(ICU_COMMON_PATH)/ucnv_ext.c $(ICU_COMMON_PATH)/ucnvhz.c \ + $(ICU_COMMON_PATH)/ucnv_io.c $(ICU_COMMON_PATH)/ucnvisci.c \ + $(ICU_COMMON_PATH)/ucnvlat1.c $(ICU_COMMON_PATH)/ucnv_lmb.c \ + $(ICU_COMMON_PATH)/ucnvmbcs.c $(ICU_COMMON_PATH)/ucnvscsu.c \ + $(ICU_COMMON_PATH)/ucnv_set.c $(ICU_COMMON_PATH)/ucnv_u16.c \ + $(ICU_COMMON_PATH)/ucnv_u32.c $(ICU_COMMON_PATH)/ucnv_u7.c \ + $(ICU_COMMON_PATH)/ucnv_u8.c \ + $(ICU_COMMON_PATH)/udata.c $(ICU_COMMON_PATH)/udatamem.c \ + $(ICU_COMMON_PATH)/udataswp.c $(ICU_COMMON_PATH)/uenum.c \ + $(ICU_COMMON_PATH)/uhash.c $(ICU_COMMON_PATH)/uinit.c \ + $(ICU_COMMON_PATH)/uinvchar.c $(ICU_COMMON_PATH)/uloc.c \ + $(ICU_COMMON_PATH)/umapfile.c $(ICU_COMMON_PATH)/umath.c \ + $(ICU_COMMON_PATH)/umutex.c $(ICU_COMMON_PATH)/unames.c \ + $(ICU_COMMON_PATH)/unorm_it.c $(ICU_COMMON_PATH)/uresbund.c \ + $(ICU_COMMON_PATH)/ures_cnv.c $(ICU_COMMON_PATH)/uresdata.c \ + $(ICU_COMMON_PATH)/usc_impl.c $(ICU_COMMON_PATH)/uscript.c \ + $(ICU_COMMON_PATH)/ushape.c $(ICU_COMMON_PATH)/ustrcase.c \ + $(ICU_COMMON_PATH)/ustr_cnv.c $(ICU_COMMON_PATH)/ustrfmt.c \ + $(ICU_COMMON_PATH)/ustring.c $(ICU_COMMON_PATH)/ustrtrns.c \ + $(ICU_COMMON_PATH)/ustr_wcs.c $(ICU_COMMON_PATH)/utf_impl.c \ + $(ICU_COMMON_PATH)/utrace.c $(ICU_COMMON_PATH)/utrie.c \ + $(ICU_COMMON_PATH)/utypes.c $(ICU_COMMON_PATH)/wintz.c \ + $(ICU_COMMON_PATH)/utrie2_builder.c $(ICU_COMMON_PATH)/icuplug.c \ + $(ICU_COMMON_PATH)/propsvec.c $(ICU_COMMON_PATH)/ulist.c \ + $(ICU_COMMON_PATH)/uloc_tag.c + +icu_src_files += \ + $(ICU_COMMON_PATH)/bmpset.cpp $(ICU_COMMON_PATH)/unisetspan.cpp \ + $(ICU_COMMON_PATH)/brkeng.cpp $(ICU_COMMON_PATH)/brkiter.cpp \ + $(ICU_COMMON_PATH)/caniter.cpp $(ICU_COMMON_PATH)/chariter.cpp \ + $(ICU_COMMON_PATH)/dictbe.cpp $(ICU_COMMON_PATH)/locbased.cpp \ + $(ICU_COMMON_PATH)/locid.cpp $(ICU_COMMON_PATH)/locutil.cpp \ + $(ICU_COMMON_PATH)/normlzr.cpp $(ICU_COMMON_PATH)/parsepos.cpp \ + $(ICU_COMMON_PATH)/propname.cpp $(ICU_COMMON_PATH)/rbbi.cpp \ + $(ICU_COMMON_PATH)/rbbidata.cpp $(ICU_COMMON_PATH)/rbbinode.cpp \ + $(ICU_COMMON_PATH)/rbbirb.cpp $(ICU_COMMON_PATH)/rbbiscan.cpp \ + $(ICU_COMMON_PATH)/rbbisetb.cpp $(ICU_COMMON_PATH)/rbbistbl.cpp \ + $(ICU_COMMON_PATH)/rbbitblb.cpp $(ICU_COMMON_PATH)/resbund_cnv.cpp \ + $(ICU_COMMON_PATH)/resbund.cpp $(ICU_COMMON_PATH)/ruleiter.cpp \ + $(ICU_COMMON_PATH)/schriter.cpp $(ICU_COMMON_PATH)/serv.cpp \ + $(ICU_COMMON_PATH)/servlk.cpp $(ICU_COMMON_PATH)/servlkf.cpp \ + $(ICU_COMMON_PATH)/servls.cpp $(ICU_COMMON_PATH)/servnotf.cpp \ + $(ICU_COMMON_PATH)/servrbf.cpp $(ICU_COMMON_PATH)/servslkf.cpp \ + $(ICU_COMMON_PATH)/triedict.cpp $(ICU_COMMON_PATH)/ubrk.cpp \ + $(ICU_COMMON_PATH)/uchriter.cpp $(ICU_COMMON_PATH)/uhash_us.cpp \ + $(ICU_COMMON_PATH)/uidna.cpp $(ICU_COMMON_PATH)/uiter.cpp \ + $(ICU_COMMON_PATH)/unifilt.cpp $(ICU_COMMON_PATH)/unifunct.cpp \ + $(ICU_COMMON_PATH)/uniset.cpp $(ICU_COMMON_PATH)/uniset_props.cpp \ + $(ICU_COMMON_PATH)/unistr_case.cpp $(ICU_COMMON_PATH)/unistr_cnv.cpp \ + $(ICU_COMMON_PATH)/unistr.cpp $(ICU_COMMON_PATH)/unistr_props.cpp \ + $(ICU_COMMON_PATH)/unormcmp.cpp $(ICU_COMMON_PATH)/unorm.cpp \ + $(ICU_COMMON_PATH)/uobject.cpp $(ICU_COMMON_PATH)/uset.cpp \ + $(ICU_COMMON_PATH)/usetiter.cpp $(ICU_COMMON_PATH)/uset_props.cpp \ + $(ICU_COMMON_PATH)/usprep.cpp $(ICU_COMMON_PATH)/ustack.cpp \ + $(ICU_COMMON_PATH)/ustrenum.cpp $(ICU_COMMON_PATH)/utext.cpp \ + $(ICU_COMMON_PATH)/util.cpp $(ICU_COMMON_PATH)/util_props.cpp \ + $(ICU_COMMON_PATH)/uvector.cpp $(ICU_COMMON_PATH)/uvectr32.cpp \ + $(ICU_COMMON_PATH)/errorcode.cpp \ + $(ICU_COMMON_PATH)/bytestream.cpp $(ICU_COMMON_PATH)/stringpiece.cpp \ + $(ICU_COMMON_PATH)/mutex.cpp $(ICU_COMMON_PATH)/dtintrv.cpp \ + $(ICU_COMMON_PATH)/ucnvsel.cpp $(ICU_COMMON_PATH)/uvectr64.cpp \ + $(ICU_COMMON_PATH)/locavailable.cpp $(ICU_COMMON_PATH)/locdispnames.cpp \ + $(ICU_COMMON_PATH)/loclikely.cpp $(ICU_COMMON_PATH)/locresdata.cpp \ + $(ICU_COMMON_PATH)/normalizer2impl.cpp $(ICU_COMMON_PATH)/normalizer2.cpp \ + $(ICU_COMMON_PATH)/filterednormalizer2.cpp $(ICU_COMMON_PATH)/ucol_swp.cpp \ + $(ICU_COMMON_PATH)/uprops.cpp $(ICU_COMMON_PATH)/utrie2.cpp + + +# This is the empty compiled-in icu data structure +# that we need to satisfy the linker. +icu_src_files += $(ICU_COMMON_PATH)/../stubdata/stubdata.c + +icu_c_includes := \ + $(ICU_COMMON_PATH)/ \ + $(ICU_COMMON_PATH)//../i18n + +# We make the ICU data directory relative to $ANDROID_ROOT on Android, so both +# device and sim builds can use the same codepath, and it's hard to break one +# without noticing because the other still works. + +icu_local_cflags += -D_REENTRANT -DU_COMMON_IMPLEMENTATION -O3 -DHAVE_ANDROID_OS=1 +icu_local_cflags += '-DICU_DATA_DIR_PREFIX_ENV_VAR="SQLCIPHER_ICU_PREFIX"' +icu_local_cflags += '-DICU_DATA_DIR="/icu"' +icu_local_ldlibs := -lc + +# +# Build for the target (device). +# + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(icu_src_files) +LOCAL_C_INCLUDES := $(icu_c_includes) +LOCAL_CFLAGS := $(icu_local_cflags) -DPIC -fPIC +LOCAL_LDLIBS += $(icu_local_ldlibs) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libicuuc +include $(BUILD_STATIC_LIBRARY) + +#---------- +# end icuuc +#---------- + +#-------------- +# start icui18n +#-------------- +include $(CLEAR_VARS) +LOCAL_PATH := $(PROJECT_ROOT_PATH) +#ICU_I18N_PATH := $(LOCAL_PATH)/icu4c/i18n +ICU_I18N_PATH := icu4c/i18n + +src_files := \ + $(ICU_I18N_PATH)/bocsu.c $(ICU_I18N_PATH)/ucln_in.c $(ICU_I18N_PATH)/decContext.c \ + $(ICU_I18N_PATH)/ulocdata.c $(ICU_I18N_PATH)/utmscale.c $(ICU_I18N_PATH)/decNumber.c + +src_files += \ + $(ICU_I18N_PATH)/indiancal.cpp $(ICU_I18N_PATH)/dtptngen.cpp $(ICU_I18N_PATH)/dtrule.cpp \ + $(ICU_I18N_PATH)/persncal.cpp $(ICU_I18N_PATH)/rbtz.cpp $(ICU_I18N_PATH)/reldtfmt.cpp \ + $(ICU_I18N_PATH)/taiwncal.cpp $(ICU_I18N_PATH)/tzrule.cpp $(ICU_I18N_PATH)/tztrans.cpp \ + $(ICU_I18N_PATH)/udatpg.cpp $(ICU_I18N_PATH)/vtzone.cpp \ + $(ICU_I18N_PATH)/anytrans.cpp $(ICU_I18N_PATH)/astro.cpp $(ICU_I18N_PATH)/buddhcal.cpp \ + $(ICU_I18N_PATH)/basictz.cpp $(ICU_I18N_PATH)/calendar.cpp $(ICU_I18N_PATH)/casetrn.cpp \ + $(ICU_I18N_PATH)/choicfmt.cpp $(ICU_I18N_PATH)/coleitr.cpp $(ICU_I18N_PATH)/coll.cpp \ + $(ICU_I18N_PATH)/cpdtrans.cpp $(ICU_I18N_PATH)/csdetect.cpp $(ICU_I18N_PATH)/csmatch.cpp \ + $(ICU_I18N_PATH)/csr2022.cpp $(ICU_I18N_PATH)/csrecog.cpp $(ICU_I18N_PATH)/csrmbcs.cpp \ + $(ICU_I18N_PATH)/csrsbcs.cpp $(ICU_I18N_PATH)/csrucode.cpp $(ICU_I18N_PATH)/csrutf8.cpp \ + $(ICU_I18N_PATH)/curramt.cpp $(ICU_I18N_PATH)/currfmt.cpp $(ICU_I18N_PATH)/currunit.cpp \ + $(ICU_I18N_PATH)/datefmt.cpp $(ICU_I18N_PATH)/dcfmtsym.cpp $(ICU_I18N_PATH)/decimfmt.cpp \ + $(ICU_I18N_PATH)/digitlst.cpp $(ICU_I18N_PATH)/dtfmtsym.cpp $(ICU_I18N_PATH)/esctrn.cpp \ + $(ICU_I18N_PATH)/fmtable_cnv.cpp $(ICU_I18N_PATH)/fmtable.cpp $(ICU_I18N_PATH)/format.cpp \ + $(ICU_I18N_PATH)/funcrepl.cpp $(ICU_I18N_PATH)/gregocal.cpp $(ICU_I18N_PATH)/gregoimp.cpp \ + $(ICU_I18N_PATH)/hebrwcal.cpp $(ICU_I18N_PATH)/inputext.cpp $(ICU_I18N_PATH)/islamcal.cpp \ + $(ICU_I18N_PATH)/japancal.cpp $(ICU_I18N_PATH)/measfmt.cpp $(ICU_I18N_PATH)/measure.cpp \ + $(ICU_I18N_PATH)/msgfmt.cpp $(ICU_I18N_PATH)/name2uni.cpp $(ICU_I18N_PATH)/nfrs.cpp \ + $(ICU_I18N_PATH)/nfrule.cpp $(ICU_I18N_PATH)/nfsubs.cpp $(ICU_I18N_PATH)/nortrans.cpp \ + $(ICU_I18N_PATH)/nultrans.cpp $(ICU_I18N_PATH)/numfmt.cpp $(ICU_I18N_PATH)/olsontz.cpp \ + $(ICU_I18N_PATH)/quant.cpp $(ICU_I18N_PATH)/rbnf.cpp $(ICU_I18N_PATH)/rbt.cpp \ + $(ICU_I18N_PATH)/rbt_data.cpp $(ICU_I18N_PATH)/rbt_pars.cpp $(ICU_I18N_PATH)/rbt_rule.cpp \ + $(ICU_I18N_PATH)/rbt_set.cpp $(ICU_I18N_PATH)/regexcmp.cpp $(ICU_I18N_PATH)/regexst.cpp \ + $(ICU_I18N_PATH)/rematch.cpp $(ICU_I18N_PATH)/remtrans.cpp $(ICU_I18N_PATH)/repattrn.cpp \ + $(ICU_I18N_PATH)/search.cpp $(ICU_I18N_PATH)/simpletz.cpp $(ICU_I18N_PATH)/smpdtfmt.cpp \ + $(ICU_I18N_PATH)/sortkey.cpp $(ICU_I18N_PATH)/strmatch.cpp $(ICU_I18N_PATH)/strrepl.cpp \ + $(ICU_I18N_PATH)/stsearch.cpp $(ICU_I18N_PATH)/tblcoll.cpp $(ICU_I18N_PATH)/timezone.cpp \ + $(ICU_I18N_PATH)/titletrn.cpp $(ICU_I18N_PATH)/tolowtrn.cpp $(ICU_I18N_PATH)/toupptrn.cpp \ + $(ICU_I18N_PATH)/translit.cpp $(ICU_I18N_PATH)/transreg.cpp $(ICU_I18N_PATH)/tridpars.cpp \ + $(ICU_I18N_PATH)/ucal.cpp $(ICU_I18N_PATH)/ucol_bld.cpp $(ICU_I18N_PATH)/ucol_cnt.cpp \ + $(ICU_I18N_PATH)/ucol.cpp $(ICU_I18N_PATH)/ucoleitr.cpp $(ICU_I18N_PATH)/ucol_elm.cpp \ + $(ICU_I18N_PATH)/ucol_res.cpp $(ICU_I18N_PATH)/ucol_sit.cpp $(ICU_I18N_PATH)/ucol_tok.cpp \ + $(ICU_I18N_PATH)/ucsdet.cpp $(ICU_I18N_PATH)/ucurr.cpp $(ICU_I18N_PATH)/udat.cpp \ + $(ICU_I18N_PATH)/umsg.cpp $(ICU_I18N_PATH)/unesctrn.cpp $(ICU_I18N_PATH)/uni2name.cpp \ + $(ICU_I18N_PATH)/unum.cpp $(ICU_I18N_PATH)/uregexc.cpp $(ICU_I18N_PATH)/uregex.cpp \ + $(ICU_I18N_PATH)/usearch.cpp $(ICU_I18N_PATH)/utrans.cpp $(ICU_I18N_PATH)/windtfmt.cpp \ + $(ICU_I18N_PATH)/winnmfmt.cpp $(ICU_I18N_PATH)/zonemeta.cpp $(ICU_I18N_PATH)/zstrfmt.cpp \ + $(ICU_I18N_PATH)/numsys.cpp $(ICU_I18N_PATH)/chnsecal.cpp \ + $(ICU_I18N_PATH)/cecal.cpp $(ICU_I18N_PATH)/coptccal.cpp $(ICU_I18N_PATH)/ethpccal.cpp \ + $(ICU_I18N_PATH)/brktrans.cpp $(ICU_I18N_PATH)/wintzimpl.cpp $(ICU_I18N_PATH)/plurrule.cpp \ + $(ICU_I18N_PATH)/plurfmt.cpp $(ICU_I18N_PATH)/dtitvfmt.cpp $(ICU_I18N_PATH)/dtitvinf.cpp \ + $(ICU_I18N_PATH)/tmunit.cpp $(ICU_I18N_PATH)/tmutamt.cpp $(ICU_I18N_PATH)/tmutfmt.cpp \ + $(ICU_I18N_PATH)/colldata.cpp $(ICU_I18N_PATH)/bmsearch.cpp $(ICU_I18N_PATH)/bms.cpp \ + $(ICU_I18N_PATH)/currpinf.cpp $(ICU_I18N_PATH)/uspoof.cpp $(ICU_I18N_PATH)/uspoof_impl.cpp \ + $(ICU_I18N_PATH)/uspoof_build.cpp \ + $(ICU_I18N_PATH)/regextxt.cpp $(ICU_I18N_PATH)/selfmt.cpp $(ICU_I18N_PATH)/uspoof_conf.cpp \ + $(ICU_I18N_PATH)/uspoof_wsconf.cpp $(ICU_I18N_PATH)/ztrans.cpp $(ICU_I18N_PATH)/zrule.cpp \ + $(ICU_I18N_PATH)/vzone.cpp $(ICU_I18N_PATH)/fphdlimp.cpp $(ICU_I18N_PATH)/fpositer.cpp\ + $(ICU_I18N_PATH)/locdspnm.cpp $(ICU_I18N_PATH)/decnumstr.cpp $(ICU_I18N_PATH)/ucol_wgt.cpp + +c_includes = \ + $(ICU_I18N_PATH)/ \ + $(ICU_I18N_PATH)/../common + + +# +# Build for the target (device). +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(src_files) +LOCAL_C_INCLUDES := $(c_includes) + +LOCAL_CFLAGS += -D_REENTRANT -DPIC -DU_I18N_IMPLEMENTATION -fPIC +LOCAL_CFLAGS += -O3 + +LOCAL_STATIC_LIBRARIES += libicuuc +LOCAL_LDLIBS += -lc +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libicui18n + +include $(BUILD_STATIC_LIBRARY) + +#------------ +# end icui18n +#------------ + +#--------------- +# start stubdata +#--------------- + +# Build configuration: +# +# "Large" includes all the supported locales. +# Japanese includes US and Japan. +# US-Euro is needed for IT or PL builds +# Default is suitable for CS, DE, EN, ES, FR, NL +# US has only EN and ES + +config := $(word 1, \ + $(if $(findstring ar,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring da,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring el,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring fi,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring he,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring hr,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring hu,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring id,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring ko,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring nb,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring pt,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring ro,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring ru,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring sk,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring sr,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring sv,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring th,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring tr,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring uk,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring zh,$(PRODUCT_LOCALES)),large) \ + $(if $(findstring ja,$(PRODUCT_LOCALES)),us-japan) \ + $(if $(findstring it,$(PRODUCT_LOCALES)),us-euro) \ + $(if $(findstring pl,$(PRODUCT_LOCALES)),us-euro) \ + $(if $(findstring cs,$(PRODUCT_LOCALES)),default) \ + $(if $(findstring de,$(PRODUCT_LOCALES)),default) \ + $(if $(findstring fr,$(PRODUCT_LOCALES)),default) \ + $(if $(findstring nl,$(PRODUCT_LOCALES)),default) \ + us) + +#include $(LOCAL_PATH)/root.mk +# derive a string like 'icudt44l' from a local file like 'external/icu4c/stubdata/icudt44l-all.dat' +stubdata_path:= $(PROJECT_ROOT_PATH)/icu4c/stubdata +root_dat_path := $(wildcard $(stubdata_path)/*-all.dat) +root := $(patsubst $(stubdata_path)/%,%,$(patsubst %-all.dat,%,$(root_dat_path))) + + +PRODUCT_COPY_FILES += $(LOCAL_PATH)/$(root)-$(config).dat:/system/usr/icu/$(root).dat + +ifeq ($(WITH_HOST_DALVIK),true) + $(eval $(call copy-one-file,$(LOCAL_PATH)/$(root)-$(config).dat,$(HOST_OUT)/usr/icu/$(root).dat)) +endif + +#------------- +# end stubdata +#------------- diff --git a/external/android-2.2/libandroid_runtime.so b/external/android-2.2/libandroid_runtime.so new file mode 100644 index 00000000..fa370f30 Binary files /dev/null and b/external/android-2.2/libandroid_runtime.so differ diff --git a/external/android-2.2/libbinder.so b/external/android-2.2/libbinder.so new file mode 100644 index 00000000..5cbc35f2 Binary files /dev/null and b/external/android-2.2/libbinder.so differ diff --git a/external/android-2.3/libandroid_runtime.so b/external/android-2.3/libandroid_runtime.so new file mode 100644 index 00000000..fa370f30 Binary files /dev/null and b/external/android-2.3/libandroid_runtime.so differ diff --git a/external/android-2.3/libbinder.so b/external/android-2.3/libbinder.so new file mode 100644 index 00000000..5cbc35f2 Binary files /dev/null and b/external/android-2.3/libbinder.so differ diff --git a/jni/Android.mk b/jni/Android.mk index 2fd39253..4b9ec913 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -2,13 +2,14 @@ LOCAL_PATH:= $(call my-dir) EXTERNAL_PATH := ../external + ifeq ($(TARGET_ARCH), arm) LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))" else LOCAL_CFLAGS += -DPACKED="" endif -TARGET_PLATFORM := android-8 +#TARGET_PLATFORM := android-8 ifeq ($(WITH_JIT),true) LOCAL_CFLAGS += -DWITH_JIT @@ -21,14 +22,13 @@ endif include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - CursorWindow.cpp \ + info_guardianproject_database_sqlcipher_SQLiteCompiledSql.cpp \ info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp \ info_guardianproject_database_sqlcipher_SQLiteProgram.cpp \ info_guardianproject_database_sqlcipher_SQLiteQuery.cpp \ info_guardianproject_database_sqlcipher_SQLiteStatement.cpp \ - info_guardianproject_database_sqlcipher_SQLiteCompiledSql.cpp - #android_database_CursorWindow.cpp \ - #info_guardianproject_database_CursorWindow.cpp \ + info_guardianproject_database_CursorWindow.cpp \ + CursorWindow.cpp # info_guardianproject_database_sqlcipher_SQLiteDebug.cpp LOCAL_C_INCLUDES += \ @@ -50,7 +50,7 @@ LOCAL_SHARED_LIBRARIES := \ libsqlite3_android LOCAL_CFLAGS += -U__APPLE__ -LOCAL_LDFLAGS += -L../external/android-2.2/ -L../external/libs/armeabi/ +LOCAL_LDFLAGS += -L../external/android-2.3/ -L../external/libs/armeabi/ LOCAL_LDFLAGS += -L/home/n8fr8/android/mydroid/out/target/product/generic/obj/SHARED_LIBRARIES/libutils_intermediates/LINKED/ -L/home/n8fr8/android/mydroid/out/target/product/generic/obj/SHARED_LIBRARIES/libbinder_intermediates/LINKED/ -L/home/n8fr8/android/mydroid/out/target/product/generic/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/LINKED/ # libs from the NDK @@ -58,7 +58,9 @@ LOCAL_LDLIBS += -ldl -llog # libnativehelper and libandroid_runtime are included with Android but not the NDK LOCAL_LDLIBS += -lnativehelper -landroid_runtime -lutils -lbinder # these are build in the ../external section -LOCAL_LDLIBS += -lsqlcipher -lsqlcipher_android + +#LOCAL_REQUIRED_MODULES += libsqlcipher libicuuc libicui18n +LOCAL_LDLIBS += -lsqlcipher_android ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK diff --git a/jni/CursorWindow.cpp b/jni/CursorWindow.cpp index c1064739..a1666f9f 100644 --- a/jni/CursorWindow.cpp +++ b/jni/CursorWindow.cpp @@ -31,7 +31,7 @@ #include "CursorWindow.h" -namespace android { +namespace guardianproject { CursorWindow::CursorWindow(size_t maxSize) : mMaxSize(maxSize) diff --git a/jni/CursorWindow.h b/jni/CursorWindow.h index 0b886c16..f52eb5a9 100644 --- a/jni/CursorWindow.h +++ b/jni/CursorWindow.h @@ -62,7 +62,7 @@ // When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window #define WINDOW_STORAGE_INLINE_NUMERICS 1 -namespace android { +namespace guardianproject { typedef struct { diff --git a/jni/android_database_CursorWindow.cpp b/jni/info_guardianproject_database_CursorWindow.cpp similarity index 97% rename from jni/android_database_CursorWindow.cpp rename to jni/info_guardianproject_database_CursorWindow.cpp index 91449bc5..d2498033 100644 --- a/jni/android_database_CursorWindow.cpp +++ b/jni/info_guardianproject_database_CursorWindow.cpp @@ -33,8 +33,7 @@ #include "sqlite3_exception.h" #include "android_util_Binder.h" - -namespace android { +namespace guardianproject { static jfieldID gWindowField; static jfieldID gBufferField; @@ -74,7 +73,8 @@ LOG_WINDOW("native_init_empty: window = %p", window); static void native_init_memory(JNIEnv * env, jobject object, jobject memObj) { - sp memory = interface_cast(ibinderForJavaObject(env, memObj)); + android::sp memory = android::interface_cast(android::ibinderForJavaObject(env, memObj)); + if (memory == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Couldn't get native binder"); return; @@ -99,9 +99,9 @@ static jobject native_getBinder(JNIEnv * env, jobject object) { CursorWindow * window = GET_WINDOW(env, object); if (window) { - sp memory = window->getMemory(); + android::sp memory = window->getMemory(); if (memory != NULL) { - sp binder = memory->asBinder(); + android::sp binder = memory->asBinder(); return javaObjectForIBinder(env, binder); } } @@ -308,7 +308,7 @@ LOG_WINDOW("Getting string for %d,%d from %p", row, column, window); if (size > 0) { #if WINDOW_STORAGE_UTF8 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string - String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + android::String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); return env->NewString((jchar const *)utf16.string(), utf16.size()); #else return env->NewString((jchar const *)window->offsetToPtr(field.data.buffer.offset), size / 2); @@ -389,7 +389,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window); if (size > 0) { #if WINDOW_STORAGE_UTF8 // Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string - String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); + android::String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1); int32_t strSize = utf16.size(); if (strSize > bufferSize || dst == NULL) { newArray = env->NewCharArray(strSize); @@ -718,8 +718,8 @@ int register_android_database_CursorWindow(JNIEnv * env) return -1; } - return AndroidRuntime::registerNativeMethods(env, "android/database/CursorWindow", + return android::AndroidRuntime::registerNativeMethods(env, "info/guardianproject/database/CursorWindow", sMethods, NELEM(sMethods)); } -} // namespace android +} // namespace guardianproject diff --git a/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp b/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp index 0fbd50a9..3f6bcd8d 100644 --- a/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp +++ b/jni/info_guardianproject_database_sqlcipher_SQLiteDatabase.cpp @@ -36,10 +36,7 @@ #include #include -#include -#include #include -#include #include #include "sqlite3_exception.h" @@ -97,8 +94,13 @@ static void registerLoggingFunc(const char *path) { loggingFuncSet = true; } - - +/* public native void setICURoot(String path); */ +void setICURoot(JNIEnv* env, jobject object, jstring ICURoot) +{ + char const * ICURootPath = env->GetStringUTFChars(ICURoot, NULL); + setenv("SQLCIPHER_ICU_PREFIX", ICURootPath, 1); + env->ReleaseStringUTFChars(ICURoot, ICURootPath); +} /* public native void dbopen(String path, int flags, String locale); */ @@ -173,6 +175,8 @@ void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags) goto done; } + sqlite3_enable_load_extension(handle, 1); + LOGV("Opened '%s' - %p\n", path8, handle); env->SetIntField(object, offset_db_handle, (int) handle); handle = NULL; // The caller owns the handle now. @@ -464,7 +468,7 @@ static JNINativeMethod sMethods[] = {"native_setLocale", "(Ljava/lang/String;I)V", (void *)native_setLocale}, {"native_getDbLookaside", "()I", (void *)native_getDbLookaside}, {"releaseMemory", "()I", (void *)native_releaseMemory}, - + {"setICURoot", "(Ljava/lang/String;)V", (void *)setICURoot}, }; int register_android_database_SQLiteDatabase(JNIEnv *env) @@ -511,7 +515,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_database_SQLiteStatement(env); -// register_android_database_CursorWindow(env); + register_android_database_CursorWindow(env); //register_android_database_SQLiteDebug(env); diff --git a/jni/info_guardianproject_database_sqlcipher_SQLiteQuery.cpp b/jni/info_guardianproject_database_sqlcipher_SQLiteQuery.cpp index c50401e3..e069a1de 100644 --- a/jni/info_guardianproject_database_sqlcipher_SQLiteQuery.cpp +++ b/jni/info_guardianproject_database_sqlcipher_SQLiteQuery.cpp @@ -32,19 +32,14 @@ #include "CursorWindow.h" #include "sqlite3_exception.h" -namespace android { - -// From android_database_CursorWindow.cpp - CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow); -} namespace guardianproject { +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow); sqlite3_stmt * compile(JNIEnv* env, jobject object, sqlite3 * handle, jstring sqlString); - static jfieldID gHandleField; static jfieldID gStatementField; @@ -54,7 +49,6 @@ static jfieldID gStatementField; #define GET_HANDLE(env, object) \ (sqlite3 *)env->GetIntField(object, gHandleField) - static int skip_rows(sqlite3_stmt *statement, int maxRows) { int retryCount = 0; for (int i = 0; i < maxRows; i++) { @@ -120,7 +114,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, int numColumns; int retryCount; int boundParams; - android::CursorWindow * window; + CursorWindow * window; if (statement == NULL) { LOGE("Invalid statement in fillWindow()"); @@ -146,9 +140,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, } // Get the native window - window = android::get_window_from_object(env, javaWindow); - //window = GET_WINDOW(env, javaWindow); - + window = get_window_from_object(env, javaWindow); if (!window) { LOGE("Invalid CursorWindow"); jniThrowException(env, "java/lang/IllegalArgumentException", @@ -186,7 +178,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, // since it mey be possible for it to be relocated on a call to alloc() when // the field data is being allocated. { - android::field_slot_t * fieldDir = window->allocRow(); + field_slot_t * fieldDir = window->allocRow(); if (!fieldDir) { LOGE("Failed allocating fieldDir at startPos %d row %d", startPos, numRows); return startPos + numRows + finish_program_and_get_row_count(statement) + 1; @@ -221,7 +213,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, // This must be updated after the call to alloc(), since that // may move the field around in the window - android::field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); + field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); fieldSlot->type = FIELD_TYPE_STRING; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; @@ -261,7 +253,7 @@ static jint native_fill_window(JNIEnv* env, jobject object, jobject javaWindow, // This must be updated after the call to alloc(), since that // may move the field around in the window - android::field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); + field_slot_t * fieldSlot = window->getFieldSlot(numRows, i); fieldSlot->type = FIELD_TYPE_BLOB; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; @@ -343,7 +335,7 @@ static jstring native_column_name(JNIEnv* env, jobject object, jint columnIndex) static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ - {"native_fill_window", "(Landroid/database/CursorWindow;IIII)I", (void *)native_fill_window}, + {"native_fill_window", "(Linfo/guardianproject/database/CursorWindow;IIII)I", (void *)native_fill_window}, {"native_column_count", "()I", (void*)native_column_count}, {"native_column_name", "(I)Ljava/lang/String;", (void *)native_column_name}, }; diff --git a/libs/armeabi/libdatabase_sqlcipher.so b/libs/armeabi/libdatabase_sqlcipher.so deleted file mode 100755 index 27ecd1a8..00000000 Binary files a/libs/armeabi/libdatabase_sqlcipher.so and /dev/null differ diff --git a/libs/armeabi/libsqlcipher.so b/libs/armeabi/libsqlcipher.so deleted file mode 100644 index 4fec579b..00000000 Binary files a/libs/armeabi/libsqlcipher.so and /dev/null differ diff --git a/libs/armeabi/libsqlcipher_android-10.so b/libs/armeabi/libsqlcipher_android-10.so deleted file mode 100755 index 1c754942..00000000 Binary files a/libs/armeabi/libsqlcipher_android-10.so and /dev/null differ diff --git a/libs/armeabi/libsqlcipher_android-8.so b/libs/armeabi/libsqlcipher_android-8.so deleted file mode 100755 index 79c38f6b..00000000 Binary files a/libs/armeabi/libsqlcipher_android-8.so and /dev/null differ diff --git a/libs/armeabi/libsqlcipher_android-9.so b/libs/armeabi/libsqlcipher_android-9.so deleted file mode 100755 index 1c754942..00000000 Binary files a/libs/armeabi/libsqlcipher_android-9.so and /dev/null differ diff --git a/src/info/guardianproject/database/AbstractCursor.java b/src/info/guardianproject/database/AbstractCursor.java index ebd9f52d..bb1c627f 100644 --- a/src/info/guardianproject/database/AbstractCursor.java +++ b/src/info/guardianproject/database/AbstractCursor.java @@ -22,11 +22,6 @@ import android.content.ContentResolver; import android.database.CharArrayBuffer; -import android.database.ContentObservable; -import android.database.ContentObserver; -import android.database.CursorWindow; -import android.database.DataSetObservable; -import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; import android.util.Config; @@ -37,7 +32,7 @@ * This is an abstract cursor class that handles a lot of the common code * that all cursors need to deal with and is provided for convenience reasons. */ -public abstract class AbstractCursor implements android.database.CrossProcessCursor { +public abstract class AbstractCursor implements CrossProcessCursor { private static final String TAG = "Cursor"; DataSetObservable mDataSetObservable = new DataSetObservable(); @@ -204,14 +199,11 @@ public final boolean moveToPosition(int position) { * Copy data from cursor to CursorWindow * @param position start position of data * @param window - */ - public void fillWindow(int position, android.database.CursorWindow window) { - - - if (position < 0 || position > getCount()) { + */ + public void fillWindow(int position, CursorWindow window) { + if (position < 0 || position > getCount()) { return; } - window.acquireReference(); try { int oldpos = mPos; @@ -483,7 +475,6 @@ protected DataSetObservable getDataSetObservable() { return mDataSetObservable; } - public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); @@ -617,8 +608,6 @@ public void onChange(boolean selfChange) { cursor.onChange(false); } } - - } /** diff --git a/src/info/guardianproject/database/AbstractWindowedCursor.java b/src/info/guardianproject/database/AbstractWindowedCursor.java index a104582f..5b654434 100644 --- a/src/info/guardianproject/database/AbstractWindowedCursor.java +++ b/src/info/guardianproject/database/AbstractWindowedCursor.java @@ -17,7 +17,6 @@ package info.guardianproject.database; import android.database.CharArrayBuffer; -import android.database.CursorWindow; /** * A base class for Cursors that store their data in {@link CursorWindow}s. diff --git a/src/info/guardianproject/database/BulkCursorNative.java b/src/info/guardianproject/database/BulkCursorNative.java index d5e4bb86..250495a9 100644 --- a/src/info/guardianproject/database/BulkCursorNative.java +++ b/src/info/guardianproject/database/BulkCursorNative.java @@ -16,15 +16,14 @@ package info.guardianproject.database; -import java.util.HashMap; -import java.util.Map; - -import android.database.CursorWindow; import android.os.Binder; -import android.os.Bundle; +import android.os.RemoteException; import android.os.IBinder; import android.os.Parcel; -import android.os.RemoteException; +import android.os.Bundle; + +import java.util.HashMap; +import java.util.Map; /** * Native implementation of the bulk cursor. This is only for use in implementing diff --git a/src/info/guardianproject/database/BulkCursorToCursorAdaptor.java b/src/info/guardianproject/database/BulkCursorToCursorAdaptor.java index d602ed4b..ba104e0b 100644 --- a/src/info/guardianproject/database/BulkCursorToCursorAdaptor.java +++ b/src/info/guardianproject/database/BulkCursorToCursorAdaptor.java @@ -18,12 +18,13 @@ import android.database.CharArrayBuffer; import android.database.ContentObserver; -import android.database.CursorWindow; import android.database.DataSetObserver; -import android.os.Bundle; import android.os.RemoteException; +import android.os.Bundle; import android.util.Log; +import java.util.Map; + /** * Adapts an {@link IBulkCursor} to a {@link Cursor} for use in the local * process. @@ -88,7 +89,7 @@ public synchronized IContentObserver getObserver() { if (mObserverBridge == null) { mObserverBridge = new SelfContentObserver(this); } - return null;//mObserverBridge.getContentObserver(); + return mObserverBridge.getContentObserver(); } @Override @@ -180,7 +181,6 @@ public boolean requery() { * @hide * @deprecated */ - /* @Override public boolean deleteRow() { try { @@ -207,7 +207,7 @@ public boolean deleteRow() { Log.e(TAG, "Unable to delete row because the remote process is dead"); return false; } - }*/ + } @Override public String[] getColumnNames() { @@ -225,7 +225,7 @@ public String[] getColumnNames() { /** * @hide * @deprecated - *//* + */ @Override public boolean commitUpdates(Map> additionalValues) { @@ -258,7 +258,7 @@ public boolean commitUpdates(Map { + + @Override + public void registerObserver(ContentObserver observer) { + super.registerObserver(observer); + } + + /** + * invokes dispatchUpdate on each observer, unless the observer doesn't want + * self-notifications and the update is from a self-notification + * @param selfChange + */ + public void dispatchChange(boolean selfChange) { + synchronized(mObservers) { + for (ContentObserver observer : mObservers) { + if (!selfChange || observer.deliverSelfNotifications()) { + observer.dispatchChange(selfChange); + } + } + } + } + + /** + * invokes onChange on each observer + * @param selfChange + */ + public void notifyChange(boolean selfChange) { + synchronized(mObservers) { + for (ContentObserver observer : mObservers) { + observer.onChange(selfChange); + } + } + } +} diff --git a/src/info/guardianproject/database/ContentObserver.java b/src/info/guardianproject/database/ContentObserver.java new file mode 100644 index 00000000..f3c64cf1 --- /dev/null +++ b/src/info/guardianproject/database/ContentObserver.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package info.guardianproject.database; + +import android.os.Handler; + +/** + * Receives call backs for changes to content. Must be implemented by objects which are added + * to a {@link ContentObservable}. + */ +public abstract class ContentObserver extends android.database.ContentObserver { + + private Transport mTransport; + + // Protects mTransport + private Object lock = new Object(); + + /* package */ Handler mHandler; + + private final class NotificationRunnable implements Runnable { + + private boolean mSelf; + + public NotificationRunnable(boolean self) { + mSelf = self; + } + + public void run() { + ContentObserver.this.onChange(mSelf); + } + } + + private static final class Transport extends IContentObserver.Stub { + ContentObserver mContentObserver; + + public Transport(ContentObserver contentObserver) { + mContentObserver = contentObserver; + } + + public boolean deliverSelfNotifications() { + ContentObserver contentObserver = mContentObserver; + if (contentObserver != null) { + return contentObserver.deliverSelfNotifications(); + } + return false; + } + + public void onChange(boolean selfChange) { + ContentObserver contentObserver = mContentObserver; + if (contentObserver != null) { + contentObserver.dispatchChange(selfChange); + } + } + + public void releaseContentObserver() { + mContentObserver = null; + } + } + + /** + * onChange() will happen on the provider Handler. + * + * @param handler The handler to run {@link #onChange} on. + */ + public ContentObserver(Handler handler) { + + super(handler); + mHandler = handler; + } + + /** + * Gets access to the binder transport object. Not for public consumption. + * + * {@hide} + */ + public IContentObserver getContentObserver() { + synchronized(lock) { + if (mTransport == null) { + mTransport = new Transport(this); + } + return mTransport; + } + } + + /** + * Gets access to the binder transport object, and unlinks the transport object + * from the ContentObserver. Not for public consumption. + * + * {@hide} + */ + public IContentObserver releaseContentObserver() { + synchronized(lock) { + Transport oldTransport = mTransport; + if (oldTransport != null) { + oldTransport.releaseContentObserver(); + mTransport = null; + } + return oldTransport; + } + } + + /** + * Returns true if this observer is interested in notifications for changes + * made through the cursor the observer is registered with. + */ + public boolean deliverSelfNotifications() { + return false; + } + + /** + * This method is called when a change occurs to the cursor that + * is being observed. + * + * @param selfChange true if the update was caused by a call to commit on the + * cursor that is being observed. + */ + public void onChange(boolean selfChange) {} + +} diff --git a/src/info/guardianproject/database/CrossProcessCursor.java b/src/info/guardianproject/database/CrossProcessCursor.java new file mode 100644 index 00000000..901c8f73 --- /dev/null +++ b/src/info/guardianproject/database/CrossProcessCursor.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package info.guardianproject.database; + +public interface CrossProcessCursor extends Cursor{ + /** + * returns a pre-filled window, return NULL if no such window + */ + CursorWindow getWindow(); + + /** + * copies cursor data into the window start at pos + */ + void fillWindow(int pos, CursorWindow winow); + + /** + * This function is called every time the cursor is successfully scrolled + * to a new position, giving the subclass a chance to update any state it + * may have. If it returns false the move function will also do so and the + * cursor will scroll to the beforeFirst position. + * + * @param oldPosition the position that we're moving from + * @param newPosition the position that we're moving to + * @return true if the move is successful, false otherwise + */ + boolean onMove(int oldPosition, int newPosition); + +} diff --git a/src/info/guardianproject/database/CrossProcessCursorWrapper.java b/src/info/guardianproject/database/CrossProcessCursorWrapper.java new file mode 100644 index 00000000..2c090416 --- /dev/null +++ b/src/info/guardianproject/database/CrossProcessCursorWrapper.java @@ -0,0 +1,58 @@ +package info.guardianproject.database; + +import android.database.Cursor; +import android.database.CrossProcessCursor; +import android.database.CursorWindow; +import android.database.CursorWrapper; + +public class CrossProcessCursorWrapper extends CursorWrapper implements CrossProcessCursor { + + public CrossProcessCursorWrapper(Cursor cursor) { + super(cursor); + } + + @Override + public CursorWindow getWindow() { + return null; + } + + @Override + public void fillWindow(int position, CursorWindow window) { + if (position < 0 || position > getCount()) { + return; + } + window.acquireReference(); + try { + moveToPosition(position - 1); + window.clear(); + window.setStartPosition(position); + int columnNum = getColumnCount(); + window.setNumColumns(columnNum); + while (moveToNext() && window.allocRow()) { + for (int i = 0; i < columnNum; i++) { + String field = getString(i); + if (field != null) { + if (!window.putString(field, getPosition(), i)) { + window.freeLastRow(); + break; + } + } else { + if (!window.putNull(getPosition(), i)) { + window.freeLastRow(); + break; + } + } + } + } + } catch (IllegalStateException e) { + // simply ignore it + } finally { + window.releaseReference(); + } + } + + @Override + public boolean onMove(int oldPosition, int newPosition) { + return true; + } +} diff --git a/src/info/guardianproject/database/Cursor.java b/src/info/guardianproject/database/Cursor.java index 83087e4c..c14695eb 100644 --- a/src/info/guardianproject/database/Cursor.java +++ b/src/info/guardianproject/database/Cursor.java @@ -20,8 +20,6 @@ import android.content.ContentResolver; import android.database.CharArrayBuffer; -import android.database.ContentObserver; -import android.database.DataSetObserver; import android.net.Uri; import android.os.Bundle; diff --git a/src/info/guardianproject/database/CursorToBulkCursorAdaptor.java b/src/info/guardianproject/database/CursorToBulkCursorAdaptor.java index abeaa709..b9a9712c 100644 --- a/src/info/guardianproject/database/CursorToBulkCursorAdaptor.java +++ b/src/info/guardianproject/database/CursorToBulkCursorAdaptor.java @@ -16,10 +16,7 @@ package info.guardianproject.database; -import java.util.Map; - -import android.database.ContentObserver; -import android.database.CursorWindow; +import info.guardianproject.database.*; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -27,6 +24,8 @@ import android.util.Config; import android.util.Log; +import java.util.Map; + /** * Wraps a BulkCursor around an existing Cursor making it remotable. @@ -36,7 +35,7 @@ public final class CursorToBulkCursorAdaptor extends BulkCursorNative implements IBinder.DeathRecipient { private static final String TAG = "Cursor"; - private final android.database.CrossProcessCursor mCursor; + private final CrossProcessCursor mCursor; private CursorWindow mWindow; private final String mProviderName; private final boolean mReadOnly; @@ -76,12 +75,12 @@ public void onChange(boolean selfChange) { } } - public CursorToBulkCursorAdaptor(android.database.Cursor cursor, IContentObserver observer, String providerName, + public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName, boolean allowWrite, CursorWindow window) { try { - mCursor = (android.database.CrossProcessCursor) cursor; + mCursor = (CrossProcessCursor) cursor; if (mCursor instanceof AbstractWindowedCursor) { - android.database.AbstractWindowedCursor windowedCursor = (android.database.AbstractWindowedCursor) cursor; + AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor; if (windowedCursor.hasWindow()) { if (Log.isLoggable(TAG, Log.VERBOSE) || Config.LOGV) { Log.v(TAG, "Cross process cursor has a local window before setWindow in " @@ -206,7 +205,7 @@ public boolean updateRows(Map> val + ", uid=" + Binder.getCallingUid()); return false; } - return updateRows(values); + return mCursor.commitUpdates(values); } public boolean deleteRow(int position) { @@ -220,7 +219,7 @@ public boolean deleteRow(int position) { if (mCursor.moveToPosition(position) == false) { return false; } - return deleteRow(position); + return mCursor.deleteRow(); } public Bundle getExtras() { diff --git a/src/info/guardianproject/database/CursorWindow.java b/src/info/guardianproject/database/CursorWindow.java new file mode 100644 index 00000000..b9d3d032 --- /dev/null +++ b/src/info/guardianproject/database/CursorWindow.java @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package info.guardianproject.database; + +import info.guardianproject.database.sqlcipher.SQLiteClosable; +import android.database.CharArrayBuffer; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A buffer containing multiple cursor rows. + */ +public class CursorWindow extends android.database.CursorWindow implements Parcelable { + /** The pointer to the native window class */ + @SuppressWarnings("unused") + private int nWindow; + + private int mStartPos; + + /** + * Creates a new empty window. + * + * @param localWindow true if this window will be used in this process only + */ + public CursorWindow(boolean localWindow) { + super(localWindow); + mStartPos = 0; + native_init(localWindow); + } + + /** + * Returns the starting position of this window within the entire + * Cursor's result set. + * + * @return the starting position of this window within the entire + * Cursor's result set. + */ + public int getStartPosition() { + return mStartPos; + } + + /** + * Set the start position of cursor window + * @param pos + */ + public void setStartPosition(int pos) { + mStartPos = pos; + } + + /** + * Returns the number of rows in this window. + * + * @return the number of rows in this window. + */ + public int getNumRows() { + acquireReference(); + try { + return getNumRows_native(); + } finally { + releaseReference(); + } + } + + private native int getNumRows_native(); + /** + * Set number of Columns + * @param columnNum + * @return true if success + */ + public boolean setNumColumns(int columnNum) { + acquireReference(); + try { + return setNumColumns_native(columnNum); + } finally { + releaseReference(); + } + } + + private native boolean setNumColumns_native(int columnNum); + + /** + * Allocate a row in cursor window + * @return false if cursor window is out of memory + */ + public boolean allocRow(){ + acquireReference(); + try { + return allocRow_native(); + } finally { + releaseReference(); + } + } + + private native boolean allocRow_native(); + + /** + * Free the last row + */ + public void freeLastRow(){ + acquireReference(); + try { + freeLastRow_native(); + } finally { + releaseReference(); + } + } + + private native void freeLastRow_native(); + + /** + * copy byte array to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy + */ + public boolean putBlob(byte[] value, int row, int col) { + acquireReference(); + try { + return putBlob_native(value, row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean putBlob_native(byte[] value, int row, int col); + + /** + * Copy String to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy + */ + public boolean putString(String value, int row, int col) { + acquireReference(); + try { + return putString_native(value, row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean putString_native(String value, int row, int col); + + /** + * Copy integer to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy + */ + public boolean putLong(long value, int row, int col) { + acquireReference(); + try { + return putLong_native(value, row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean putLong_native(long value, int row, int col); + + + /** + * Copy double to cursor window + * @param value + * @param row + * @param col + * @return false if fail to copy + */ + public boolean putDouble(double value, int row, int col) { + acquireReference(); + try { + return putDouble_native(value, row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean putDouble_native(double value, int row, int col); + + /** + * Set the [row, col] value to NULL + * @param row + * @param col + * @return false if fail to copy + */ + public boolean putNull(int row, int col) { + acquireReference(); + try { + return putNull_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean putNull_native(int row, int col); + + + /** + * Returns {@code true} if given field is {@code NULL}. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} + */ + public boolean isNull(int row, int col) { + acquireReference(); + try { + return isNull_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean isNull_native(int row, int col); + + /** + * Returns a byte array for the given field. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a String value for the given field + */ + public byte[] getBlob(int row, int col) { + acquireReference(); + try { + return getBlob_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native byte[] getBlob_native(int row, int col); + + /** + * Checks if a field contains either a blob or is null. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} or a blob + */ + public boolean isBlob(int row, int col) { + acquireReference(); + try { + return isBlob_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Checks if a field contains a long + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is a long + */ + public boolean isLong(int row, int col) { + acquireReference(); + try { + return isInteger_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Checks if a field contains a float. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is a float + */ + public boolean isFloat(int row, int col) { + acquireReference(); + try { + return isFloat_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Checks if a field contains either a String or is null. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return {@code true} if given field is {@code NULL} or a String + */ + public boolean isString(int row, int col) { + acquireReference(); + try { + return isString_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native boolean isBlob_native(int row, int col); + private native boolean isString_native(int row, int col); + private native boolean isInteger_native(int row, int col); + private native boolean isFloat_native(int row, int col); + + /** + * Returns a String for the given field. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a String value for the given field + */ + public String getString(int row, int col) { + acquireReference(); + try { + return getString_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native String getString_native(int row, int col); + + /** + * copy the text for the given field in the provided char array. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @param buffer the CharArrayBuffer to copy the text into, + * If the requested string is larger than the buffer + * a new char buffer will be created to hold the string. and assigne to + * CharArrayBuffer.data + */ + public void copyStringToBuffer(int row, int col, CharArrayBuffer buffer) { + if (buffer == null) { + throw new IllegalArgumentException("CharArrayBuffer should not be null"); + } + if (buffer.data == null) { + buffer.data = new char[64]; + } + acquireReference(); + try { + char[] newbuf = copyStringToBuffer_native( + row - mStartPos, col, buffer.data.length, buffer); + if (newbuf != null) { + buffer.data = newbuf; + } + } finally { + releaseReference(); + } + } + + private native char[] copyStringToBuffer_native( + int row, int col, int bufferSize, CharArrayBuffer buffer); + + /** + * Returns a long for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a long value for the given field + */ + public long getLong(int row, int col) { + acquireReference(); + try { + return getLong_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native long getLong_native(int row, int col); + + /** + * Returns a double for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a double value for the given field + */ + public double getDouble(int row, int col) { + acquireReference(); + try { + return getDouble_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + private native double getDouble_native(int row, int col); + + /** + * Returns a short for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a short value for the given field + */ + public short getShort(int row, int col) { + acquireReference(); + try { + return (short) getLong_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Returns an int for the given field. + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return an int value for the given field + */ + public int getInt(int row, int col) { + acquireReference(); + try { + return (int) getLong_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Returns a float for the given field. + * row is 0 based + * + * @param row the row to read from, row - getStartPosition() being the actual row in the window + * @param col the column to read from + * @return a float value for the given field + */ + public float getFloat(int row, int col) { + acquireReference(); + try { + return (float) getDouble_native(row - mStartPos, col); + } finally { + releaseReference(); + } + } + + /** + * Clears out the existing contents of the window, making it safe to reuse + * for new data. Note that the number of columns in the window may NOT + * change across a call to clear(). + */ + public void clear() { + acquireReference(); + try { + mStartPos = 0; + native_clear(); + } finally { + releaseReference(); + } + } + + /** Clears out the native side of things */ + private native void native_clear(); + + /** + * Cleans up the native resources associated with the window. + */ + public void close() { + releaseReference(); + } + + private native void close_native(); + + @Override + protected void finalize() { + // Just in case someone forgot to call close... + close_native(); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public CursorWindow createFromParcel(Parcel source) { + return new CursorWindow(source,0); + } + + public CursorWindow[] newArray(int size) { + return new CursorWindow[size]; + } + }; + + public static CursorWindow newFromParcel(Parcel p) { + return CREATOR.createFromParcel(p); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongBinder(native_getBinder()); + dest.writeInt(mStartPos); + } + + public CursorWindow(Parcel source,int foo) { + + super(true); + + IBinder nativeBinder = source.readStrongBinder(); + mStartPos = source.readInt(); + + native_init(nativeBinder); + } + + /** Get the binder for the native side of the window */ + private native IBinder native_getBinder(); + + /** Does the native side initialization for an empty window */ + private native void native_init(boolean localOnly); + + /** Does the native side initialization with an existing binder from another process */ + private native void native_init(IBinder nativeBinder); + + @Override + protected void onAllReferencesReleased() { + close_native(); + } +} diff --git a/src/info/guardianproject/database/CursorWrapper.java b/src/info/guardianproject/database/CursorWrapper.java index 18206501..2f1eaa9d 100644 --- a/src/info/guardianproject/database/CursorWrapper.java +++ b/src/info/guardianproject/database/CursorWrapper.java @@ -16,14 +16,13 @@ package info.guardianproject.database; -import java.util.Map; - import android.content.ContentResolver; -import android.database.ContentObserver; -import android.database.DataSetObserver; +import android.database.CharArrayBuffer; import android.net.Uri; import android.os.Bundle; +import java.util.Map; + /** * Wrapper class for Cursor that delegates all calls to the actual cursor object */ @@ -197,6 +196,14 @@ public boolean moveToPrevious() { return mCursor.moveToPrevious(); } + public void registerContentObserver(ContentObserver observer) { + mCursor.registerContentObserver(observer); + } + + public void registerDataSetObserver(DataSetObserver observer) { + mCursor.registerDataSetObserver(observer); + } + public boolean requery() { return mCursor.requery(); } @@ -217,6 +224,14 @@ public boolean supportsUpdates() { return mCursor.supportsUpdates(); } + public void unregisterContentObserver(ContentObserver observer) { + mCursor.unregisterContentObserver(observer); + } + + public void unregisterDataSetObserver(DataSetObserver observer) { + mCursor.unregisterDataSetObserver(observer); + } + /** * @hide * @deprecated diff --git a/src/info/guardianproject/database/DataSetObservable.java b/src/info/guardianproject/database/DataSetObservable.java new file mode 100644 index 00000000..c025735d --- /dev/null +++ b/src/info/guardianproject/database/DataSetObservable.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package info.guardianproject.database; + +/** + * A specialization of Observable for DataSetObserver that provides methods for + * invoking the various callback methods of DataSetObserver. + */ +public class DataSetObservable extends Observable { + /** + * Invokes onChanged on each observer. Called when the data set being observed has + * changed, and which when read contains the new state of the data. + */ + public void notifyChanged() { + synchronized(mObservers) { + for (DataSetObserver observer : mObservers) { + observer.onChanged(); + } + } + } + + /** + * Invokes onInvalidated on each observer. Called when the data set being monitored + * has changed such that it is no longer valid. + */ + public void notifyInvalidated() { + synchronized (mObservers) { + for (DataSetObserver observer : mObservers) { + observer.onInvalidated(); + } + } + } +} diff --git a/src/info/guardianproject/database/DataSetObserver.java b/src/info/guardianproject/database/DataSetObserver.java new file mode 100644 index 00000000..53204cfe --- /dev/null +++ b/src/info/guardianproject/database/DataSetObserver.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package info.guardianproject.database; + +/** + * Receives call backs when a data set has been changed, or made invalid. The typically data sets + * that are observed are {@link Cursor}s or {@link android.widget.Adapter}s. + * DataSetObserver must be implemented by objects which are added to a DataSetObservable. + */ +public abstract class DataSetObserver { + /** + * This method is called when the entire data set has changed, + * most likely through a call to {@link Cursor#requery()} on a {@link Cursor}. + */ + public void onChanged() { + // Do nothing + } + + /** + * This method is called when the entire data becomes invalid, + * most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a + * {@link Cursor}. + */ + public void onInvalidated() { + // Do nothing + } +} diff --git a/src/info/guardianproject/database/IBulkCursor.java b/src/info/guardianproject/database/IBulkCursor.java index 863442a9..8b3ef2fb 100644 --- a/src/info/guardianproject/database/IBulkCursor.java +++ b/src/info/guardianproject/database/IBulkCursor.java @@ -16,13 +16,12 @@ package info.guardianproject.database; -import java.util.Map; - -import android.database.CursorWindow; -import android.os.Bundle; +import android.os.RemoteException; import android.os.IBinder; import android.os.IInterface; -import android.os.RemoteException; +import android.os.Bundle; + +import java.util.Map; /** * This interface provides a low-level way to pass bulk cursor data across diff --git a/src/info/guardianproject/database/MatrixCursor.java b/src/info/guardianproject/database/MatrixCursor.java index df0fda85..c8a42593 100644 --- a/src/info/guardianproject/database/MatrixCursor.java +++ b/src/info/guardianproject/database/MatrixCursor.java @@ -18,8 +18,7 @@ import java.util.ArrayList; -import android.database.ContentObserver; -import android.database.DataSetObserver; +import android.database.CharArrayBuffer; /** @@ -281,7 +280,29 @@ public boolean isNull(int column) { } - + @Override + public void registerContentObserver(ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterContentObserver(ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + // TODO Auto-generated method stub + + } @Override public void copyStringToBuffer(int columnIndex, @@ -290,5 +311,31 @@ public void copyStringToBuffer(int columnIndex, } - + @Override + public void registerContentObserver( + android.database.ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void registerDataSetObserver( + android.database.DataSetObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterContentObserver( + android.database.ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterDataSetObserver( + android.database.DataSetObserver observer) { + // TODO Auto-generated method stub + + } } diff --git a/src/info/guardianproject/database/MergeCursor.java b/src/info/guardianproject/database/MergeCursor.java index 408f3edb..39fe45d4 100644 --- a/src/info/guardianproject/database/MergeCursor.java +++ b/src/info/guardianproject/database/MergeCursor.java @@ -16,8 +16,7 @@ package info.guardianproject.database; -import android.database.ContentObserver; -import android.database.DataSetObserver; +import android.database.CharArrayBuffer; @@ -263,7 +262,40 @@ public boolean requery() private Cursor[] mCursors; - + @Override + public void registerContentObserver( + android.database.ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void registerDataSetObserver( + android.database.DataSetObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterContentObserver( + android.database.ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterDataSetObserver( + android.database.DataSetObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void copyStringToBuffer(int columnIndex, + android.database.CharArrayBuffer buffer) { + // TODO Auto-generated method stub + + } diff --git a/src/info/guardianproject/database/Observable.java b/src/info/guardianproject/database/Observable.java index d8c48323..0a11db97 100644 --- a/src/info/guardianproject/database/Observable.java +++ b/src/info/guardianproject/database/Observable.java @@ -21,7 +21,7 @@ /** * Provides methods for (un)registering arbitrary observers in an ArrayList. */ -public abstract class Observable extends android.database.Observable { +public abstract class Observable { /** * The list of observers. An observer can be in the list at most * once and will never be null. diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java b/src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java index ff3262ee..710984f6 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteCompiledSql.java @@ -88,7 +88,6 @@ private void compile(String sql, boolean forceCompilation) { try { // Note that the native_compile() takes care of destroying any previously // existing programs before it compiles. - //TODO implement native compile native_compile(sql); } finally { mDatabase.unlock(); diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteCursor.java b/src/info/guardianproject/database/sqlcipher/SQLiteCursor.java index 308261e3..f9da9743 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteCursor.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteCursor.java @@ -15,23 +15,10 @@ */ package info.guardianproject.database.sqlcipher; -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ import info.guardianproject.database.AbstractWindowedCursor; +import info.guardianproject.database.CursorWindow; +import info.guardianproject.database.DataSetObserver; import info.guardianproject.database.SQLException; import java.util.HashMap; @@ -39,8 +26,9 @@ import java.util.Map; import java.util.concurrent.locks.ReentrantLock; -import android.database.CursorWindow; -import android.database.DataSetObserver; +import android.database.CharArrayBuffer; +import android.database.ContentObserver; +import android.database.CrossProcessCursor; import android.os.Handler; import android.os.Message; import android.os.Process; @@ -51,8 +39,11 @@ /** * A Cursor implementation that exposes results from a query on a * {@link SQLiteDatabase}. + * + * SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple + * threads should perform its own synchronization when using the SQLiteCursor. */ -public class SQLiteCursor extends AbstractWindowedCursor { +public class SQLiteCursor extends AbstractWindowedCursor implements CrossProcessCursor { static final String TAG = "Cursor"; static final int NO_COUNT = -1; @@ -146,7 +137,7 @@ public void run() { if (mCursorState != mThreadState) { mLock.unlock(); break; - }; + } try { int count = mQuery.fillWindow(cw, mMaxRead, mCount); // return -1 means not finished @@ -172,12 +163,15 @@ public void run() { } } + /** * @hide */ protected class MainThreadNotificationHandler extends Handler { public void handleMessage(Message msg) { - notifyDataSetChange(); + + notifyDataSetChange(); + } } @@ -194,7 +188,7 @@ public void registerDataSetObserver(DataSetObserver observer) { try { mNotificationHandler = new MainThreadNotificationHandler(); if (mPendingData) { - notifyDataSetChange(); + notifyDataSetChange(); mPendingData = false; } } finally { @@ -336,7 +330,8 @@ public int getColumnIndex(String columnName) { /** * @hide * @deprecated - */ + */ + // @Override public boolean deleteRow() { checkPosition(); @@ -395,16 +390,17 @@ public String[] getColumnNames() { * @hide * @deprecated */ - @Override + // @Override public boolean supportsUpdates() { - return super.supportsUpdates() && !TextUtils.isEmpty(mEditTable); + // return super.supportsUpdates() && !TextUtils.isEmpty(mEditTable); + return !TextUtils.isEmpty(mEditTable); } /** * @hide * @deprecated */ - @Override + // @Override public boolean commitUpdates(Map> additionalValues) { if (!supportsUpdates()) { @@ -610,5 +606,53 @@ protected void finalize() { super.finalize(); } } -} + @Override + public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { + + + } + + @Override + public void registerContentObserver(ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void registerDataSetObserver( + android.database.DataSetObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterContentObserver(ContentObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void unregisterDataSetObserver( + android.database.DataSetObserver observer) { + // TODO Auto-generated method stub + + } + + @Override + public void fillWindow(int startPos, android.database.CursorWindow window) { + + + window.setStartPosition(startPos); + mCount = mQuery.fillWindow((info.guardianproject.database.CursorWindow)window, mInitialRead, 0); + // return -1 means not finished + if (mCount == NO_COUNT){ + mCount = startPos + mInitialRead; + Thread t = new Thread(new QueryThread(mCursorState), "query thread"); + t.start(); + } + + } + + +} diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java b/src/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java index 7c27d931..c2a8fb65 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteCursorDriver.java @@ -43,7 +43,7 @@ public interface SQLiteCursorDriver { * * @return The new count value. */ - void cursorRequeried(android.database.Cursor cursor); + void cursorRequeried(Cursor cursor); /** * Called by a SQLiteCursor when it it closed to destroy this object as well. diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteDatabase.java b/src/info/guardianproject/database/sqlcipher/SQLiteDatabase.java index 68b9a77a..0e125b59 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteDatabase.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteDatabase.java @@ -16,6 +16,7 @@ package info.guardianproject.database.sqlcipher; +import info.guardianproject.database.CrossProcessCursorWrapper; import info.guardianproject.database.DatabaseUtils; import info.guardianproject.database.SQLException; import info.guardianproject.database.sqlcipher.SQLiteDebug.DbStats; @@ -73,58 +74,46 @@ public class SQLiteDatabase extends SQLiteClosable { private static final int EVENT_DB_OPERATION = 52000; private static final int EVENT_DB_CORRUPT = 75004; + private static void loadICUData(Context context) { + + try { + File applicationFilesDirectory = context.getFilesDir(); + File icuDir = new File(applicationFilesDirectory, "icu"); + if(!icuDir.exists()) icuDir.mkdirs(); + File icuDataFile = new File(icuDir, "icudt44l.dat"); + if(!icuDataFile.exists()) { + InputStream in = context.getAssets().open("icudt44l.mp3"); + OutputStream out = new FileOutputStream(icuDataFile); + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + out.flush(); + out.close(); + } + } + catch (Exception e) { + Log.e(TAG, "Error copying icu data file" + e.getMessage()); + } + } + public static void loadLibs (Context context) { - - File baseFile = context.getFilesDir(); - - File libFile = new File(baseFile.getParent(),"lib"); - - - System.loadLibrary("stlport_shared"); - - System.loadLibrary("sqlcipher"); - - String soFileName = "libsqlcipher_android"; - - - File soFile = new File(baseFile,soFileName + ".so"); - - boolean soLoaded = soFile.exists(); - - if (!soLoaded) - { - libFile = new File(baseFile.getParent(),"lib"); - File soSrcFile = new File(libFile,soFileName + "-" + android.os.Build.VERSION.SDK_INT + ".so"); - - try - { - InputStream in = new FileInputStream(soSrcFile); - - soFile.getParentFile().mkdirs(); - OutputStream out = new FileOutputStream(soFile); - - // Transfer bytes from in to out - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - in.close(); - out.close(); - } - catch (IOException ioe) - { - - } - - } - - System.load(soFile.getAbsolutePath()); - - System.loadLibrary("database_sqlcipher"); + System.loadLibrary("stlport_shared"); + System.loadLibrary("sqlcipher_android"); + System.loadLibrary("database_sqlcipher"); + + File applicationFilesDirectory = context.getFilesDir(); + String icuRootPath = android.os.Build.VERSION.SDK_INT < 9 ? applicationFilesDirectory.getAbsolutePath() + : "/system/usr"; + setICURoot(icuRootPath); + if(android.os.Build.VERSION.SDK_INT < 9){ + loadICUData(context); + } } - + /** * Algorithms used in ON CONFLICT clause * http://www.sqlite.org/lang_conflict.html @@ -182,7 +171,7 @@ public static void loadLibs (Context context) */ public static final int CONFLICT_NONE = 0; private static final String[] CONFLICT_VALUES = new String[] - {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "}; + {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "}; /** * Maximum Length Of A LIKE Or GLOB Pattern @@ -405,7 +394,7 @@ public void setLockingEnabled(boolean lockingEnabled) { /* package */ void onCorruption() { Log.e(TAG, "Removing corrupt database: " + mPath); - // EventLog.writeEvent(EVENT_DB_CORRUPT, mPath); + // EventLog.writeEvent(EVENT_DB_CORRUPT, mPath); try { // Close the database (if we can), which will cause subsequent operations to fail. close(); @@ -492,18 +481,18 @@ private void checkLockHoldTime() { long elapsedTime = SystemClock.elapsedRealtime(); long lockedTime = elapsedTime - mLockAcquiredWallTime; if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT && - !Log.isLoggable(TAG, Log.VERBOSE) && - (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) { + !Log.isLoggable(TAG, Log.VERBOSE) && + (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) { return; } if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) { int threadTime = (int) - ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000); + ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000); if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS || - lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) { + lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) { mLastLockMessageTime = elapsedTime; String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was " - + threadTime + "ms"; + + threadTime + "ms"; if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) { Log.d(TAG, msg, new Exception()); } else { @@ -567,7 +556,7 @@ public void beginTransactionWithListener(SQLiteTransactionListener transactionLi if (mLock.getHoldCount() > 1) { if (mInnerTransactionIsSuccessful) { String msg = "Cannot call beginTransaction between " - + "calling setTransactionSuccessful and endTransaction"; + + "calling setTransactionSuccessful and endTransaction"; IllegalStateException e = new IllegalStateException(msg); Log.e(TAG, "beginTransaction() failed", e); throw e; @@ -644,7 +633,7 @@ public void endTransaction() { } catch (SQLException e) { if (Config.LOGD) { Log.d(TAG, "exception during rollback, maybe the DB previously " - + "performed an auto-rollback"); + + "performed an auto-rollback"); } } } @@ -653,7 +642,7 @@ public void endTransaction() { unlockForced(); if (Config.LOGV) { Log.v(TAG, "unlocked " + Thread.currentThread() - + ", holdCount is " + mLock.getHoldCount()); + + ", holdCount is " + mLock.getHoldCount()); } } } @@ -676,7 +665,7 @@ public void setTransactionSuccessful() { } if (mInnerTransactionIsSuccessful) { throw new IllegalStateException( - "setTransactionSuccessful may only be called once per call to beginTransaction"); + "setTransactionSuccessful may only be called once per call to beginTransaction"); } mInnerTransactionIsSuccessful = true; } @@ -720,7 +709,7 @@ public boolean isDbLockedByOtherThreads() { @Deprecated public boolean yieldIfContended() { return yieldIfContendedHelper(false /* do not check yielding */, - -1 /* sleepAfterYieldDelay */); + -1 /* sleepAfterYieldDelay */); } /** @@ -764,7 +753,7 @@ private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfte if (checkFullyYielded) { if (this.isDbLockedByCurrentThread()) { throw new IllegalStateException( - "Db locked more than once. yielfIfContended cannot yield"); + "Db locked more than once. yielfIfContended cannot yield"); } } if (sleepAfterYieldDelay > 0) { @@ -775,7 +764,7 @@ private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfte while (remainingDelay > 0) { try { Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ? - remainingDelay : SLEEP_AFTER_YIELD_QUANTUM); + remainingDelay : SLEEP_AFTER_YIELD_QUANTUM); } catch (InterruptedException e) { Thread.interrupted(); } @@ -792,7 +781,7 @@ private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfte /** Maps table names to info about what to which _sync_time column to set * to NULL on an update. This is used to support syncing. */ private final Map mSyncUpdateInfo = - new HashMap(); + new HashMap(); public Map getSyncedTables() { synchronized(mSyncUpdateInfo) { @@ -822,7 +811,7 @@ static private class SyncUpdateInfo { * @param foreignKey The key that refers to the primary key in table */ SyncUpdateInfo(String masterTable, String deletedTable, - String foreignKey) { + String foreignKey) { this.masterTable = masterTable; this.deletedTable = deletedTable; this.foreignKey = foreignKey; @@ -849,8 +838,8 @@ public interface CursorFactory { * String, SQLiteQuery)}. */ public Cursor newCursor(SQLiteDatabase db, - SQLiteCursorDriver masterQuery, String editTable, - SQLiteQuery query); + SQLiteCursorDriver masterQuery, String editTable, + SQLiteQuery query); } /** @@ -882,7 +871,7 @@ public static SQLiteDatabase openDatabase(String path, String password, CursorFa // Try to recover from this, if we can. // TODO: should we do this for other open failures? Log.e(TAG, "Deleting and re-creating corrupt database " + path, e); - // EventLog.writeEvent(EVENT_DB_CORRUPT, path); + // EventLog.writeEvent(EVENT_DB_CORRUPT, path); if (!path.equalsIgnoreCase(":memory")) { // delete is only for non-memory database files new File(path).delete(); @@ -890,7 +879,7 @@ public static SQLiteDatabase openDatabase(String path, String password, CursorFa sqliteDatabase = new SQLiteDatabase(path, password, factory, flags); } ActiveDatabases.getInstance().mActiveDatabases.add( - new WeakReference(sqliteDatabase)); + new WeakReference(sqliteDatabase)); return sqliteDatabase; } @@ -1009,7 +998,7 @@ public long getMaximumSize() { } try { prog = new SQLiteStatement(this, - "PRAGMA max_page_count;"); + "PRAGMA max_page_count;"); long pageCount = prog.simpleQueryForLong(); return pageCount * getPageSize(); } finally { @@ -1039,7 +1028,7 @@ public long setMaximumSize(long numBytes) { numPages++; } prog = new SQLiteStatement(this, - "PRAGMA max_page_count = " + numPages); + "PRAGMA max_page_count = " + numPages); long newPageCount = prog.simpleQueryForLong(); return newPageCount * pageSize; } finally { @@ -1061,7 +1050,7 @@ public long getPageSize() { } try { prog = new SQLiteStatement(this, - "PRAGMA page_size;"); + "PRAGMA page_size;"); long size = prog.simpleQueryForLong(); return size; } finally { @@ -1105,7 +1094,7 @@ public void markTableSyncable(String table, String deletedTable) { * @param updateTable this is the table that will have its _sync_dirty */ public void markTableSyncable(String table, String foreignKey, - String updateTable) { + String updateTable) { markTableSyncable(table, foreignKey, updateTable, null); } @@ -1123,19 +1112,19 @@ public void markTableSyncable(String table, String foreignKey, * updateTable */ private void markTableSyncable(String table, String foreignKey, - String updateTable, String deletedTable) { + String updateTable, String deletedTable) { lock(); try { native_execSQL("SELECT _sync_dirty FROM " + updateTable - + " LIMIT 0"); + + " LIMIT 0"); native_execSQL("SELECT " + foreignKey + " FROM " + table - + " LIMIT 0"); + + " LIMIT 0"); } finally { unlock(); } SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable, - foreignKey); + foreignKey); synchronized (mSyncUpdateInfo) { mSyncUpdateInfo.put(table, info); } @@ -1238,10 +1227,10 @@ public SQLiteStatement compileStatement(String sql) throws SQLException { * @see Cursor */ public Cursor query(boolean distinct, String table, String[] columns, - String selection, String[] selectionArgs, String groupBy, - String having, String orderBy, String limit) { + String selection, String[] selectionArgs, String groupBy, + String having, String orderBy, String limit) { return queryWithFactory(null, distinct, table, columns, selection, selectionArgs, - groupBy, having, orderBy, limit); + groupBy, having, orderBy, limit); } /** @@ -1277,17 +1266,17 @@ public Cursor query(boolean distinct, String table, String[] columns, * @see Cursor */ public Cursor queryWithFactory(CursorFactory cursorFactory, - boolean distinct, String table, String[] columns, - String selection, String[] selectionArgs, String groupBy, - String having, String orderBy, String limit) { + boolean distinct, String table, String[] columns, + String selection, String[] selectionArgs, String groupBy, + String having, String orderBy, String limit) { if (!isOpen()) { throw new IllegalStateException("database not open"); } String sql = SQLiteQueryBuilder.buildQueryString( - distinct, table, columns, selection, groupBy, having, orderBy, limit); + distinct, table, columns, selection, groupBy, having, orderBy, limit); return rawQueryWithFactory( - cursorFactory, sql, selectionArgs, findEditTable(table)); + cursorFactory, sql, selectionArgs, findEditTable(table)); } /** @@ -1319,11 +1308,11 @@ public Cursor queryWithFactory(CursorFactory cursorFactory, * @see Cursor */ public Cursor query(String table, String[] columns, String selection, - String[] selectionArgs, String groupBy, String having, - String orderBy) { + String[] selectionArgs, String groupBy, String having, + String orderBy) { return query(false, table, columns, selection, selectionArgs, groupBy, - having, orderBy, null /* limit */); + having, orderBy, null /* limit */); } /** @@ -1357,11 +1346,11 @@ public Cursor query(String table, String[] columns, String selection, * @see Cursor */ public Cursor query(String table, String[] columns, String selection, - String[] selectionArgs, String groupBy, String having, - String orderBy, String limit) { + String[] selectionArgs, String groupBy, String having, + String orderBy, String limit) { return query(false, table, columns, selection, selectionArgs, groupBy, - having, orderBy, limit); + having, orderBy, limit); } /** @@ -1391,8 +1380,8 @@ public Cursor rawQuery(String sql, String[] selectionArgs) { * {@link Cursor}s are not synchronized, see the documentation for more details. */ public Cursor rawQueryWithFactory( - CursorFactory cursorFactory, String sql, String[] selectionArgs, - String editTable) { + CursorFactory cursorFactory, String sql, String[] selectionArgs, + String editTable) { if (!isOpen()) { throw new IllegalStateException("database not open"); } @@ -1406,10 +1395,9 @@ public Cursor rawQueryWithFactory( Cursor cursor = null; try { - cursor = driver.query( - cursorFactory != null ? cursorFactory : mFactory, - selectionArgs); + cursorFactory != null ? cursorFactory : mFactory, + selectionArgs); } finally { if (Config.LOGV || mSlowQueryThreshold != -1) { @@ -1424,13 +1412,13 @@ public Cursor rawQueryWithFactory( if (Config.LOGV || duration >= mSlowQueryThreshold) { Log.v(SQLiteCursor.TAG, "query (" + duration + " ms): " + driver.toString() + ", args are " - + (selectionArgs != null - ? TextUtils.join(",", selectionArgs) - : "") + ", count is " + count); + + (selectionArgs != null + ? TextUtils.join(",", selectionArgs) + : "") + ", count is " + count); } } } - return cursor; + return new CrossProcessCursorWrapper(cursor); } /** @@ -1452,9 +1440,9 @@ public Cursor rawQueryWithFactory( * @hide */ public Cursor rawQuery(String sql, String[] selectionArgs, - int initialRead, int maxRead) { + int initialRead, int maxRead) { SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory( - null, sql, selectionArgs, null); + null, sql, selectionArgs, null); c.setLoadStyle(initialRead, maxRead); return c; } @@ -1494,7 +1482,7 @@ public long insert(String table, String nullColumnHack, ContentValues values) { * @return the row ID of the newly inserted row, or -1 if an error occurred */ public long insertOrThrow(String table, String nullColumnHack, ContentValues values) - throws SQLException { + throws SQLException { return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE); } @@ -1512,7 +1500,7 @@ public long insertOrThrow(String table, String nullColumnHack, ContentValues val public long replace(String table, String nullColumnHack, ContentValues initialValues) { try { return insertWithOnConflict(table, nullColumnHack, initialValues, - CONFLICT_REPLACE); + CONFLICT_REPLACE); } catch (SQLException e) { Log.e(TAG, "Error inserting " + initialValues, e); return -1; @@ -1532,9 +1520,9 @@ public long replace(String table, String nullColumnHack, ContentValues initialVa * @return the row ID of the newly inserted row, or -1 if an error occurred */ public long replaceOrThrow(String table, String nullColumnHack, - ContentValues initialValues) throws SQLException { + ContentValues initialValues) throws SQLException { return insertWithOnConflict(table, nullColumnHack, initialValues, - CONFLICT_REPLACE); + CONFLICT_REPLACE); } /** @@ -1554,7 +1542,7 @@ public long replaceOrThrow(String table, String nullColumnHack, * OR -1 if any error */ public long insertWithOnConflict(String table, String nullColumnHack, - ContentValues initialValues, int conflictAlgorithm) { + ContentValues initialValues, int conflictAlgorithm) { if (!isOpen()) { throw new IllegalStateException("database not open"); } @@ -1608,7 +1596,7 @@ public long insertWithOnConflict(String table, String nullColumnHack, for (int i = 0; i < size; i++) { Map.Entry entry = entriesIter.next(); DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue()); - + } } @@ -1621,7 +1609,7 @@ public long insertWithOnConflict(String table, String nullColumnHack, } else { if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Inserting row " + insertedRowId + " from " - + initialValues + " using " + sql); + + initialValues + " using " + sql); } } return insertedRowId; @@ -1654,8 +1642,8 @@ public int delete(String table, String whereClause, String[] whereArgs) { SQLiteStatement statement = null; try { statement = compileStatement("DELETE FROM " + table - + (!TextUtils.isEmpty(whereClause) - ? " WHERE " + whereClause : "")); + + (!TextUtils.isEmpty(whereClause) + ? " WHERE " + whereClause : "")); if (whereArgs != null) { int numArgs = whereArgs.length; for (int i = 0; i < numArgs; i++) { @@ -1701,7 +1689,7 @@ public int update(String table, ContentValues values, String whereClause, String * @return the number of rows affected */ public int updateWithOnConflict(String table, ContentValues values, - String whereClause, String[] whereArgs, int conflictAlgorithm) { + String whereClause, String[] whereArgs, int conflictAlgorithm) { if (values == null || values.size() == 0) { throw new IllegalArgumentException("Empty values"); } @@ -1853,7 +1841,7 @@ public void execSQL(String sql, Object[] bindArgs) throws SQLException { protected void finalize() { if (isOpen()) { Log.e(TAG, "close() was never explicitly called on database '" + - mPath + "' ", mStackTrace); + mPath + "' ", mStackTrace); closeClosable(); onAllReferencesReleased(); } @@ -1868,8 +1856,8 @@ protected void finalize() { * exists, mFlags will be updated appropriately. */ public SQLiteDatabase(String path, String password, CursorFactory factory, int flags) { - - + + if (path == null) { throw new IllegalArgumentException("path should not be null"); } @@ -1879,9 +1867,9 @@ public SQLiteDatabase(String path, String password, CursorFactory factory, int f mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace(); mFactory = factory; dbopen(mPath, mFlags); - + execSQL("PRAGMA key = '" + password + "'"); - + if (SQLiteDebug.DEBUG_SQL_CACHE) { mTimeOpened = getTime(); } @@ -1982,13 +1970,13 @@ public final String getPath() { if (blockingPackage == null) blockingPackage = ""; /* - EventLog.writeEvent( - EVENT_DB_OPERATION, - getPathForLogs(), - sql, - durationMillis, - blockingPackage, - samplePercent);*/ + EventLog.writeEvent( + EVENT_DB_OPERATION, + getPathForLogs(), + sql, + durationMillis, + blockingPackage, + samplePercent);*/ } /** @@ -2068,9 +2056,9 @@ public void setLocale(Locale locale) { */ if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) { Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " + - getPath() + "; i.e., NO space for this sql statement in cache: " + - sql + ". Please change your sql statements to use '?' for " + - "bindargs, instead of using actual values"); + getPath() + "; i.e., NO space for this sql statement in cache: " + + sql + ". Please change your sql statements to use '?' for " + + "bindargs, instead of using actual values"); } // don't add this entry to cache } else { @@ -2078,7 +2066,7 @@ public void setLocale(Locale locale) { mCompiledQueries.put(sql, compiledStatement); if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" + - mCompiledQueries.size() + "|" + sql); + mCompiledQueries.size() + "|" + sql); } } } @@ -2120,9 +2108,9 @@ private void deallocCachedSqlStatements() { if (SQLiteDebug.DEBUG_SQL_CACHE) { Log.v(TAG, "|cache_stats|" + - getPath() + "|" + mCompiledQueries.size() + - "|" + mNumCacheHits + "|" + mNumCacheMisses + - "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql); + getPath() + "|" + mCompiledQueries.size() + + "|" + mNumCacheHits + "|" + mNumCacheMisses + + "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql); } return compiledStatement; } @@ -2185,7 +2173,7 @@ public synchronized void setMaxSqlCacheSize(int cacheSize) { throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE); } else if (cacheSize < mMaxSqlCacheSize) { throw new IllegalStateException("cannot set cacheSize to a value less than the value " + - "set with previous setMaxSqlCacheSize() call."); + "set with previous setMaxSqlCacheSize() call."); } mMaxSqlCacheSize = cacheSize; } @@ -2200,7 +2188,7 @@ private ActiveDatabases() {} // disable instantiation of this class /** * this method is used to collect data about ALL open databases in the current process. - * bugreport is a user of this data. + * bugreport is a user of this data. */ /* package */ static ArrayList getDbStats() { ArrayList dbStatsList = new ArrayList(); @@ -2243,7 +2231,7 @@ private ActiveDatabases() {} // disable instantiation of this class } if (pageCount > 0) { dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(), - lookasideUsed)); + lookasideUsed)); } } } @@ -2282,12 +2270,17 @@ private static ArrayList> getAttachedDbs(SQLiteDatabase dbO ArrayList> attachedDbs = new ArrayList>(); Cursor c = dbObj.rawQuery("pragma database_list;", null); while (c.moveToNext()) { - attachedDbs.add(new Pair(c.getString(1), c.getString(2))); + attachedDbs.add(new Pair(c.getString(1), c.getString(2))); } c.close(); return attachedDbs; } + /** + * Sets the root directory to search for the ICU data file + */ + public static native void setICURoot(String path); + /** * Native call to open the database. * diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java b/src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java index 2d991155..a7af18cf 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteDirectCursorDriver.java @@ -79,7 +79,7 @@ public void cursorDeactivated() { // Do nothing } - public void cursorRequeried(android.database.Cursor cursor) { + public void cursorRequeried(Cursor cursor) { // Do nothing } diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java b/src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java index 42fbf0c6..b883358d 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteOpenHelper.java @@ -106,6 +106,9 @@ public synchronized SQLiteDatabase getWritableDatabase(String password) { db = SQLiteDatabase.openOrCreateDatabase(path, password, mFactory); + // db = SQLiteDatabase.openDatabase(path,mFactory , SQLiteDatabase.OPEN_READWRITE); + + //db = mContext.openOrCreateDatabase(mName, 0, mFactory); } diff --git a/src/info/guardianproject/database/sqlcipher/SQLiteQuery.java b/src/info/guardianproject/database/sqlcipher/SQLiteQuery.java index 43a047a5..f29f7ee7 100644 --- a/src/info/guardianproject/database/sqlcipher/SQLiteQuery.java +++ b/src/info/guardianproject/database/sqlcipher/SQLiteQuery.java @@ -15,7 +15,8 @@ */ package info.guardianproject.database.sqlcipher; -import android.database.CursorWindow; +import info.guardianproject.database.*; + import android.os.SystemClock; import android.util.Log; @@ -57,8 +58,7 @@ public class SQLiteQuery extends SQLiteProgram { * @param window The window to fill into * @return number of total rows in the query */ - /* package */ - int fillWindow(CursorWindow window, + /* package */ int fillWindow(CursorWindow window, int maxRead, int lastPos) { long timeStart = SystemClock.uptimeMillis(); mDatabase.lock(); @@ -185,11 +185,9 @@ public void bindString(int index, String value) { if (!mClosed) super.bindString(index, value); } - private final native int native_fill_window(CursorWindow window, int startPos, int offsetParam, int maxRead, int lastPos); - private final native int native_column_count(); private final native String native_column_name(int columnIndex); diff --git a/src/package.html b/src/package.html new file mode 100644 index 00000000..1f76d9f1 --- /dev/null +++ b/src/package.html @@ -0,0 +1,14 @@ + + +Contains classes to explore data returned through a content provider. +

+If you need to manage data in a private database, use the {@link +android.database.sqlite} classes. These classes are used to manage the {@link +android.database.Cursor} object returned from a content provider query. Databases +are usually created and opened with {@link android.content.Context#openOrCreateDatabase} +To make requests through +content providers, you can use the {@link android.content.ContentResolver +content.ContentResolver} class. +

All databases are stored on the device in /data/data/<package_name>/databases + +