AndroidのSQLiteDatabaseの覗き方

## 非root端末のSQLiteDatabaseを覗きたい

Androidアプリの開発で非root端末のSQLiteDatabaseは覗けない。
厳密には覗けないわけではなく、該当するアプリからは
アクセスできるのだけど、これでは勝手が悪い。
開発中はSQLを直接叩きたかった。
だから無理矢理に覗く方法を作りました。

## どうやって？

アプリ側に１クラスで完結する小さなプログラムを埋め込み、
そこ経由でアクセスできるようにした。
要はバックドアのようなもの。
もちろん開発専用なので間違ってもリリースビルドには入れては行けない。

## サンプルプログラム

説明にに使用したアプリケーションはこちらになります。
このアプリケーションのbuild.gradleとMyApplication.javaが該当します。

* [FastCheckList](https://github.com/cattaka/FastCheckList)

## 使い方

使い方は次の３ステップです。

* 小さなプログラムを埋め込む
* Application#onCreateで埋め込んだプログラムをキックする
* telnetで繋いでSQLを実行する

それでは１つずつ見て行きましょう。

### 小さなプログラムを埋め込む

AndroidManifest.xmlにINTERNETパーミッションを追加します。

```xml
    <uses-permission android:name="android.permission.INTERNET"/>
```

次にbuild.gradleのdependanciesに次の行を追加します。

```groovy
    debugCompile 'net.cattaka:telnetsqlite:1.0.0'
```

デバッグ専用なのでdebugCompileとしています。
compileにするとリリース版ビルドに含まれてしまうのでご注意ください。

### Application#onCreateで埋め込んだプログラムをキックする

カスタムアプリケーションクラスに次の様なコードを追加します。
カスタムアプリケーションクラスがない場合は作ってください。

```java
public class MyAppliction extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // SQLiteにアクセするためのサーバーを起動する（開発時専用）
        try {
             Class<?> clazz = Class.forName("net.cattaka.telnetsqlite.TelnetSqliteService");
            Thread serverThread = (Thread)clazz.getMethod("createTelnetSqliteServer", Context.class, int.class).invoke(null, this, 12080);
            serverThread.start();
       } catch (Exception e) {
            // ignore
        }
    }
}
```

serverThreadを生成するときの12080という値は、サーバーのポート番号です。
必要ならば適宜の値を変更してください。
リフレクションを使用しているのはリリースビルドでコンパイルエラーにならないようにするためで、
もしリリース時にコードごと消すならばリフレクション無しで書いても大丈夫です。

### telnetで繋いでSQLを実行する

アプリケーションを起動して、PCからtelnetで接続します。
接続方法は２つあります。

１つめはデバイスIPアドレスを指定する方法です。adb shell netcfgコマンドでIPアドレスを調べた後に、次のコマンドを実行します。

```bash
$ telnet <マシンのIPアドレス> <上で指定したポート番号>

```

２つめはadbコマンドのforwardを使ってlocalhost経由で接続する方法です。通常はUSBでデバイスを繋ぐのでこちらのほうが便利です。

```bach
$ adb forward tcp:<上で指定したポート番号> tcp:<上で指定したポート番号>
$ telnet localhost <上で指定したポート番号>
```

telnetの接続に成功したら、一行目に開きたいデータベースファイル名(SQLiteOpenHelperのコンストラクタに指定したデータベース名のこと)を指定します。それ以降はSQLを書いてEnterを押すとそのまま実行されます。

```bash
$ telnet ctk070 12080
Trying 192.168.3.70...
Connected to ctk070.xxx.yyyy.
Escape character is '^]'.
data.db                       <-データベースファイル名を入力
S
$
select * from checkListItem   <-SQLを入力して実行
S5
+id,entryId,sort,label        <-テーブルの列名(先頭に+が付いてる)
-1,1,1,財布                    <-以下テーブルのデータ(先頭に-が付いてる)
-2,1,2,鍵
-3,1,3,携帯電話
-4,1,4,手帳
-5,1,5,名刺

$
```



## GUIツールを使う

telnetで接続できるのは便利ですが、成形もされていないので見づらいです。幸いなことに対応した[RdbAssistant](https://github.com/cattaka/RdbAssistant/releases)というGUIのSQLエディタがあります。このSQLエディタはJavaアプリケーションとして動作するため、Javaランタイムがある環境ならばどこでも動作します。

* [RdbAssistant](https://github.com/cattaka/RdbAssistant/releases)

起動すると最初に接続情報を作りましょう。接続の種類のTelnetSqliteを選択し、ホスト名、ポート番号、データベース名を間違えず入力してください。

![20140927_1.png](https://qiita-image-store.s3.amazonaws.com/0/55138/099aceca-b354-96f3-453a-489a4218d7d4.png)

接続情報を作成したら、選択して接続ボタンを押してください。アプリ側が起動していればSQLの入力画面が表示されます。ここではSQLの実行だけでなく、テーブルの一覧や列名の閲覧も行えます。

![20140927_2.png](https://qiita-image-store.s3.amazonaws.com/0/55138/f0f21772-0520-d314-6085-8dff21d4a450.png)

## まとめ

Androidのアプリケーション開発でSQLiteデータベースの使用は避けては通れません。しかしだからといって中身が覗けないままでは開発も大変です。幸いAndroidのフレームワークは標準で沢山のAPIが使えるので、小さなツールは作って埋め込んでしまいましょう。ServerSocketがあれば大抵のことができるはずです。今回はSQLiteDatabaseを覗くために使用しましたが、この手法は他のことにも応用ができます。ただし、リリースビルドには含まれると駄目なので、混入しないように注意しましょう。