AndroidからDBファイルを取り出し、PCに持ってきて、「PupSQLite」や「DB Browser for SQLite」などのツールで内容を確認出来るようにします。
コマンドラインで自動取得できるようにしておくことで、CIサイクル中にDBファイルを取ってきてスキーマ自動作成などが出来る環境を整えておきます。
DBファイルの場所を確認
PCにAndroidをUSB接続し、認識しているか確認します。
C:\>adb devices
List of devices attached
FA7761800334 device
Androidのシェルに入ります。
C:\>adb shell
htc_ocnuhljapan:/ $
run-asで開発アプリのパッケージディレクトリに入ります。root化しない限りこのディレクトリから外へはアクセス出来ません。また、インストールしたユーザに入られてクラックされても困りますから、アプリがリリースビルドされている場合はこの操作は出来ません。
htc_ocnuhljapan:/ $ run-as jp.co.xyz
htc_ocnuhljapan:/data/data/jp.co.xyz $ pwd
/data/data/jp.co.xyz
パッケージディレクトリ内にdatabasesというディレクトリがあります。
htc_ocnuhljapan:/data/data/jp.co.xyz $ ls
app_database app_webview code_cache shared_prefs
app_textures cache databases
この中にアプリで作成したSQLiteのDBファイルが保存されています。
htc_ocnuhljapan:/data/data/jp.co.xyz $ cd databases
htc_ocnuhljapan:/data/data/jp.co.xyz/databases $ ls
awesomeapp.db ← SQLIteのDBファイル
このawesomeapp.dbをPCに持ってくる方法を考えます。
Android5からアクセス権限が厳しくなった
Android5(Lolipop)からAndroidOS内のパーミッションが厳しくなりました。それ以前は一旦外から直接アクセスできるディレクトリにDBファイルをコピーしておき、adb pullを使って取ってきていました。
set PACKAGE=jp.co.xyz.awsameapp
set TEMPFILE=/mnt/shell/emulated/0/Android/data/%PACKAGE%/files
set DBFILEE=awsameapp.db
adb -d shell "run-as %PACKAGE% cat databases/%DBFILE% > %TEMPFILE%/%DBFILE%"
adb pull %TEMPFILE%/%DBFILE%
自パッケージディレクトリ以外に完全にアクセス出来なくなった為、昨今のAndroidではこの手法が取れなくなってしまいました。
回避策
Androidのadbコマンド関連ソースを見ると、/system/core/adb/commandline.cppの中で、adbにはexec-outという公開されていないオプションが有り、out_to_file(int inFd, int outFd)という関数を呼んでいて、指定したファイルデスクプリタの内容をPCの標準出力に流してくれることが分かります。
// adbオプションでexec-outが指定されたか判定をしてる
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
(snip)
if (exec_in) {
copy_to_file(STDIN_FILENO, fd);
} else {
copy_to_file(fd, STDOUT_FILENO);
}
// 入力ファイルデスクリプタ内容をPCの標準出力にコピーしてる
static void copy_to_file(int inFd, int outFd) {
const size_t BUFSIZE = 32 * 1024;
char* buf = (char*) malloc(BUFSIZE);
if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
int len;
long total = 0;
int old_stdin_mode = -1;
int old_stdout_mode = -1;
D("copy_to_file(%d -> %d)", inFd, outFd);
stdinout_raw_prologue(inFd, outFd, old_stdin_mode, old_stdout_mode);
while (true) {
if (inFd == STDIN_FILENO) {
len = unix_read(inFd, buf, BUFSIZE);
} else {
len = adb_read(inFd, buf, BUFSIZE);
}
if (len == 0) {
D("copy_to_file() : read 0 bytes; exiting");
break;
}
if (len < 0) {
D("copy_to_file(): read failed: %s", strerror(errno));
break;
}
if (outFd == STDOUT_FILENO) {
fwrite(buf, 1, len, stdout);
fflush(stdout);
} else {
adb_write(outFd, buf, len);
}
total += len;
}
stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
D("copy_to_file() finished after %lu bytes", total);
free(buf);
}
この機能を利用すると端末内のdatabases/awsomeapp.dbファイルをcatし、ローカルPCのカレントディレクトリにリダイレクトできるようになります。
adb exec-out run-as jp.co.xyz cat databases/awesomeapp.db > ./awesomeapp.db
後はこれを直接使用するなり、Cordova系のハイブリッドアプリであればpackage.jsonでnpmスクリプト定義しておくなりしておきます。
以下「npm run getdb」を実行するとカレントディレクトリにDBファイルが吐き出されるpackage.json。
{
(snip)
"scripts": {
(snip)
"getdb": "adb exec-out run-as jp.co.xyz cat databases/awesomeapp.db > ./awesomeapp.db"
},
exec-outはadbコマンドのヘルプにも出ておらず、GoogleのAndroid Deveropersサイトのドキュメントにも載っていないイースターエッグ的なオプションです。今後無くなる可能性は無きにしも非ずです。
今の所exec-outオプションの存在はネット検索で情報が見つかりますが、もしexec-outが無くなったらネットに新しい方法が流れるまで時間が掛かるかも知れません。自分でソースを調べればネットに情報が出てくるまで待たなくて済みます。
補足
/development/tools/winscope/capture_sf_trace.shというシェルの中でこんなadbコマンド使用例があります。
adb exec-out su root cat /data/misc/trace/layerstrace.pb >"$outfile"
exec-outはGoogle内のプログラマが使う為のオプションなんですね。ついでに利用させて貰いましょう。