SQLCipherの16KBページサイズ対応忘備録

# SQLCipherとは

- SQLCipherはSQLiteの暗号化を提供するライブラリ
- 通常のSQLiteのAPIをそのまま使用して使えるのでセキュアなアプリを作る際によく利用される

# Android15の16KBページサイズとは

- デバイスのメモリ管理単位が従来の4KBから16KBに拡大された
- より大規模なメモリ操作を効率的に行えるようになり、特にメモリを大量に消費するアプリのパフォーマンス向上が見込める

https://developer.android.com/guide/practices/page-sizes

# SQLCipherの対応要否

- 16KBページサイズに対応したネイティブバイナリ(.a, .so)か確認する

https://mvnrepository.com/artifact/net.zetetic/sqlcipher-android/4.6.0

- MavenCentralからaarファイルをダウンロードし、格納されている.soファイルを確認する

```shell-session
# aarのダウンロード
$ curl -L https://repo1.maven.org/maven2/net/zetetic/sqlcipher-android/4.6.0/sqlcipher-android-4.6.0.aar -o sqlcipher-android-4.6.0.aar
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 9537k  100 9537k    0     0  6857k      0  0:00:01  0:00:01 --:--:-- 6856k

# aarの解凍
$ unzip sqlcipher-android-4.6.0.aar -d sqlcipher-android-4.6.0
Archive:  sqlcipher-android-4.6.0.aar
  inflating: sqlcipher-android-4.6.0/R.txt  
  inflating: sqlcipher-android-4.6.0/AndroidManifest.xml  
  inflating: sqlcipher-android-4.6.0/classes.jar  
  inflating: sqlcipher-android-4.6.0/proguard.txt  
   creating: sqlcipher-android-4.6.0/res/
   creating: sqlcipher-android-4.6.0/res/values/
  inflating: sqlcipher-android-4.6.0/res/values/values.xml  
  inflating: sqlcipher-android-4.6.0/META-INF/com/android/build/gradle/aar-metadata.properties  
   creating: sqlcipher-android-4.6.0/jni/
   creating: sqlcipher-android-4.6.0/jni/arm64-v8a/
  inflating: sqlcipher-android-4.6.0/jni/arm64-v8a/libsqlcipher.so  
   creating: sqlcipher-android-4.6.0/jni/armeabi-v7a/
  inflating: sqlcipher-android-4.6.0/jni/armeabi-v7a/libsqlcipher.so  
   creating: sqlcipher-android-4.6.0/jni/x86/
  inflating: sqlcipher-android-4.6.0/jni/x86/libsqlcipher.so  
   creating: sqlcipher-android-4.6.0/jni/x86_64/
  inflating: sqlcipher-android-4.6.0/jni/x86_64/libsqlcipher.so

# .soが2**14(16KB)にアラインされているか確認
$ ~/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p arm64-v8a/libsqlcipher.so | grep LOAD
    LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12
    LOAD off    0x0000000000523410 vaddr 0x0000000000524410 paddr 0x0000000000524410 align 2**12
    LOAD off    0x000000000057f218 vaddr 0x0000000000581218 paddr 0x0000000000581218 align 2**12

$ ~/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p armeabi-v7a/libsqlcipher.so | grep LOAD
    LOAD off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**12
    LOAD off    0x003a48e0 vaddr 0x003a58e0 paddr 0x003a58e0 align 2**12
    LOAD off    0x003d4f40 vaddr 0x003d6f40 paddr 0x003d6f40 align 2**12

$ ~/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p x86/libsqlcipher.so | grep LOAD
    LOAD off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**12
    LOAD off    0x004effc0 vaddr 0x004f0fc0 paddr 0x004f0fc0 align 2**12
    LOAD off    0x005214cc vaddr 0x005234cc paddr 0x005234cc align 2**12

$ ~/Library/Android/sdk/ndk/27.1.12297006/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p x86_64/libsqlcipher.so | grep LOAD
    LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12
    LOAD off    0x00000000005ac960 vaddr 0x00000000005ad960 paddr 0x00000000005ad960 align 2**12
    LOAD off    0x000000000060b660 vaddr 0x000000000060d660 paddr 0x000000000060d660 align 2**12

# apkをビルドしてチェック
$ sh check_elf_alignment.sh app/build/outputs/apk/release/app-release.apk

Recursively analyzing app/build/outputs/apk/release/app-release.apk

=== APK zip-alignment ===
 1405124 lib/arm64-v8a/libsqlcipher.so (OK - compressed)
 8376834 lib/x86_64/libsqlcipher.so (OK - compressed)
Verification succesful
=========================

=== ELF alignment ===
-e /var/folders/XXX/T/app-release_out_XXXXX.HU6CvFjaoM/lib/armeabi-v7a/libsqlcipher.so: UNALIGNED (2**12)
-e /var/folders/XXX/T/app-release_out_XXXXX.HU6CvFjaoM/lib/x86/libsqlcipher.so: UNALIGNED (2**12)
-e /var/folders/XXX/T/app-release_out_XXXXX.HU6CvFjaoM/lib/arm64-v8a/libsqlcipher.so: UNALIGNED (2**12)
-e /var/folders/XXX/T/app-release_out_XXXXX.HU6CvFjaoM/lib/x86_64/libsqlcipher.so: UNALIGNED (2**12)
-e \e[31mFound 4 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).
=====================
```

<details><summary>check_elf_alignment.sh</summary><div>

```sh:check_elf_alignment.sh
#!/bin/bash
progname="${0##*/}"
progname="${progname%.sh}"

# usage: check_elf_alignment.sh [path to *.so files|path to *.apk]

cleanup_trap() {
  if [ -n "${tmp}" -a -d "${tmp}" ]; then
    rm -rf ${tmp}
  fi
  exit $1
}

usage() {
  echo "Host side script to check the ELF alignment of shared libraries."
  echo "Shared libraries are reported ALIGNED when their ELF regions are"
  echo "16 KB or 64 KB aligned. Otherwise they are reported as UNALIGNED."
  echo
  echo "Usage: ${progname} [input-path|input-APK|input-APEX]"
}

if [ ${#} -ne 1 ]; then
  usage
  exit
fi

case ${1} in
  --help | -h | -\?)
    usage
    exit
    ;;

  *)
    dir="${1}"
    ;;
esac

if ! [ -f "${dir}" -o -d "${dir}" ]; then
  echo "Invalid file: ${dir}" >&2
  exit 1
fi

if [[ "${dir}" == *.apk ]]; then
  trap 'cleanup_trap' EXIT

  echo
  echo "Recursively analyzing $dir"
  echo

  if { zipalign --help 2>&1 | grep -q "\-P <pagesize_kb>"; }; then
    echo "=== APK zip-alignment ==="
    zipalign -v -c -P 16 4 "${dir}" | egrep 'lib/arm64-v8a|lib/x86_64|Verification'
    echo "========================="
  else
    echo "NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher."
    echo "  You can install the latest build-tools by running the below command"
    echo "  and updating your \$PATH:"
    echo
    echo "    sdkmanager \"build-tools;35.0.0-rc3\""
  fi

  dir_filename=$(basename "${dir}")
  tmp=$(mktemp -d -t "${dir_filename%.apk}_out_XXXXX")
  unzip "${dir}" lib/* -d "${tmp}" >/dev/null 2>&1
  dir="${tmp}"
fi

if [[ "${dir}" == *.apex ]]; then
  trap 'cleanup_trap' EXIT

  echo
  echo "Recursively analyzing $dir"
  echo

  dir_filename=$(basename "${dir}")
  tmp=$(mktemp -d -t "${dir_filename%.apex}_out_XXXXX")
  deapexer extract "${dir}" "${tmp}" || { echo "Failed to deapex." && exit 1; }
  dir="${tmp}"
fi

RED="\e[31m"
GREEN="\e[32m"
ENDCOLOR="\e[0m"

unaligned_libs=()

echo
echo "=== ELF alignment ==="

matches="$(find "${dir}" -type f)"
IFS=$'\n'
for match in $matches; do
  # We could recursively call this script or rewrite it to though.
  [[ "${match}" == *".apk" ]] && echo "WARNING: doesn't recursively inspect .apk file: ${match}"
  [[ "${match}" == *".apex" ]] && echo "WARNING: doesn't recursively inspect .apex file: ${match}"

  [[ $(file "${match}") == *"ELF"* ]] || continue

  res="$(objdump -p "${match}" | grep LOAD | awk '{ print $NF }' | head -1)"
  if [[ $res =~ 2\*\*(1[4-9]|[2-9][0-9]|[1-9][0-9]{2,}) ]]; then
    echo -e "${match}: ${GREEN}ALIGNED${ENDCOLOR} ($res)"
  else
    echo -e "${match}: ${RED}UNALIGNED${ENDCOLOR} ($res)"
    unaligned_libs+=("${match}")
  fi
done

if [ ${#unaligned_libs[@]} -gt 0 ]; then
  echo -e "${RED}Found ${#unaligned_libs[@]} unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).${ENDCOLOR}"
elif [ -n "${dir_filename}" ]; then
  echo -e "ELF Verification Successful"
fi
echo "====================="
```
</div></details>


:::note info
確認に使用するコマンドの詳細は[Android Developers](https://developer.android.com/guide/practices/page-sizes#16-kb-impact)にまとめられている
:::

# 以下対応忘備録

## Git Clone

- SQLCipherのandroid向けartifactは2種類公開されている

| artifact | GitHub |
| --- | --- |
| **`net.zetetic:android-database-sqlcipher`** | [![android-database-sqlcipher](https://opengraph.githubassets.com/5a7679fae4248faec164ad5c8bb94a539c1a8270a1160a48b4c1ac24030923c0/sqlcipher/android-database-sqlcipher)](https://github.com/sqlcipher/android-database-sqlcipher) |
| **`net.zetetic:sqlcipher-android`** | [![sqlcipher-android](https://opengraph.githubassets.com/5a7679fae4248faec164ad5c8bb94a539c1a8270a1160a48b4c1ac24030923c0/sqlcipher/sqlcipher-android)](https://github.com/sqlcipher/sqlcipher-android) |

- `net.zetetic:sqlcipher-android`は後継であり、これからアプリにSQLCipherを組み込む場合は基本的にこちらを使う
- 今回の対応も`net.zetetic:sqlcipher-android`を使う

## SQLCipher for Androidのビルド準備

- SQLCipher for Androidのビルドには2つの外部プロジェクトのビルド生成物とOpenSSLのincludeディレクトリ配下のファイルが必要

1. OpenSSLを使ってビルドした`libcrypto.a`
2. OpenSSLの`include`ディレクトリ配下のファイル群(`crypto`, `internal`, `openssl`)
3. SQLCipher本体をビルドして作成した`sqlite3.c`, `sqlite3.h`

### OpenSSLのビルド

- [sqlcipher/android-database-sqlcipher](https://github.com/sqlcipher/android-database-sqlcipher)にそれっぽいスクリプトがあるので参考にする


https://github.com/sqlcipher/android-database-sqlcipher/blob/master/android-database-sqlcipher/build-openssl-libraries.sh

<details open><summary>build-openssl-libraries.sh</summary>

```bash:build-openssl-libraries.sh
#! /usr/bin/env bash

MINIMUM_ANDROID_SDK_VERSION=$1
MINIMUM_ANDROID_64_BIT_SDK_VERSION=$2
OPENSSL_DIR=$3
ANDROID_LIB_ROOT=$4

(cd ${OPENSSL_DIR};

 if [[ ! ${MINIMUM_ANDROID_SDK_VERSION} ]]; then
     echo "MINIMUM_ANDROID_SDK_VERSION was not provided, include and rerun"
     exit 1
 fi

 if [[ ! ${MINIMUM_ANDROID_64_BIT_SDK_VERSION} ]]; then
     echo "MINIMUM_ANDROID_64_BIT_SDK_VERSION was not provided, include and rerun"
     exit 1
 fi

 if [[ ! ${ANDROID_NDK_HOME} ]]; then
     echo "ANDROID_NDK_HOME environment variable not set, set and rerun"
     exit 1
 fi

 HOST_INFO=`uname -a`
 case ${HOST_INFO} in
     Darwin*)
         TOOLCHAIN_SYSTEM=darwin-x86_64
         ;;
     Linux*)
         if [[ "${HOST_INFO}" == *i686* ]]
         then
             TOOLCHAIN_SYSTEM=linux-x86
         else
             TOOLCHAIN_SYSTEM=linux-x86_64
         fi
         ;;
     *)
         echo "Toolchain unknown for host system"
         exit 1
         ;;
 esac

 NDK_TOOLCHAIN_VERSION=4.9
 OPENSSL_CONFIGURE_OPTIONS="-fPIC -fstack-protector-all no-idea no-camellia \
 no-seed no-bf no-cast no-rc2 no-rc4 no-rc5 no-md2 \
 no-md4 no-ecdh no-sock no-ssl3 \
 no-dsa no-dh no-ec no-ecdsa no-tls1 \
 no-rfc3779 no-whirlpool no-srp \
 no-mdc2 no-ecdh no-engine \
 no-srtp"

 rm -rf ${ANDROID_LIB_ROOT}

 for SQLCIPHER_TARGET_PLATFORM in armeabi-v7a x86 x86_64 arm64-v8a
 do
     echo "Building libcrypto.a for ${SQLCIPHER_TARGET_PLATFORM}"
     case "${SQLCIPHER_TARGET_PLATFORM}" in
         armeabi-v7a)
             CONFIGURE_ARCH="android-arm -march=armv7-a"
             ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION}
             OFFSET_BITS=32
             ;;
         x86)
             CONFIGURE_ARCH=android-x86
             ANDROID_API_VERSION=${MINIMUM_ANDROID_SDK_VERSION}
             OFFSET_BITS=32
             ;;
         x86_64)
             CONFIGURE_ARCH=android64-x86_64
             ANDROID_API_VERSION=${MINIMUM_ANDROID_64_BIT_SDK_VERSION}
             OFFSET_BITS=64
             ;;
         arm64-v8a)
             CONFIGURE_ARCH=android-arm64
             ANDROID_API_VERSION=${MINIMUM_ANDROID_64_BIT_SDK_VERSION}
             OFFSET_BITS=64
             ;;
         *)
             echo "Unsupported build platform:${SQLCIPHER_TARGET_PLATFORM}"
             exit 1
     esac
     TOOLCHAIN_BIN_PATH=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/${TOOLCHAIN_SYSTEM}/bin
     PATH=${TOOLCHAIN_BIN_PATH}:${PATH} \
     ./Configure ${CONFIGURE_ARCH} \
                 -D__ANDROID_API__=${ANDROID_API_VERSION} \
                 -D_FILE_OFFSET_BITS=${OFFSET_BITS} \
                 ${OPENSSL_CONFIGURE_OPTIONS}

     if [[ $? -ne 0 ]]; then
         echo "Error executing:./Configure ${CONFIGURE_ARCH} ${OPENSSL_CONFIGURE_OPTIONS}"
         exit 1
     fi

     make clean
     PATH=${TOOLCHAIN_BIN_PATH}:${PATH} \
         make build_libs

     if [[ $? -ne 0 ]]; then
         echo "Error executing make for platform:${SQLCIPHER_TARGET_PLATFORM}"
         exit 1
     fi
     mkdir -p ${ANDROID_LIB_ROOT}/${SQLCIPHER_TARGET_PLATFORM}
     mv libcrypto.a ${ANDROID_LIB_ROOT}/${SQLCIPHER_TARGET_PLATFORM}
 done
)
```

</details>

- OpenSSLのソースをダウンロードして、展開する

```shell-session
$ curl -L https://github.com/openssl/openssl/releases/download/openssl-3.4.0/openssl-3.4.0.tar.gz -o openssl-3.4.0.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 17.4M  100 17.4M    0     0  13.0M      0  0:00:01  0:00:01 --:--:-- 82.0M
$ tar -zxvf openssl-3.4.0.tar.gz
```

- OpenSSLのリポジトリにもandroid向けのビルドについて説明があるので参考にする

https://github.com/openssl/openssl/blob/master/NOTES-ANDROID.md


```md:NOTES-ANDROID.md
 Apart from `PATH` adjustment, you need to set `ANDROID_NDK_ROOT` environment
 to point at the `NDK` directory. If you're using a side-by-side NDK the path
 will look something like `/some/where/android-sdk/ndk/<ver>`, and for a
 standalone NDK the path will be something like `/some/where/android-ndk-<ver>`.
 Both variables are significant at both configuration and compilation times.
 The NDK customarily supports multiple Android API levels, e.g. `android-14`,
 `android-21`, etc. By default, latest API level is chosen. If you need to target
 an older platform pass the argument `-D__ANDROID_API__=N` to `Configure`,
 with `N` being the numerical value of the target platform version. For example,
 to compile for Android 10 arm64 with a side-by-side NDK r20.0.5594570

    export ANDROID_NDK_ROOT=/home/whoever/Android/android-sdk/ndk/20.0.5594570
    PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin:$PATH
    ./Configure android-arm64 -D__ANDROID_API__=29
    make
```

- `ANDROID_NDK_ROOT`なるものを設定し、NDKに含まれているLLVMの`clang`にPATHを通す

```shell-session
$ export ANDROID_NDK_ROOT=$ANDROID_HOME/ndk/27.2.12479018
$ PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/darwin-x86_64/bin:$PATH
```

- `./Configure`を実行する

```shell-session
$ ./Configure android-arm64 -D__ANDROID_API__=29
Configuring OpenSSL version 3.4.0 for target android-arm64
Using os-specific seed configuration
Created configdata.pm
Running configdata.pm
Created Makefile.in
Created Makefile

**********************************************************************
***                                                                ***
***   OpenSSL has been successfully configured                     ***
***                                                                ***
***   If you encounter a problem while building, please open an    ***
***   issue on GitHub <https://github.com/openssl/openssl/issues>  ***
***   and include the output from the following command:           ***
***                                                                ***
***       perl configdata.pm --dump                                ***
***                                                                ***
***   (If you are new to OpenSSL, you might want to consult the    ***
***   'Troubleshooting' section in the INSTALL.md file first)      ***
***                                                                ***
**********************************************************************
```

- 作成された`Makefile`を見てみる(36000行のMakefileが生成されたので抜粋)


<details><summary>Makefile</summary>

```make:Makefile
##
## Makefile for OpenSSL
##
## WARNING: do not edit!
## Generated by configdata.pm from Configurations/common0.tmpl, Configurations/unix-Makefile.tmpl
## via Makefile.in

PLATFORM=android-arm64
OPTIONS=-D__ANDROID_API__=29 no-acvp-tests no-afalgeng no-asan no-brotli no-brotli-dynamic no-buildtest-c++ no-crypto-mdebug no-crypto-mdebug-backtrace no-demos no-devcryptoeng no-ec_nistp_64_gcc_128 no-egd no-external-tests no-fips no-fips-post no-fips-securitychecks no-fuzz-afl no-fuzz-libfuzzer no-h3demo no-jitter no-ktls no-md2 no-msan no-pie no-rc5 no-sctp no-ssl3 no-ssl3-method no-tfo no-trace no-ubsan no-unit-test no-uplink no-weak-ssl-ciphers no-winstore no-zlib no-zlib-dynamic no-zstd no-zstd-dynamic
CONFIGURE_ARGS=("android-arm64", "-D__ANDROID_API__=29")
SRCDIR=.
BLDDIR=.
FIPSKEY=f4556650ac31d35461610bac4ed81b1a181b2d8a43ea2854cbae22ca74560813

VERSION=3.4.0
VERSION_NUMBER=3.4.0
MAJOR=3
MINOR=4
SHLIB_VERSION_NUMBER=3
SHLIB_TARGET=linux-shared

LIBS=apps/libapps.a libcrypto.a libssl.a providers/libcommon.a \
     providers/libdefault.a providers/liblegacy.a test/libtestutil.a
SHLIBS=libcrypto.so libssl.so
SHLIB_INFO="libcrypto.so;;" "libssl.so;;"
MODULES=engines/capi.so engines/dasync.so engines/loader_attic.so \
        engines/ossltest.so engines/padlock.so providers/legacy.so \
        test/p_minimal.so test/p_test.so
FIPSMODULE=
FIPSMODULENAME=
```

</details>


- Makefileを見ると以下のコマンドを実行すれば`libcrypto.a`が生成される

```shell-session
$ make build_modules
$ make libcrypto.a 
```

- `build-openssl-libraries.sh`を見る限り、他にもオプションをつけてビルドするようだが一旦このままplatform分の`libcrypto.a`を生成する

```shell-session
# SQLCipher4.6.0に合わせてAPI Level 21でビルドする
$ ./Configure android-arm64 -D__ANDROID_API__=21
$ make clean build_modules libcrypto.a
$ mkdir -p ./../arm64-v8a
$ mv libcrypto.a ./../arm64-v8a
$ ./Configure android-arm -D__ANDROID_API__=21
$ make clean build_modules libcrypto.a
$ mkdir -p ./../armeabi-v7a
$ mv libcrypto.a ./../armeabi-v7a
$ ./Configure android-x86_64 -D__ANDROID_API__=21
$ make clean build_modules libcrypto.a
$ mkdir -p ./../x86_64
$ mv libcrypto.a ./../x86_64
$ ./Configure android-x86 -D__ANDROID_API__=21
$ make clean build_modules libcrypto.a
$ mkdir -p ./../x86
$ mv libcrypto.a ./../x86
```


### SQLCipherのビルド

- [sqlcipher/android-database-sqlcipher](https://github.com/sqlcipher/android-database-sqlcipher)にそれっぽいスクリプトがあるので参考にする

https://github.com/sqlcipher/android-database-sqlcipher/blob/master/android-database-sqlcipher/native.gradle

<details open><summary>native.gradle</summary>

```groovy:native.gradle
task buildAmalgamation() {
  onlyIf {
    def amalgamation = new File("${sqlcipherDir}/sqlite3.c")
    return !amalgamation.exists()
  }
  doLast {
    exec {
      workingDir "${sqlcipherDir}"
      environment("CFLAGS", "${sqlcipherCFlags}")
      commandLine "./configure", "--enable-tempstore=yes", "--with-crypto-lib=none"
    }
    exec {
      workingDir "${sqlcipherDir}"
      environment("CFLAGS", "${sqlcipherCFlags}")
      commandLine "make", "sqlite3.c"
    }
  }
}

task copyAmalgamation() {
    doLast {
        exec {
            workingDir "${sqlcipherDir}"
            commandLine "cp", "sqlite3.c", "sqlite3.h", "${nativeRootOutputDir}/cpp/"
        }
    }
}
```

</details>

- 解読すると以下の通りになりそう

1. [sqlcipher/sqlcipher](https://github.com/sqlcipher/sqlcipher)をcloneする
2. `CFLAGS`を設定する
3. `./configure --enable-tempstore=yes --with-crypto-lib=none`を実行する
4. `make sqlite3.c`を実行する


https://github.com/sqlcipher/sqlcipher


- 設定する`CFLAGS`は[sqlcipher/sqlcipher-android](https://github.com/sqlcipher/sqlcipher-android/blob/master/sqlcipher/src/main/jni/sqlcipher/Android.mk)のAndroid.mkを参考にすればよい


https://github.com/sqlcipher/sqlcipher-android/blob/master/sqlcipher/src/main/jni/sqlcipher/Android.mk


<details><summary>sqlcipher/Android.mk</summary>

```make:sqlcipher/Android.mk
ifdef $SSQLCIPHER_CFLAGS
LOCAL_CFLAGS += ${SQLCIPHER_CFLAGS}
else
LOCAL_CFLAGS += -DSQLITE_HAS_CODEC -DSQLCIPHER_CRYPTO_OPENSSL -DSQLITE_TEMP_STORE=2 \
	-DSQLITE_THREADSAFE=1 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_FTS3_PARENTHESIS \
	-DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_UNICODE61 -DSQLITE_ENABLE_FTS5 \
	-DSQLITE_ENABLE_MEMORY_MANAGEMENT -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_ENABLE_RTREE \
	-DSQLITE_SOUNDEX -DHAVE_USLEEP -DSQLITE_ENABLE_LOAD_EXTENSION -DSQLITE_ENABLE_STAT3 \
	-DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
	-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_MAX_VARIABLE_NUMBER=99999 \
	-DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 -DSQLITE_ENABLE_SESSION \
	-DSQLITE_ENABLE_PREUPDATE_HOOK -DSQLITE_ENABLE_DBSTAT_VTAB
endif
```

</details>

- つまり👇みたいな形になる

```shell-session
$ export CFLAGS='-DSQLITE_HAS_CODEC -DSQLCIPHER_CRYPTO_OPENSSL -DSQLITE_TEMP_STORE=2 \
	-DSQLITE_THREADSAFE=1 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_FTS3_PARENTHESIS \
	-DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS4_UNICODE61 -DSQLITE_ENABLE_FTS5 \
	-DSQLITE_ENABLE_MEMORY_MANAGEMENT -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_ENABLE_RTREE \
	-DSQLITE_SOUNDEX -DHAVE_USLEEP -DSQLITE_ENABLE_LOAD_EXTENSION -DSQLITE_ENABLE_STAT3 \
	-DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
	-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_MAX_VARIABLE_NUMBER=99999 \
	-DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 -DSQLITE_ENABLE_SESSION \
	-DSQLITE_ENABLE_PREUPDATE_HOOK -DSQLITE_ENABLE_DBSTAT_VTAB'
$ ./configure --enable-tempstore=yes --with-crypto-lib=none
$ make sqlite3.c
```

### 生成したファイルを配置する

#### OpenSSL
- 生成したplatformごとの`libcrypto.a`を`sqlcipher-android/sqlcipher/src/main/jni/sqlcipher/android-libs`ディレクトリに配置する
- `include`ディレクトリ配下のファイル群を`sqlcipher-android/sqlcipher/src/main/jni/sqlcipher/android-libs/include`ディレクトリに配置する


#### SQLCipher
- 生成した`sqlite3.c`, `sqlite3.h`を`sqlcipher-android/sqlcipher/src/main/jni/sqlcipher`ディレクトリに配置する


## ビルド

- ここまでやれば`sqlcipher-android`をビルドするだけで良い

```shell-session
$ ./gradlew assemble
> Task :sqlcipher:buildNdkBuildDebug[arm64-v8a]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:buildNdkBuildDebug[armeabi-v7a]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:buildNdkBuildDebug[x86]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:buildNdkBuildDebug[x86_64]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:processDebugManifest
package="net.zetetic.database" found in source AndroidManifest.xml: ~/sqlcipher-android/sqlcipher/src/main/AndroidManifest.xml.
Setting the namespace via the package attribute in the source AndroidManifest.xml is no longer supported, and the value is ignored.
Recommendation: remove package="net.zetetic.database" from the source AndroidManifest.xml: ~/sqlcipher-android/sqlcipher/src/main/AndroidManifest.xml.

> Task :sqlcipher:compileDebugJavaWithJavac
ノート:一部の入力ファイルは推奨されないAPIを使用またはオーバーライドしています。
ノート:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。

> Task :sqlcipher:buildNdkBuildRelease[arm64-v8a]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:buildNdkBuildRelease[armeabi-v7a]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:buildNdkBuildRelease[x86]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:buildNdkBuildRelease[x86_64]
C/C++: fcntl(): Bad file descriptor

> Task :sqlcipher:compileReleaseJavaWithJavac
ノート:一部の入力ファイルは推奨されないAPIを使用またはオーバーライドしています。
ノート:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。

> Task :sqlcipher:processReleaseManifest
package="net.zetetic.database" found in source AndroidManifest.xml: ~/sqlcipher-android/sqlcipher/src/main/AndroidManifest.xml.
Setting the namespace via the package attribute in the source AndroidManifest.xml is no longer supported, and the value is ignored.
Recommendation: remove package="net.zetetic.database" from the source AndroidManifest.xml: ~/sqlcipher-android/sqlcipher/src/main/AndroidManifest.xml.

> Task :sqlcipher:compileReleaseNoX86JavaWithJavac
ノート:一部の入力ファイルは推奨されないAPIを使用またはオーバーライドしています。
ノート:詳細は、-Xlint:deprecationオプションを指定して再コンパイルしてください。

> Task :sqlcipher:processReleaseNoX86Manifest
package="net.zetetic.database" found in source AndroidManifest.xml: ~/sqlcipher-android/sqlcipher/src/main/AndroidManifest.xml.
Setting the namespace via the package attribute in the source AndroidManifest.xml is no longer supported, and the value is ignored.
Recommendation: remove package="net.zetetic.database" from the source AndroidManifest.xml: ~/sqlcipher-android/sqlcipher/src/main/AndroidManifest.xml.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.9/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 1m 51s
93 actionable tasks: 83 executed, 10 up-to-date
```

# 16KBページサイズ対応

- `Android.mk`を使用しているプロジェクトは以下の記述を追加してビルドし直す

```make:Android.mk
LOCAL_LDFLAGS += "-Wl,-z,max-page-size=16384"
APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true
```


# ここまでやってみたものの・・・・・・・

`net.zetetic:android-database-sqlcipher`の後継である`net.zetetic:sqlcipher-android`の最新バージョンでは16KBページサイズに対応済みだった・・・・😩<br>
のでSQLCipherを使う場合は大人しく`net.zetetic:sqlcipher-android`の最新を使えばよい

❌ net.zetetic:android-database-sqlcipher:X.X.X
❌ net.zetetic:sqlcipher-android:4.6.0
⭕️ net.zetetic:sqlcipher-android:**4.6.1**


https://github.com/sqlcipher/sqlcipher-android/commit/7f7e12b4f064d50e86092c149875767fbdf738f9


# まとめ

- 16KBページサイズ対応が必要か確認するときは[`check_elf_alignment.sh`](https://cs.android.com/android/platform/superproject/main/+/main:system/extras/tools/check_elf_alignment.sh)を使おう
- androidでSQLCipherを使いたい時は`net.zetetic:android-database-sqlcipher`ではなく`net.zetetic:sqlcipher-android`の4.6.1以上を使おう