こんにちは、makki_dです。 今回はKLabでも利用しているSQLCipherについて、自前ビルドすることでapkファイル内のライブラリサイズを50%(当社比)削減する方法を紹介したいと思います。

暗号化機能付きSQLite、SQLCipher

SQLiteはファイルベースの軽量なRDBで、クライアント(iPhone/Android)内にステージや敵の基本情報(マスタデータ)等を保持するのにとても便利です。 ですが、SQLiteのデータファイルを平文のまま保持していると、改ざんされたり非公開のデータが覗き見られたりしてしまうので、暗号化しておきたいものです。 そんな時には、SQLiteに暗号化機能を加えたSQLCipher(※)がとても手軽で、KLabでも活用しています。

この記事では、Unity製のAndroid向けアプリで、SQLCipherを依存ファイルを少なくシンプルに使うための方法を紹介します。

※ここで扱うSQLCipherは、オープンソースなCommunity Editionを利用しています。

SQLCipherの普通の使い方 (Android)

SQLCipherのサイトにはAndroidへの導入手順のページもありますし、 Community Editionのバイナリパッケージも用意されています。 Unity5以降であれば、aarファイルをAssets/Plugins/Android以下に入れることでライブラリ自体は簡単に導入できます。

しかしこのライブラリはJavaから利用することを前提とするものですので、Javaのクラスに依存していますし、Unityから使う時もJavaの世界を経由して呼び出すことになってしまいます。

さらに、3.4.0まではICU (International Compornents for Unicode) という文字コード変換ライブラリが組み込まれているため、 icudt46.zipという2.2MBのファイルがもれなく含まれていました。 GooglePlayのAPKファイルサイズ制限的にも苦しいので、依存をなくしてしまいたいところです。

ネイティブ共有ライブラリとして使う方法

さて、ここでUnityでSQLiteを使う時を思い出したいと思います。

C#には、DllImport属性を使ってネイティブ共有ライブラリの関数を直接呼び出すことができます。 (MSDN, Mono)

Unityでもこの機能を使って、Plugins以下に配置したlibsqlite3.soの関数を呼びだすことでSQLiteが使えます。 SQLCipherもネイティブ共有ライブラリとしてビルドすれば、同様にDllImportできるはずです。

ネイティブ共有ライブラリのビルド方法

実際にARMv7向けのネイティブ共有ライブラリをビルドしてみたいと思います。 ビルド環境は Ubuntu 14.04、Android NDK r12b を想定してます。

前準備として次のように環境変数を設定します。 コマンド中のパスやAPI Levelは適宜読み替えてください。

export ANDROID_NDK_ROOT=$HOME/Android/android-ndk-r12b
export SYSROOT=$ANDROID_NDK_ROOT/platforms/android-14/arch-arm
export TOOLCHAIN=$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin
export PATH=$TOOLCHAIN:$PATH

SQLiteの場合

ここでまたSQLiteを思い出したいと思います。 SQLiteのビルドは、amalgamationされた単一のcソース(sqlite3.c)からコンパイルするのが簡単です。 SQLiteのDownloadsページよりソースを入手した後、 Android NDKを用いて次のようにビルドします。

unzip sqlite-amalgamation-3140100.zip
cd sqlite-amalgamation-3140100
arm-linux-androideabi-gcc --sysroot=$SYSROOT -march=armv7-a -shared sqlite3.c -o sqlite3.so

sqlite3.cは公式サイトからも入手できますが、生のソースコードツリーから生成することもできます。 configureしたあと、make sqlite3.c とするだけです。

unzip sqlite-src-3140100.zip
cd sqlite-src-3140100
./configure
make sqlite3.c

SQLCipherの場合

さて、SQLCipherではどうでしょう。 configureオプションで頑張ってクロスコンパイルすることもできるかもしれませんが、 SQLiteと同じようにamalgamationされたsqlite3.cからコンパイルするのが簡単です。

SQLCipherではsqlite3.c単体では提供されていないので、自分で生成します。 ソースをcloneしてきたら、configureしてmake sqlite3.cです。sqliteと同じですね。

git clone https://github.com/sqlcipher/sqlcipher.git
cd sqlcipher
./configure
make sqlite3.c

続いてNDKでビルドするのですが、その前にOpenSSLも用意する必要があります。 SQLCipherは暗号化にOpenSSLのlibcryptoを利用しているのですが、Androidが持つlibcrypto.soは使えませんので静的リンクします。 (Android6以降のlibcrypto.soはOpenSSLではなくBoringSSLのもので、ドキュメントにもリンクしてはならないと書かれてます。)

同じNDKでlibcrypto.aをビルドします。 (実際には使っていない機能を組み込まないために、このあたりを参考にConfigureオプションを設定します)

git clone -b openssl_1_0_2-stable https://github.com/openssl/openssl.git
cd openssl
./Configure dist
./Configure --cross-compile-prefix=$TOOLCHAIN/arm-linux-androideabi- --sysroot=$SYSROOT android-armv7
make build_libcrypto 

libcrypto.aがビルドされました。いよいよ生成したsqlite3.cからsqlcipherをビルドします。

cd sqlcipher
arm-linux-androideabi-gcc --sysroot=$SYSROOT -march=armv7-a -shared -DSQLITE_HAS_CODEC -I../openssl/include sqlite3.c -o libsqlcipher.so ../openssl/libcrypto.a

できあがったlibsqlcipher.soの使い方

こうしてAndroid用のlibsqlcipher.soを作ることが出来ました。 これは普通のネイティブ共有ライブラリなので、DllImportで直接呼び出すことができます。 Javaやicudt46.zipへの依存もありません。 既存のSQLite向けの実装のDllImportだけ書き換えれば使えるはずです。

またaarファイルでSQLCipherを導入した場合、Javaとのブリッジのためのコードの他、ARMv5向けのバイナリもapkファイルに含まれてしまいます。 Unity本体がARMv5をサポートしていないので、ARMv7とx86のネイティブ共有ライブラリのみに絞ることでapkファイルのサイズを節約できます。 空プロジェクトにライブラリのみ追加して試してみたところ、apkファイルサイズは次のようになりました。

ライブラリ導入方法 apkファイルサイズ ライブラリサイズ(差分)
ライブラリ無し 17.6 MB --
aar ver 3.4.0 24.7 MB 7.1 MB
aar ver 3.5.3 20.0 MB 2.4 MB
自前ビルド 18.8 MB 1.2 MB

この記事ではARMv7のAndroid向けバイナリのビルド方法を掲載しましたが、x86のAndroidやWindows向けのクロスコンパイルも基本的には同じ方法でできるはずです。 ぜひ挑戦してみてください。