2011/05/21

Bloggerでエクスポート/インポートするとURLが変わる

ちょっとブログの記事をまとめて変更したいことがあったので、エクスポート->記事を全て削除->エクスポートしたXMLを編集->インポート をしたら記事のURLが変更されてしまった....。
記事の中の記事へのリンクも手動で変更するハメに。
しばらくGoogleのインデックスも更新されないし、やらなきゃ良かった。

2011/05/17

Android DNSの設定を変更する

bionic libcのソースコードとドキュメントを見て以下のことがわかった。

net.dns[n].[pid] => net.dns[n] => /etc/resolv.conf
の順番でDNSを見る。

[n] : 1-8
[pid] : プロセスID


* プロセス毎の設定

system propertyのnet.dns[n].[pid]で、プロセス毎にDNSの設定ができるようになっている。
この設定が対象のプロセスにおいて見つかった場合、nが一致するnet.dns[n] は使われない。
Androidの開発においてはよほど特殊なことをしない限り、プロセス毎の設定を利用することはないだろう。


* グローバルな設定

1. net.dns[n]にDNSのIPアドレスを設定する。
2. net.dnschange の値を現在値とは別の整数値に設定する。通常この値はインクリメントされている。

net.dnschange (ここではカウンターと呼ぶことにする)はnet.dns#が変更されたことを検知するための整数値。
前回DNS Queryを発行しようとしたときと異なる値のとき、net.dns#が再読み込みされる。
libcが覚えるカウンター値はstatic変数なので、プロセスごとにカウンターによるDNS更新の判断を行っている。
javaでの実装方法は、ConnectivityServiceが参考になる。


* 補足

WiFiや3GをONにすると、net.[interface name].dns[n] が変更されて、その値の1つが net.dns[n] に設定される。

/etc/resolv.conf も見る実装がソースには残っていたが、#ifdef ANDROID_CHANGES によって除外されていた。

以下、覚書として"dns"を含むsystem propertyを挙げる。


$ adb shell getprop | grep dns
[net.change]: [net.dnschange]
[net.dns1]: [192.168.3.254]
[net.rmnet0.dns1]: [202.32.159.24]
[net.rmnet0.dns2]: [210.128.58.10]
[net.dns2]: []
[net.dnschange]: [118]
[dhcp.eth0.dns1]: [192.168.3.254]
[dhcp.eth0.dns2]: []
[dhcp.eth0.dns3]: []
[dhcp.eth0.dns4]: []
[net.eth0.dns1]: [192.168.3.254]
[net.eth0.dns2]: []
[net.dns3]: []

2011/05/16

Androidでb-mobile Fair 15日間の通信量

5/1-5/15の使用量: 28MB

連休中に出かけることが少なかったとはいえ、少なすぎる...。
目標値の3分の1以下。日中に節約しすぎた。
仕事中にメールは見ないので、休憩時間以外は3Gを切っていた。

主な用途はメール・ブラウザ・RSSリーダー。
RSSリーダーでのフィードの取得はWiFiで行っていた。
なので、通勤中のメールとブラウザの使用が主な内訳。

しかしまあ、あと105日使えるのだから、気分は良い。
4ヶ月/500MB/¥5000 という少しだけ割高なライトプランを出しても売れるのではなかろうか。

2011/05/14

AndroidのProxy設定のないアプリにProxyを越えさせる(HTTP+少しHTTPS)

DeleGateとwebnamedを使い、Proxy設定のないアプリでファイアウォールを越えてHTTP/HTTPS通信することに成功した。

こちらのブログを参考にさせてもらった。
DeleGateとwebnamedのネットワーク構成は"AndroidのProxy設定のないアプリにProxyを越えさせる(HTTPのみ)"に書いた図の、squidがDeleGateに置き換わったもの。

文末にDeleGateのinit scriptを示す。
参考元のスクリプトと主に違うところは、

- chkconfigに対応している
- DeleGateが参照するDNSサーバをwebnamedノードにしている
(192.168.3.10 がwebnamed、192.168.3.20:80 がProxy)

さて、題名に"少しHTTPS"とある理由だが、DeleGateのSSL透過プロキシ機能はman-in-the-middleな仕組みなので、サーバ証明書の検証をスキップする機能があるアプリでないと、HTTPSを使えない。

Dolphin Browserを試したところ、HTTPSでアクセスすると"アクセスしようとしているサーバ名が証明書と違うがどうするか?"という意味の警告が表示され、そのままページを開くかどうか選択できる。Gmailを開くことができた。
NewsRobはGoogle Readerから同期する際にExceptionが発生して、同期することができなかった。後日他のRSS Readerを試してみようと思う。

/etc/init.d/delegate:

#!/bin/sh
# chkconfig: 345 98 20
# description: delegate server
# processname: delegated

DAEMON=/usr/local/bin/delegated
DGROOT=/var/spool/delegate
DELEGATE_PORT=8080
PROXY_HOST=192.168.3.20
PROXY_PORT=80
DNS_SERV=192.168.3.10

test -x $DAEMON || exit 0

start_proc() {
$DAEMON DGROOT=$DGROOT -P$DELEGATE_PORT \
PROXY="$PROXY_HOST:$PROXY_PORT:*" \
PERMIT="http,https:*:*" \
REMITTABLE=http,https \
STLS=mitm \
RELAY=vhost RES_NS="$DNS_SERV,END"
}

stop_proc() {
$DAEMON DGROOT=$DGROOT -P$SRC_PORT -Fkill
}

case "$1" in
start)
start_proc
;;
stop)
stop_proc
;;
restart)
stop_proc
if [ "$?" != "0" ]; then
exit 1
fi
start_proc
;;
*)
echo "Usage: $0 start|stop|restart"
exit 1
;;
esac

2011/05/12

rgrepでソースコードを検索する

私はC/Javaのソースコードを検索するときvim/eclipse/ctags/globalを使うが、ソースコードを解析するときに比較的rgrepを使うことが多い。

git/svnレポジトリから取得したソースコードを検索するときのために、以下のaliasを定義している。


alias rgrep='rgrep --exclude="*\.o" --exclude="*\.class" --exclude="*\.dex" --exclude="*\.swp" --exclude="tags" --exclude="GTAGS" --exclude="GSYMS" --exclude="GRTAGS" --exclude="GPATH" --exclude-dir="*\.git" --exclude-dir="*\.svn"'

2011/05/10

HTC Desire - CyanogenMod 6.1.1のバッテリー消費量(WiFi)

以下の状態において、8時間で100%=>%94 だった。
意外と減り幅が少ない印象。

- Wifi ON, 3G OFF
- K9-Mail : IMAP Idle 2アカウント, メール1件取得して6時間LED点滅
- NewsRob : 1時間毎の同期、100件くらい自動取得
- なまず速報β (速報なし)


#追記 5/11

以下の使い方で、100%=>70%

- 7.5時間WiFi、NewsRobで220件くらい自動取得、ほとんどスリープ状態
- 3時間3G、1.5時間ほどNewsRobやブラウザを使う。
- 通信量: WiFi 3.9MB、3G 4.0MB

2011/05/09

AndroidのProxy設定のないアプリにProxyを越えさせる(HTTPのみ)

ここで紹介する方法は動作はするが役立つ場面が少ない。

squid透過プロキシとProxy越えをするDNS Serverを使って、プロキシ設定のできないアプリにファイアウォールを越えさせることに成功した。

ただし、squid透過プロキシの仕組み上、HTTPSは通すことができない。squid 3.1のssl-bumpというman-in-the-middleな機能を使うとできそうだが、制約が大きそうなのでやらない。
HTTPSを通せないので、大抵HTTPSが必要なアカウント認証するWebアプリが使えないのが痛い。

これだけやってもHTTPのみ...。
はっきり言って、プロキシ設定ができるOpera Mobileだけの方が役に立つ。

以下、検証した構成。
Androidからsquidへは、iptables で80 portをsquidのホストの3128 portへリダイレクトした。

#追記 5/14
その後、squidの代わりにDeleGateを使ってHTTPSを通すことに成功した。


                       +-----------------------+
                       |  android smart phone  |
                       +-----------------------+
                          |                |
                          | DNS query      | http
                          v                v 
                    +----------+     +-------------+
                    | webnamed |     |   squid     |
                    +----------+     |(transparent)|
                          |  |       +-------------+
                DNS query |  |  http        |
                          v  +---------+    | http
                    +------------+     |    |
                    | DNS Server |     |    |
                    +------------+     v    v 
                                     +-------------+
                                     | http proxy  |
                                     |  firewall   |
                                     +-------------+
                    +--------------+   |    |
                    | resolver.cgi |<--+    | http
                    +--------------+        v
                           |         |-------------+
                 DNS query |         |   web site  |
                           v         +-------------+
                    +------------+
                    | DNS Server |
                    +------------+

2011/05/08

Androidの非公開APIを呼ぶ

リフレクションを使うと、Android SDKには公開されていないAPIを呼び出すことができる。
公開されていないAPIなので、バージョンごとの互換性が失われる可能性が高いことに注意。

以下はUSBテザリングをONにする処理。


ConnectivityManager cm = (ConnectivityManager) getApplicationContext().getSystemService(CONNECTIVITY_SERVICE);

Class c = Class.forName(cm.getClass().getName());
Field f = c.getDeclaredField("mService");
f.setAccessible(true);
Object iconn = f.get(cm);

// Method: int tether(String iface)
Method method_tether = iconn.getClass().getMethod("tether", String.class);
Object ret = method_tether.invoke(iconn, "usb0");

// Returns ConnectivityManager.TETHER_ERROR_*
int e = ((Integer) ret).intValue();

2011/05/07

Android K-9 Mail 待機時の通信量を節約する(2)

K-9 Mail通信量計測の詳細。

* 条件

- b-mobile Fair
- K-9 Mail v3.706
- Gmailの1アカウント、SSL、受信トレイが空の状態。


* 計測結果

計測値はIMAP同期が5回の同期での平均値、IMAP IDLEが1時間の間に発生した通信の平均値。
()内は128 Bytes換算のパケット数

[IMAP同期の同期毎の平均値]
- IPパケット数 = 12 (12)
- IPパケットサイズ合計 = 2.01 KBytes

[IMAP IDLEの平均値 (リフレッシュ間隔24分)]
- IPパケット数 = 5分毎に12 (12), 初回接続時に47 (62)
- IPパケットサイズ合計 = 5分毎に1.54 KBytes, 初回接続時に8.47 KBytes


* どちらが節約できるか

5分毎のIMAP同期とIMAP IDLEは、パケット数においてほぼ同等と見なせる。
したがって、メールが通知されるまで5分以上かかっても良い人はIMAP同期の方が節約できて、それほど待てない人はIMAP IDLEにするしかない。IDLE接続のリフレッシュ時間はデフォルトの24分が良い。


* パケット数の算出について

実測したパケット数はIPパケット数なので、モバイルデータ通信時のパケット数はもっと多くなるかもしれない。
節約するならばデータサイズだけでなく、パケット数の削減を考えなければならない。
実際は単純には計算できないと思うが、b-mobile Fairのサイトでは便宜上1パケット=128 Bytesで計算する方法を紹介している。
私はモバイルデータ通信のプロトコルについて詳しくないので、この方法にあやかってみる。

b-mobile Fairでは4ヶ月で1GBytesまでという契約だが、通信量とパケット数は関係する。
b-mobileのサポートに問い合わせて見たら、純粋な送受信したいデータの他にヘッダ、デリミタといったオーバヘッドも通信量として計算されるので、100バイトを1回送信するよりも1バイトを100回送信する方が通信量が多いということだ。


* IMAP IDLE接続のタイムアウト

IMAP IDLEの5分毎というのはサーバから送られてくるKeepAliveのパケットだと思われる。
IMAP IDLE接続のリフレッシュ間隔とは関係がない。
上記のパケット以外に、約25分間隔でFINパケットが来た。FINパケットの後、K-9 Mailは同一のIPアドレスへ再接続した。
そして特筆すべきは、たまにRSTパケットがサーバから来た。RSTパケットが来る条件は不明。
RSTは接続を強制終了する要求であり、K-9 Mailが再接続を試みたようだが、さらにRSTが返された。
RSTに続く通信は9.27 KBytes、54(69)パケットのやりとりが発生した後に別のサーバへ接続。
これらの現象はIMAP IDLE接続のタイムアウトと関係しているようだが、明確な仕様がわからなかった。

FIN、RSTはIDLE接続のリフレッシュ時間を24分(デフォルト)に設定しておくと返されない。
そのかわり、リフレッシュ間隔毎に再接続のためのやりとりが発生する。
デフォルトが24分なのはFINが返ってくる25分という時間と関係があるのかもしれない。

Android K-9 Mail 待機時の通信量を節約する(1)

私はb-mobile Fairの回線を使用し、メーラーはK-9 Mailを使っている。
待機時のメーラーの通信量が気になったので、パケット代節約の方法を探るために計測した。

ここでの計測対象はGmailに限った話しで、他のメールサーバだと結果が異なるかもしれない。

結論を言うと、
  • プッシュ接続(IMAP IDLE)より、5分より大きい間隔での定期的な同期(IMAP同期)の方が、パケット数的にはお得。
  • 節約したいならばIMAP同期で同期時間を10分以上に設定。
  • 節約したいけどプッシュ通知は必須というときは、IMAP IDLEのリフレッシュ時間を24分(デフォルト)に設定。
  • いずれにせよ、1ヶ月で5~6万パケットの大した数ではないので、倹約家以外はあまり気にする必要はなさそう。

である。

詳細は Android K-9 Mail 待機時の通信量を節約する(2) に。


補足としては、どちらの方法でも取得するフォルダの数は少ない方が通信量を節約できる。
K-9 Mailではプッシュフォルダの数だけ通信の接続数が増えるようだ。

私はK-9 Mailでプッシュフォルダを"1stクラスフォルダのみ"として、受信トレイのみを1stクラスに設定している。
基本的に受信トレイからの振り分けはPCからまとめてやっている。

逆USBテザリングでメール待ち受けができるCyanogenModパッチ(2)

環境: Ubuntu 10.10 Desktop, HTC Desire, CyanogenMod 6.1.1

前回のパッチの欠点を改善したものを作った。一応patchと、jarを固めたzipをここに置いてある。

このパッチにできることは、次のとおり。

- 一度だけAndroidとPCに設定をしておけば、USBケーブルを刺したときに通信できるようになる。
- 逆USBテザリングと通常の正方向のUSBテザリングを切り替えられる。

これで晴れて快適な逆USBテザリング生活?ができるようになった。

必要な設定は
- PC
    USB RNDIS (Ubuntuだとusb0)のPC側IPアドレスを固定アドレスにする。
- Android
    システムプロパティを設定する。
    - usb.reverse.dns1 ... DNSアドレス
    - usb.reverse.gw ... PC側のusb0のアドレス
    これらのプロパティのいずれかが空のとき、通常のUSBテザリングになる。

RNDISドライバさえあれば他のOSでも動くはずだが、試していない。


以下、設定方法。

ここに記述してあることは当然上記パッチをAndroidに焼かないと動作しないので、あしからず。

AndroidをUSBで接続したときに現れるusb0インターフェースのIPアドレスを固定する。

Ubuntu Desktopに標準インストールされるNetwork Managerとethtoolではこれができなかった。なぜなら、usb0のMACアドレスは接続するたびに変化する。Network Managerが前回接続したことのあるインターフェースであると認識せず、usb0に施したはずの設定が反映されない。

ということで、Network Managerは消して、/etc/network/interfaces での運用に変更する。
# Network Managerと共存する方法があるかもしれないが、私は上手く行っていない。


# apt-get remove network-manager



これは私の環境のinterfaces。usb0 以外の設定は、環境に合わせて変更する必要がある。
usb0 のPC側アドレスは 192.168.42.100 とする。192.168.42.0/24 のうち、192.168.42.129 以外なら他のアドレスでも良い。

PC: /etc/network/interfaces

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp

auto usb0
iface usb0 inet static
    address 192.168.42.100
    netmask 255.255.255.0 



Androidで次のファイルを作成する。system partitionなのでadb remountや、recovery modeで変更する。

android: /data/local.prop:

net.dns1=
usb.reverse.dns1=
usb.reverse.gw=



Androidを再起動する。
PCからadbでシステムプロパティの値を設定する。
192.168.3.254 はDNSのアドレスなので、環境に合わせて変更する。


adb shell setprop usb.reverse.dns1 192.168.3.254
adb shell setprop usb.reverse.gw 192.168.42.100



以上で設定完了。

この状態でAndroidをUSBケーブルでPCに接続し、USBテザリングを有効にしてWebブラウザ等が使用できるかを確認する。
なお、Android Marketでダウンロードできないときは、Google Talkに一度ログインするとダウンロードが開始するかもしれない。

通常の正方向のUSBテザリングに戻したいときは、usb.reverse.dns1かusb.reverse.gwを空にすれば良い。

adb shell setprop usb.reverse.gw ""



このパッチでも不便な点がある。
それは複数の異なるLAN上のPCに接続するとき、DNSとゲートウェイの設定を変更しなければならないことだ。

2011/05/06

git pullとgit pull --rebaseの違い

git pull          ... git fetch, git merge [origin/master]
git pull --rebase ... git fetch, git rebase [origin/master]

古いgitだと git pull に --rebase オプションがない。


それでは merge と rebase の違いは?というと、他に詳しく説明しているページをご参考に。

はしょって言うと、次のとおり。

個人のブランチに他のコミットを取り込む ... rebase
公開されたコミットを統合する ... merge (rebaseしてはいけない)

ADWLauncherで地味にうれしいこと

アイコンをドラッグしてゴミ箱に入れるとき、1秒くらい待つと"アンインストールしますか?"と聞いてくる。
アプリケーションの管理を開かなくてもアンインストールできる。
アプリの入れ替えをちょくちょくやるのでうれしい。

2011/05/05

EclipseでF11を押すとAndroidManifest.xml.outが生成されるときの対処

EclipseでF11を押すと、xmlファイル名の後ろに.outが付いたファイル(AndroidManifest.xml.outやlayout.xml.out)が生成されてプロジェクトが実行されないときの対処。

発生条件
- EclipseにADT PluginとWTP Pluginをインストールしている
- Androidプロジェクトで作業している
- xmlファイルを開いている
- (Ctrl+)F11を押す

これはEclipse WTPの機能で、xmlファイルに対して Run As > XSL Transformation を実行したことになるため。

以下の設定を変更すればOK。

Window > Preferences > Run/Debug > Launching を開き、
"Always launch the previously launched application' in the 'Launch Operation' section."
のラジオボタンを選択する。

こちらがネタ元

git tag のコミット日時を一覧表示する

git tagのタグ一覧をコミット日時と共に見たいと思うことがある。
gitだけで見る方法がわからなかったので、スクリプトを書いた。

こんな感じ。

$ git-tagdate
2011-02-08 11:00:00 -0800 tag1
2011-02-09 12:00:00 -0800 tag2
2011-02-10 13:00:00 -0800 tag3


日付順にソートしたいときは git-tagdate | sort で。

git-tagdate:

#!/usr/bin/perl

open(TAG, "git tag|") || die "$!";
while ($t = <TAG>) {
    chomp $t;
    open(LOG, "git log --format=\%ci $t^..$t|") || next;
    $d = <LOG>;
    close LOG;
    chomp $d;
    print "$d  $t\n";
}
close TAG;

逆USBテザリングの通信速度/メリット/デメリット

先に結論を言うと、WiFiと比較したときの逆USBテザリングのメリットはあまりない。

逆USBテザリングの通信速度を計測した。
接続したPCの回線はYahooBB ADSL 8M。
ping値は私のレンタルサーバ(埼玉->東京)に対して行った。

* HTC Desire

SpeedTest.Net
Download 5.19 Mbps, Upload 0.77 Mbps
ping 28ms

* PC - Ubuntu 10.10 Desktop x86_64

速度.jpスピードテスト
Download 5.13 Mbps, Upload 0.80 Mbps
ping 26ms


ということで、この環境だとほぼUSBホストOSと同等の速度が出ることがわかった。

逆USBテザリングのメリットは、

- 充電速度がWiFi接続中よりも速い
- WiFiステーションがなくても接続できる

だと思う。
WiFi接続しながら充電していると充電完了までの時間が長くなる。逆USBテザリングの場合は通信のための消費電力がかなり小さいものと思われるため、WiFi OFF時の充電速度とほぼ変わらない。
WiFiステーションはいまどきどこでもありそうなので、あまりアドバンテージにはならないかもしれないが、セキュリティ上の理由でWiFiステーションを使いたくないときに、モバイルデータ通信の代替にはなる。

ちなみに私が逆USBテザリングを選択する理由は"趣味"だ。


反対にデメリットは多い。

- PCが必要
- PCでの設定が必要 (Linuxではできたが、Windowsは知らない)
- USBケーブルが必要
- rootedが必要
- USBテザリングをサポートしているAndroidが必要
- メール待ち受けやAndroid Marketを使用するにはAndroidのベースモジュールの変更が必要
- USBでの接続時間が長いとバッテリーの劣化が進むかも

Linux、rooted、テザリングをサポートしているROMが必要という時点で、逆USBテザリングは相当しきいが高い。
VMWare等でLinuxを動かせばWindowsでもできないことはないが...

テザリングは、最近ではドコモのOptimus PADやKDDIのHTC Evoが標準でサポートしている。今後標準サポートする機種は増えてくるのではないかと思う。

逆USBテザリングでメール待ち受けができるCyanogenModパッチ(1)

Androidの逆USBテザリング時に通信しないアプリが存在する理由で述べた問題を解決すべく、Androidのベースモジュールに手を入れた。

やったことは、アプリに対してUSBテザリングのON/OFFをWiFiの接続状態に見せかけるようにした。
ConnectivityManager を通じてネットワークの状態を確認しているアプリは、USBテザリングがONになったときWiFiが接続したものと認識するようになる。
また、USBテザリングがON(OFF)になったときにWiFiが接続(切断)したという通知がアプリに伝わるようになる。

すなわち、メーラーの自動受信やAndroid MarketのOATのような待機系の機能が逆USBテザリング時に使えるようになる。


試したアプリの動作は次のとおり。

* K-9 Mail

IMAP IDLE接続でUSBテザリングON/NAT設定すると、"同期停止"の表示がなくなり、メールを自動的に受信した。
USBテザリングをOFFにすると、再び"同期停止"の表示になった。

* Android Market

アプリからのダウンロード、PCサイトからのOTAインストール(USBにつながっているからOn The Airじゃないけど)はできた。
しかしたまにダウンロードが開始しないことがある。そのようなときはダウンロード履歴を何回か開いてみると開始する。Google Talkにログインするとダウンロードが開始した。通常のWiFi接続のときも同様の現象を見たことがあるので、これが今回の変更による問題なのかどうかはわからない。

* NewsRob

WiFi Onlyの定期的な同期を設定。USBテザリングON/NAT設定すると、設定された同期間隔でフィードを取得した。

* なまず速報

USBテザリングON/NAT設定した後すぐに接続した。
USBテザリングをOFFにするとしばらく通知領域にアイコンが残り、"診断情報"のサーバ接続が"はい"のままだった。
これはアプリの性質上、できる限り切断時間を短くするよう、繰り返しリトライを行っているものと推測される。


変更内容は以下のとおり。ものすごくwork aroundなやり方。

* android.net.ConnectivityManager
  • getActiveNetworkInfo() ... テザリングON時に"WiFiで接続した"という情報を返す。
  • getAllNetworkInfo() ... WiFiのNetworkInfoについて、テザリングが有効であれば"WiFiで接続した"という情報に置き換える。
  • getNetworkInfo(type) ... getAllNetworkInfo()と同様。

* com.android.server.connectivity.Tethering
  • TetheredState.enter() ... USBテザリングがONになった直後、"WiFiで接続した"というIntentをbroadcastする。
  • TetheredState.exit() ... USBテザリングがOFFになった直後、"WiFiが切断した"というIntentをbroadcastする。


ビルドした/system/framework配下のファイルとソースコードのパッチを一応ここにおいた。
zipはHTC Desire、CyanogenMod 6.1.1限定なので需要ないと思うが。

最後に問題点を挙げる。

- WiFiとテザリングの同時使用は考えていないので、USBテザリング中にWiFiをONにするとおかしな挙動をするかもしれない。

- 本来の正方向のUSBテザリングを3G回線でするとき、アプリがWiFiだと認識してWiFi向けの機能を実行してしまう。アクティブな接続の種類をアプリに返すとき、3Gより優先してなりすましWiFiの情報を返してしまうため。

- USBテザリングをONにした後、それほど間を空けずホストOS側でrusbtether-natを実行しないと、アプリが通信できないものと判断してしまうかもしれない。

- jarを焼いて直後のOS起動後、USBテザリングをOFFにするとUSBデバッグができない状態となり、USBテザリングをON/OFF繰り替えしても復帰できなくなってしまった。再起動したら直った。その後この現象は発生していない。

2011/05/04

CyanogenMod 6 froyo-stable をソースコードからビルドする

環境: Ubuntu 10.10 Desktop x86_64

CyanogenMod 6.1.1の/system/framework/framework.jar, services.jar を入れ替えるために、ソースコードからビルドした。
/syste/framework/配下のファイルの入れ替え以外は試していない。

CyanogenMod Wikiにプラットフォームごとのビルド方法が説明されている。有難い。
UbuntuとOS Xでの方法が書いてあるが、両方見た方がいいかも。


$ repo init -u repo init -u git://github.com/CyanogenMod/android.git -b froyo-stable
$ repo sync


を実行すると


error: revision master in CyanogenMod/android_device_advent_vega not found


というエラーが発生した。
.repo/manifest.xml からandroid_device_advent_vegaをコメントアウトして、再び repo sync。
これが完了したらビルドの設定。

WikiでのUbuntu上でのビルド方法には brunch bravo を実行するように書いてあったものの、このバージョンのenvsetup.shにはbrunchがないようだ。
OS X上でのビルド方法にはlaunchを実行するように書いてある。

続いてmake。frameworks/base と frameworks/base/services/java だけ部分的にmakeしたいが、モジュール間の依存関係を調べるのも面倒なのでフルビルド。

次のエラーが発生した。


(unknown): error 17: Field org.apache.http.protocol.HTTP.EXPECT_CONTINUE has changed value from "100-Continue" to "100-continue"


このスレッドを見ると、CyanogenMode 6.1.1リリースのすぐ後に修正された問題のようだ。


external/apache-http/src/org/apache/http/protocol/HTTP.java:    public static final String EXPECT_CONTINUE = "100-continue";


の 100-continue を 100-Continue に変更。

続いて次のエラーが発生した。


make: *** No rule to make target `vendor/cyanogen/proprietary/RomManager.apk', needed by `out/target/product/bravo/system/app/RomManager.apk'.  Stop.


WikiにROM Managerをダウンロードするように書いてあったが、やらなかったので出た。
ビルド中にcurlで外から取得するようになっていて、取得先のURLが見つかっていない。
CyanogenModの.zipから取り出して配置して、再びmake。

以上でmakeが完了した。
フルビルドした後はビルドしたいモジュールのディレクトリに移動して、mmコマンドを実行。

実はCyanogenModのビルドに先立って、手元にandroid git repositoryから取得したソースがあったので、それを使おうとした。
CyanogenModのframework.jarのclasses.dexをsmaliで逆コンパイルし、一部のクラスだけ置き換えたのだが、問題があった。
gitのタグandroid-2.2.1_r1とCyanogenMod 6.1.1では、android.internal.RのリソースIDが変わっていて、適切なリソースを取得できなかった。

Android Platformでの一部のプロジェクトのみmakeする

例えば frameworks/base をmakeしたいとき。
mmmをAndroid.mk のあるディレクトリを渡して実行する。

$ . build/envsetup.sh
$ mmm frameworks/base


mmmは再帰的にディレクトリを見るわけではないので、
frameworks/base 配下のプロジェクトに対しても個別にmmmを実行する。

Ubuntu 10.10 x86_64 でAndroidをビルドする

環境: Ubuntu 10.10 Desktop x86_64

libstdc++.so がないと言われる

embeddedさんのブログを参考にして、

$ sudo apt-get install gcc-multilib g++-multilib ia32-libs lib32z1-dev lib32ncurses5-dev

を実行したらビルドできた。
たぶん
g++-4.4-multilib: /usr/lib/gcc/x86_64-linux-gnu/4.4/32/libstdc++.so
が必要だったのだと思う。

2011/05/03

Androidの逆USBテザリング時に通信しないアプリが存在する理由

正確にはWiFi/3GがOFF、USBテザリングONのときに通信しないアプリが存在する理由。

私が使用している K-9 Mail、NewsRob、ついっぷるは逆USBテザリング時に自動同期しない。Android Marketでアプリをダウンロードできない。
その理由について考えた。

Android SDKのConnectivityManagerから、ネットワーク接続の状態を取得できる。
ConnectivityManagerが認識する接続の種類を挙げる。

TYPE_MOBILE
TYPE_WIFI
TYPE_WIMAX
TYPE_BLUETOOTH (非公開, 2.3.3から)
TYPE_DUMMY (非公開,2.3.3から)
TYPE_ETHERNET (非公開,2.3.3から)
(Sub Type) TYPE_MOBILE_DUN
(Sub Type) TYPE_MOBILE_HIPRI
(Sub Type) TYPE_MOBILE_MMS
(Sub Type) TYPE_MOBILE_SUPL

この中にUSBテザリングはない。
AndroidのUSBテザリングはUSB => WiFi/3G の方向での通信だけを想定していると思われるため、テザリング中であるか否かはAndroid端末内からの通信ができるかどうかと関係ない、ということなのだろう。

2.3.3で追加されているTYPE_BLUETOOTH, TYPE_DUMMY, TYPE_ETHERNETは@hideが記述されていて、Android SDKには非公開となっている。コミットログには

  Add some network types that OEM's are asking for.
  Adding them hidden so that if OEM's are rolling their
  own at least they can use the same values.
  Will mark them unhidden in a future sdk release.

と記述されていたので、企業からリクエストがあって追加した将来の互換性を保つための値で、わからないがTYPE_ETHERNETはEthernetインターフェースを装備したデバイスに使うのだと思う。Androidが動くNet Bookとか?

定期的にデータを取得するようなアプリは、ConnectivityManagerから接続状態を見て、未接続状態のときはデータを取得しない処理が実装されていることが多いと思われる。それが、アプリのお作法として適切だ。
したがって、WiFi/Mobile Data Connection/WiMAXが全て未接続であれば、USBテザリングのON/OFFには関係なく未接続状態だと判断されるだろう。
オープンソースであるK-9 Mailのソースを確認したらメールを同期する条件として、少なくとも全ての接続タイプのうち1つでもState.CONNECTEDであることが含まれていた。

なお、ConnectivityManagerでチェックせずに通信を開始するような機能は使用できる。
ついっぷるでは、「ネットワークに接続されていません」とメッセージは出ていても、手動で更新ができる。


#追記 5/5
ConnectivityManagerで接続状態を確認していることだけが理由ではなかった。
K-9 Mailはメール送受信用のサービスが常時動作しているが、常に走っているわけではなくネットワークが切断状態のときはサスペンドするようになっている。再び起動するのは ConnectivityManager.CONNECTIVITY_ACTION の Intent が通知されたときだ。
電力消費を抑えるための工夫だろうと思う。実験したところ、Android Marketについても同様のことが言える。

逆USBテザリングでメール待ち受けができるCyanogenModパッチを作って検証したら上手く動いた。

2011/05/02

HTC Desire Softbankを解約してb-mobile Fairへ変更した

今日b-mobile Fairを開通したので、HTC DesireのSoftbank契約を解除した。
契約日は1年前のHTC Desire発売日。今の時点で支払い残高は37,500円だそうだ。

早速通信速度を計測した。
SpeedTest というアプリがあり、速度計測アプリの中では結構気に入っているのだが、10MBくらい通信量を使う。
もったいないから busybox の wget を使った。

こんな感じで。
$ time wget -O /dev/null http://domain/url

久喜市の自宅から東京のどこかにあるらしい私のレンタルサーバへアクセス。
1MiBのファイルをダウンロードしたら、2.2Mbpsくらい出た。ping は 120-140ms くらい。
満足できる速度だ。アップロードは試していない。

my b-mobileで残データ量を確認したら、だいたい使った分だけ減っていた。
b-mobileのサイトでFAQを確認したら、「表示はリアルタイムではなく数時間ごとに更新される」そうだ。表示の最小単位は1MB。
残量表示はそれでいいが、使う側としては「数時間ごと」の使用量を1Byte単位で表示してほしいものだ。

ところで、b-mobileのSIMを刺すとアンテナの表示が x になると聞いていたが、CyanogenModのおかげか、RADIOを最新にしているおかげなのかはわからないが、問題なく表示されている。

Android Reverse USB Tethering with NAT

Environment: Android:CyanogenMod 6.1.1, PC:Ubuntu 10.10

It might be inconvenient when using reverse usb tethering with bridged network.

I must be execute the script that deletes a bridge interface when I stop tethering.
Without doing that, the network transfer in PC stops.

So I tried tethering with NAT that doesn't stop the network transfer in PC.
I came to be able to disconnect USB cable anytime without iconvenient effect in PC.

Firstly, enable IP forwarding in my PC.

/etc/sysctl.conf:

net.ipv4.ip_forward=1



To apply, execute 'sysctl -p'.
If you want to change the perameter temporarlily without editing sysctl.conf,
execute 'sysctl -w net.ipv4.ip_forward=1'.

Next, configure iptables.

iptables -A INPUT -j ACCEPT
iptables -A OUTPUT -j ACCEPT
iptables -A FORWARD -i usb0 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE



To enable auto setting of iptables in Ubuntu, you must write a script to apply your settings.
I use following script in /etc/rc.local.

/etc/rc.local:

#!/bin/sh -e
/sbin/iptables-restore < /etc/iptables-rusbnat.rules



iptables-rusbnat.rules are created by 'iptables-save > iptables-rusbnat.rules'.


You have to do the work above only once.

You have to do following works whenever enabling reverse tethering.
Enable 'USB Tethering' in android. Execute rusbtether-nat in PC.

rusbtether-nat:

#!/bin/sh 
set -e
export LANG=C
ROUTE=/system/bin/route
GWA=`LANG=C ifconfig usb0 | grep "inet addr:" | \
    sed 's/ *inet addr:\([0-9\.]\+\).*$/\1/'`

ping -I usb0 -c 1 $GWA > /dev/null
# add route
adb shell su -c "$ROUTE add default gw $GWA dev usb0"

DNS=`grep nameserver /etc/resolv.conf | head -n1 | awk '{print$2}'`

# clear dns
for p in net.dns1 net.dns2 net.dns3 net.rmnet0.dns1 \
    net.rmnet0.dns2 dhcp.eth0.dns1 dhcp.eth0.dns2 \
    dhcp.eth0.dns3 dhcp.eth0.dns4 net.eth0.dns1 \
    net.eth0.dns2 net.usb0.dns1 net.usb0.dns2; do
    adb shell su -c "setprop $p ''"
done

# set dns
adb shell su -c "setprop net.dns1 $DNS"

echo Completed



To stop reverse tethering, just disconnect USB cable or disable 'USB Tethering' in android.
You have nothing to do in PC.

You should not to enable WiFi/3G while using reverse tethering.
Because dns settings in android are changed when enabling WiFi/3G and dns client will be not able to resolve names.
If you enable WiFi/3G while reverse tethering, execute rusbtether-nat again to fix dns settings.

Proxy越えをするDNS Server

Proxy越えをするDNS Serverを作った。Perlの Net::DNS::Nameserver を利用した。
これとsquidの多段Proxyを組み合わせれば、AndroidのProxy対応していないHTTPアクセスするアプリを全てProxy越えさせることが可能なはず。

ダウンロードはここのwebnamed-*.tgz。
webnamed + 逆USBテザリング + squid透過Proxyを組み合わせて、DMZのProxy越えができるかを検証しようと思う。

* 仕組み

既存のLAN内のDNS Server ... Default DNS Server
作ったDNS Server ... Slave DNS Server (webnamed)
外部CGI Server上のCGI ... Resolver CGI (resolver.cgi)

1) Slave DNS ServerがDNS queryを受信する。
2) アドレス解決のDNS query の場合、Resolver CGI にDomain Nameを送信して、アドレスを取得する。
3) アドレス解決以外の DNS queryまたは 2) でアドレスが未解決の場合は、Default DNS ServerへDNS queryをforwardする。
4) Slave DNS Serverが 2) または 3) の結果を返信する。


* なぜwebnamedが必要か

まずAndroidにProxy設定をせずにProxy越えするには、DMZのHTTP ProxyへProxy用のHTTP Headerを含むHTTP Requestを投げる必要がある。これにはAndroidのWebブラウザからのHTTP Requestを、一旦squidのTransparent Proxyに投げる。次にsquidからDMZのHTTP Proxyへ投げれば解決する。

だが、WebブラウザはHTTP Proxyを通すことを認識していないため、WebブラウザのDNS Resolverが動いてしまう。Firewall内のDNS Serverは、外部DNS Serverへアドレス解決を参照しない運用になっていることが多いため、大抵DNS queryが失敗するだろう。
そこで、このDNS queryに対して適切に返答するために、今回作ったDNS Serverが必要になる。


* resolver.cgi について

resolver.cgi は私のレンタルサーバに配置した。
webnamed のデフォルトの設定だとそのレンタルサーバに接続する。別のサーバに配置することも簡単にできる。
Google App Engineの利用を最初に考えたものの、InetAddress.getHostAddress() が禁止されていたので断念した。
広告入りの無料サーバでも動作するが、CGIへの直接アクセスは(できるけど)規約違反になることがあるので確認が必要。


* 他に方法はないのか

Google先生への質問スキルが低いのか、似たような物が見つからなかった。
あるいは、もっと別の解決方法が存在するのか、単に需要がないのか。
ご存知の方がいたら教えてください。

2011/05/01

vimで線や矢印を描画する

たまにテキストでクラス図やネットワーク構成図を書くことがある。
vimのプラグインDrawIt!を使うと線、矩形、矢印、楕円を描画することができる。