ペペロン頭脳

ソフトウェアエンジニアのメモ的なアレ。

NDKなSQLiteで"unable to open database file"

最近はお仕事でAndroidさんとお付き合いしているんですけど、これがまたコア機能をNDKゴリッゴリで書いてあるプロジェクトをフォークしてゴニョる感じでして。
もともとAndroidなんてHello WorldレベルだったのにいきなりNDKかよ先達も周りにいねーよ、という状況で泥沼の一月半ほどを過ごしてまいりました。
ネイティブビルドをCygwinなバッチで走らせていたのを全部Eclipseでできるようにしたり、よっしゃーと思ってndk-debugしてみたらエラーエラーで調べたところ外部のAndroid.mkインクルードさせてるとndk-gdbが辿れないと知って愕然としたり。

そういうのと並行して、AndroidSDK標準のJavaSQLiteラッパーでなくてNDKなCライブラリを使うコードのテストでハマっていたのが解決したのでここに記す。

現象

sqlite3.c/h をラップしているお客様謹製ライブラリをAndroid実機テストしていたのだが、一部”unable to open database file”のエラーメッセージが返ってくるクエリがあった。

  • CREATE DATABASE/TABLE, INSERT, JOINしないSELECT: 正常に実行される
  • JOINありのSELECT: エラー!
  • VACUUM: エラー!

構成と設定

DBファイルは「内部ストレージ」にディレクトリを切って置いている。ディレクトリもファイルも読み取り/書き込みOKな権限設定。
さらにAndroidManifest.xmlにもpermission.WRITE_EXTERNAL_STORAGEを指定済み。

解決法

どうやらjournal等の一時ファイルを作れなくてエラー吐いてるようだ・・。パーミッションの設定は適切なはずなのに。
ということでググりまくったところ、StackOverflow大先生が教えてくれました。
java - Why fopen gets fail in native method due to permission issue from Android-NDK application? - Stack Overflow

#include <unistd.h>

// ファイル作成する処理の前にこの一行を追加するだけ。1回でよい。
// リンク先では"/sdcard/"指定してるけど、機種によって違うかも?
chdir("/path/to/内部ストレージのルート");

詳しく調べてないんだけど、どうやらネイティブプロセス起動時は"/"がカレントになっている。
そこから”/sdcard/”下にファイルをつくろうとすると、permission.WRITE_EXTERNAL_STORAGEを設定していても不正な操作になってしまうようだ。
そこでカレントを”/sdcard/”に移してやることで解決・・・ということらしい。


こういうマイナーな問題について調べるのほんと辛い。必要だし仕方ないんだけど、原因のあたりを付けるまで時間かかるし、解決しても達成感がまるでない。場当たり的な知識ばかり増えていく気がするけど、これも蓄えていくしかないのかな・・
これ以外にもADTバージョンアップしたらぶっ壊れたり、うっかりJDK1.7使ってたらEclipseプラグインうまく入らなかったり、Android.mkの書き換えやら構成変更でつまずいたり。
コード書く以外の調査やら試行錯誤に大幅に時間と体力を奪われてて、ここのところ陰鬱な気分で仕事してたけど明日からはゴリゴリ実装していけそう。

Android NDK ネイティブプログラミング

Android NDK ネイティブプログラミング