2011/04/30

Androidで逆USBテザリングを使う(NAT)

環境: Android:CyanogenMod 6.1.1, PC:Ubuntu 10.10

逆USBテザリングをブリッジ接続で行うと、不便が生じた。

- テザリングを止めるとき、PC側でブリッジ削除スクリプトを実行する必要がある。
- ブリッジ削除スクリプトを実行しないとPC側の通信ができなくなる。

それに、Androidにsshで入りたいわけでもないので、AndroidのIPアドレスがLAN内で見える必要もないし。
ということで、NATを利用する方法を考えた。この方法ならば、いつUSBを抜いてもPC側の通信には影響がない。
ブリッジ接続を紹介していたMathieuさんのコメント欄のところで、原理的に同じことを書いている人がいた。

なお、メーラーが自動受信しないとか、Android Marketのダウンロードが動作しないなどのAndroidの逆USBテザリング時に通信しないアプリが存在する理由で述べた問題があるが、私はAndroidのモジュールを変更して無理やりできるようにした。興味のある方はリンク先を参照してください。


まずPC側のIP Forwardを有効にする。

/etc/sysctl.conf:

net.ipv4.ip_forward=1


sysctl -p で反映。sysctl.confを変更せずに一時的に試すなら、
sysctl -w net.ipv4.ip_forward=1 で。

次に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

# 一部修正 5/6

Ubuntuでiptablesの設定をOS起動時に有効にするには、自分でスクリプトを書く必要がある。私は /etc/rc.local に次のように書いている。

/etc/rc.local:

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


iptables-rusbnat.rules は上記の設定を行った後に iptables-save > iptables-rusbnat.rules で作った。

ここまで一度設定すれば良い。

以下はテザリングを有効にする度にPC側で実行するもの。
Android側ではやることはテザリングの設定を有効にするだけ。
停止するときはAndroidでテザリングの設定を無効にする。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/'`

if [ "$GWA" = "" ]; then
    echo exit
    exit 1
fi

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


なお、AndroidのDNSを再設定するところはブリッジ接続の方法と変わらないので、テザリング中にWiFi/3GをONしない方が良い。
ONにした場合DNSの設定が変わって名前解決できなくなるが、再度 rusbtether-nat を実行すれば解決できるようになる。

#追記 5/3
Android側に付くアドレス 192.168.42.129 は固定のようだ。com.android.server.connectivity.Tethering にハードコーディングされていた。

Google検索にBloggerの最新記事がヒットする速度

Bloggerで記事を投稿した30秒後に記事中のキーワードで検索をかけたら、その記事がトップに出た。26400件ヒットしたにもかかわらず。
こういう検索アルゴリズムの仕様に少し疑問もあるが、15へぇくらいだ。

2011/04/29

Androidの通信量の節約方法を考える

近いうちに Softbank を解約して b-mobile Fair に移行すべく、通信量を節約する方法を模索している。
今までは自宅でもWiFiを使わずに3Gで通信していたこともあり、月300-400MB程度使っていた。
目標は 平日8MB/日、休日4MB/日。これで120日使うと900MB。1GBまでの残り100MBはたまに使うGoogle Mapやメール添付イメージなどのバッファとして考えている。
b-mobile U300で通信料制限なしという選択肢もあるが、通信速度は私にとってとても重要なことだ。遅いとイラッと来るので。

現在通信量のうち多くの割合を占めるものは以下の3つ。

1) 通勤中のRSSリーダ、Webブラウザ使用。たぶん通信量の8割を占める。
2) Android Marketでのアプリのアップデート、新しいアプリのダウンロード。
3) Gmailの待ち受け、取得、写真送受信。メイン携帯へのメールもGmail(auone)で取得。

- RSSリーダ対策

自動更新はOFFにする。RSSリーダ(NewsRob)は、出勤前にキャッシュしておく。
手動で更新するのは割りと苦にならない性格なので。

会社では技術調査の名目で社内LANに接続したPCにスマフォを接続している。Firewallを越えるにはProxyに対応したアプリが必要。そこで、プロキシに対応した Sparse rss というのがあったが、Google Readerの同期機能がない。残念。
Google Reader にフィードを集約しているので、個人的に対応は必須要件。

結局探したところで、Proxy対応したアプリが使いやすいとは限らないので、Proxy越えをする要rootedな別の方法を選択した。後日検証する予定。

- Webブラウザ対策

Webブラウザを使うときは Opera Mini を使う。Opera Mini はOpera用のサーバが仲介して、参照先のWebページを圧縮してからブラウザに送るため、普通のダイレクトに接続するタイプのブラウザよりも通信量が結構少ない。
画質の設定を最低・携帯表示ONにすると、広告等の画像の多いページの通信量がかなり軽減される。私の場合、ほとんどが文字情報を見たいだけだったりするのでこれで十分。

ただ、Opera Miniは仲介サーバにつながらないことがある。そんなときは仕方なくOpera MobileまたはDolphin Browser Miniを使っている。

#追記 5/8
Opera MobileでOpera TurboをONにすると、Opera Miniと同様に圧縮される。


- Android Market対策

自動更新をOFFにして、自宅でアップデート&インストールする。
アプリのバージョンアップでデグレードすることもあるため、自動更新はリスクが高いと思っている。
なので元々自動更新はしていない。この作業も苦にならない。

- Gmail対策

メーラはK9-MailでIMAP Idleを使用。待ち受けだけだと、それほど通信量は多くなさそうだが、今度念のため計測しておきたい(計測した)。
たまに取った写真を送ったり、送られたりするのが通信量が大きい。
送信するときは SmallPic で小さくしてから。
受信した添付ファイルは、すぐに見る必要のないものは自宅でWiFiやPCで確認する。
K9-Mailは受信するメッセージのサイズを指定できるので、8kにしている。

#追記 5/3
- 広告/情報収集対策

無料アプリの中にはAdMobなどの広告を取得するものがあるので、通信系のアプリではなくても起動する度に広告をダウンロードすることがある。よく起動するアプリならば、広告なしの有料版に変更したほうが通信量を節約できる。
また、アプリ使用状況の情報収集のためにデータを送信するアプリもある。データ送信することをユーザに許可を求めるアプリが送信するのは構わないが、WiFiのときだけという設定がほしい。
パケットを眺めていたら、ToggleSettings無料版はwww.togglesettings.comと通信するのでアンインストールした。CyanogenModのウィジットボタンで事足りてしまったので。有料版は使ったことがないが、どうなのだろうか。

Ubuntuのアップグレードを促すダイアログに驚く

Ubuntuで作業している最中、突然Ubuntu11.04へアップグレードするかどうかを聞くダイアログ(結構大きめ)が表示された。これには驚いた。
さらにそのダイアログのデフォルトのフォーカスが「今すぐアップグレードする」だったことにも驚いた。

このインターフェースは不親切だと思った。
パネルから小さな枠の通知を表示するだけにするとか、他にスマートで効果的な方法があったはず。
さすがにこれはナシだ。

気を取り直して自動起動するアプリからアップデート通知をはずした。

chkconfig対応のsquid init script

Ubuntuでapt-getでインストールしたらinitスクリプトがなかったので。
起動時にDNSテストをスキップする"-D"が指定されていることに注意。

/etc/init.d/squid:

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

# description for chkconfig: 345 98 20
#  345: run levels
#  98: priority to start
#  20: priority to stop

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/squid
NAME=squid
DESC="squid server"
PIDFILE=/var/run/$NAME.pid
test -x $DAEMON || exit 0

if [ -f /etc/default/$NAME ]; then
    . /etc/default/$NAME
fi

DAEMON_OPTS="-D"

start_proc() {
    $DAEMON $DAEMON_OPTS
    if [ "$?" -eq 0 ]
    then
        pidof $DAEMON > $PIDFILE
        return 0
    fi
    return 1
}

stop_proc() {
    kill -9 `cat $PIDFILE 2>/dev/null`
    [ "$?" -ne 0 ] && return 1
    rm -f $PIDFILE
    return 0
}

status_proc() {
    killall -0 $DAEMON 2>/dev/null
    return "$?"
}

case "$1" in
    start)
        echo "Starting ${DESC}"
        start_proc
        [ "$?" -eq 0 ] || echo "failed"
        ;;
    stop)
        echo "Shutting down ${DESC}"
        stop_proc
        [ "$?" -eq 0 ] || echo "failed"
        ;;
    restart|force-reload)
        echo "Restarting ${DESC}"
        stop_proc
        if [ $? -ne 0 ]; then
        failure ; echo ; exit 0
        fi
        sleep 1
        start_proc
        [ "$?" -eq 0 ] || echo "failed"
        ;;
    status)
        status_proc
        [ $? -eq 0 ] && echo "$DESC is alive" || echo "$DESC is dead"
        echo
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
        exit 1
        ;;
esac
exit 0

2011/04/28

Android MarketでDownload pausedが発生したときの対処

逆USBテザリングをするために色々弄っていたのが悪かったのか、WiFi接続時にAndroid Marketからアプリをインストールできなくなってしまった。

現象としては、"Download paused" (日本語で"ダウンロードが一時停止しました") が表示され、ずっとダウンロードが終わらない。3Gに切り替えると正常にダウンロード&インストールはできる。
WiFiネットワークにファイアウォールはない。また、logcat にExceptionやそれらしいメッセージは出ないので原因究明のきっかけを掴めない。
最終的には原因不明のまま、メニューのプライバシーにあるデータの初期化を行って回復した。

少し調べたら同じ現象の人がいるようで、

  • 再起動する。
  • Marketアプリのキャッシュ、データをクリアする。
  • Marketアプリにアカウントを再登録する。
  • Google Talkに接続する。

を試してみた。しかし、現象は変わらず。
Titanium Backupでデータの復元はできるので、結局Factory Resetを選択した。

#追記 5/8
  • Google Talkに接続して、すでにログインしている場合はログアウト・ログインする。
    Google TalkとAndroid Marketの認証・通信の仕組みは共有されているのだろうか。
    PCからMarketサイトでインストールを実行した後、WiFiに接続しているAndroidでダウンロードが始まらないときは、これをやると始まる。
  • /cacheの容量が少ないとダウンロードが開始しないようだ。/cacheのパーティションサイズを変更したとき、大きなファイルはダウンロード開始しなかった。不足しているなら不足しているというエラーを出してくれればいいのに、エラーが出ない。

Bloggerにテキストファイルを投稿するスクリプト

このブログのほとんどの記事をLinuxからGoogleCLで投稿している。
記事のタイトルとタグをいちいちコマンド引数に渡すのが面倒なので、投稿するファイルから拾うスクリプトを作成して使っている。

実行方法:
$ blogger-post file1.txt file2.txt ...

投稿ファイルのサンプル (UTF-8):

記事のタイトル
tag1,tag2
本文
本文
本文


blogger-post:

#!/usr/bin/perl
use utf8;

my $blog = "My Blog";
my $tf = "/tmp/blogger-post.tmp";

while (my $f = shift) {
    print "Posting $f ...\n";
    open(IN, "<:utf8", $f) || die "Cannot open $f : $!\n";
    my $title = <IN>;
    chomp $title;
    (length($title) > 0) || die "Title is empty\n";
    my $tags = <IN>;
    chomp $tags;
    open(OUT, ">:utf8", $tf) || die "Cannot write to $tf : $!\n";
    while (<IN>) {
        print OUT;
    }
    close IN;
    close OUT;
    my $r = system("google blogger post --blog=\"$blog\" ".
        "--draft -n \"$title\" -t \"$tags\" $tf");
    unlink $tf;
    exit 1 if ($r ne 0);
}


$blog は対象のブログ名。