仙石浩明の日記

Android

2011年1月6日

香港で Hutchison (3HK) SIM カードと Galaxy S (GT-I9000) を買ってみた

2009年の年末に香港に行ったときは、 Hutchison と CSL の SIM を比較して CSL を選んだが、 単に CSL の HK$1000 Prepaid SIM の有効期限が 2年だった (フツーは 6ヶ月) ことに魅力を感じたからで、 データ通信自体を比較したわけではなかった。 そこで今回は Hutchison Telecom Hong Kong (和記電訊, 3HK) の 3G Int'l Roaming Rechargeable SIM Card (3G 國際漫遊循環儲值咭) HK$98 (約1080円) を買うことにした。

3HK 3G Int'l Roaming Rechargeable SIM

今回の訪港は夜のフライトで、 しかも向かい風 (偏西風) が強くて到着が大幅に遅れ、 香港国際空港に着いたのは 23:00 過ぎ。 出発ロビーの 3Shop (3HK の店) も 1010 Centre (CSL の店) も既に閉ってる (正確に言えば到着ロビーに着いた時点で 23:30 だったので行くのを断念)。

ホテルへのバス (パックツアーなので空港⇔ホテルの送迎が含まれてる) の中で 24:00 を過ぎたので、 さっそく Nexus One (2009年の年末に購入した HK$1000 Prepaid SIM を入れてある) で 「179179」 にダイヤルして、 CSL の Mobile Broadband Service の 5-day pass を申し込む (HK$178, 約1960円)。

CSL の Mobile Broadband Service (データ通信サービス) は 24:00 が課金の区切りとなるので、 24:00 前に申し込んでしまうとそれが一日目とカウントされてしまう (例えば 23:00 に使い始めると、 1-day pass なのに 24:00 までの 1時間しか使えない恐れがあるが、 本当にそうなるのか恐くて実験できない ^^;)。 もっと早い時間に香港に着く場合だと、 最初の日に Mobile Broadband Service を申し込むべきか悩ましいことになる。
その点、 3HK SIM だと、 24:00 区切りなのは CSL と同じだが、 一日の上限 HK$28 (約310円) に達するまでは HK$2/MB の従量課金なので、 何時に着こうが迷わずデータ通信を開始することができる。
CSL の 1-day pass は HK$50 (約550円) なので 3HK の HK$28 が圧倒的に安く感じられるが、 5日間で考えると CSL の HK$178 に対し、 3HK は HK$28*5 = HK$140 と、 差が縮まる。

で、ホテルに着いて Nexus One を WiFi AP (ポータブル Wi-Fi アクセスポイント, Android 2.2 の標準機能) として使う。 ホテルの部屋でも無線LAN サービスが提供されているのだが、 一日の利用料が HK$100 (約1100円) とバカ高いので、 滞在中はずっと Nexus One をアクセスポイント代わりに使った。 ノートPC 2台 (前回前々回、香港で購入した NetBook) と Android 端末 2台 (今回購入した Galaxy S と IDEOS U8150-B) でこの 「WiFi AP」 を利用したが、 常に安定して利用できた。 しかも日中は (当然だが) スマートフォンとして使えるわけで、 これならモバイル WiFi ルータ専用機なんて要らない。

翌日、 旺角へ行って適当に歩いていて見つけた 3Shop (亞皆老街40-46號) で
3G Int'l Roaming Rechargeable SIM Card (3G 國際漫遊循環儲値咭) HK$98 を買う。
そして、 そこからほど近い電器店、 百老滙 Broadway (西洋菜南街78號) で Galaxy S (i9000) を買った。

もっと読む...
Filed under: Android,SIM,香港 — hiroaki_sengoku @ 09:36
2010年12月31日

香港の深水埗で華為 IDEOS U8150-B (EMOBILE S31HW) を買ってみた

年末に香港へ行って電脳街を巡るのが毎年の恒例行事になってきた。 香港の電脳街は、 すでに秋葉原を追い越した感じがする。 近頃の秋葉は面白いモノをあまり見かけなくなり、 たまに見かけても、 大抵は香港などで流行ってるモノを単に持ち込んだだけで、 しかも値段は香港の倍以上だったりする。 デフレと言われて久しい日本だが、 PC にしろケータイにしろ値段が高すぎる。 これから発展していくアジアと、 これから衰退していくだけの日本ということなのか。

いつものように深水埗の高登電腦中心を巡回していたら、 大一(香港)有限公司IDEOS U8150-B を HK$1380 で売っているのを見つけた。 U8150-B といえば来年1月中旬にイー・モバイルから発売される予定の Pocket WiFi S (S31HW) の同型機。 ただし、 ファーウェイによれば、 グローバル展開されている 「IDEOS」 と日本向け S31HW は別の製品、 ということらしい。 なぜ日本は 「グローバル展開」 の対象ではないのだろう?

「Pocket WiFi S」は、 音声通話機能とAndroid™ 2.2を搭載したWi-Fiルーターです。 Android™ 2.2搭載端末としては国内最安※1となる端末価格19,800円(税込)での提供と、 国内最軽量※2の重さ約105gサイズを実現しました。

「国内最安」 「国内最軽量」 つまり世界基準では安くもなければ軽くもないということ。 本来 Android は 2.2 froyo 以降からデフォルトで 「ポータブルWi-Fiアクセスポイント」 として使えるのに、 わざわざ 「Wi-Fiルーター」 と呼んでいるあたりにも、 いかに日本が世界標準からずれてきてしまっているかを感じる。

とはいえ、 日本では 19,800円でしか買えない端末が HK$1380 (約 14,500円) で買えるのなら悪くない。 思わず衝動買いしてしまった。 画面が QVGA (2.8" 320x240) だったり、 内蔵カメラが貧弱 (一応 320万画素なのだが...) だったり、 CPU が MSM7225 528MHz だったりと、 Android 端末としては見劣りするものの W-CDMA I/II/V/IX (2100/1900/850/1700MHz) に対応したスマートフォンが 15,000円弱で買えるとなると、 一気に普及が進むヨカン。 ちょうど 2年前、 ここ香港で 4バンド GSM 機を HK$899 (約 10,000円) で買ったのが遠い昔のことのようだ。

中華 Android 端末なんて 「安かろう悪かろう」 だろうなどと思ってはいけない。 カスタマイズしすぎて Android とは別物になりつつある国産スマートフォンより、 よほど素直で使いやすい (そして圧倒的に安い)。 買った当初は値段相応の期待しか持っていなくて、 メイン端末が壊れたときなどの非常用端末くらいの感覚だったのだが、 高解像度の画面 (およびマルチタッチ) を必要としないアプリのほとんどがそのまま動いてしまった。 これなら非常用としてだけでなく、 大きな端末を持ち歩きたくない時とかにも使えそう。 Android 2.2 へアップデートできない一部の国産 Android 端末だと、 動かないアプリが沢山でてきてしまう。

ただし、 多くのアプリ開発者が QVGA などという尋常でない低解像度を想定していないためか、 Android マーケットでダウンロードできないアプリが少なからずある。 そういうアプリも別の Android 端末でダウンロードして得た apk ファイルを、 「adb install」 コマンドなどで IDEOS へインストールしてみると、 ほとんどが特に問題無く使えるようだ。

購入時のファームウェアは、

Androidバージョン2.2
ベースバンドバージョン2210
カーネルバージョン2.6.32.9-perf
huawei@product #1
ビルド番号U8150V100R001C127B818SP05

だったが、 この 「ビルド番号」 だと、 日本の一部(?)のキャリアの SIM でデータ通信できないという問題がある (音声通話は可能)。 例えば、 ドコモの SIM で mopera U を使う場合は 3G 通信可能だが、 ソフトバンクの銀SIM/黒SIM や、 WILLCOM CORE 3G の SIM では接続できなかった。

Huawei のページで U8150 のファームウェアを検索してみると、

ProductSoftware NameRelease Date
U8150U8150 V100R001C02B82 7SP02(Italy Vodafone ) 2010-12-15

が見つかった。 Italy Vodafone と書いてあるあたりが気になるが、 google で検索してみると、 このファームウェアを書込んだら 3G 通信できるようになった、 という旨のページを見つけた:

huaweidevice.comのサイトから、 「U8150 V100R001C02B82 7SP02(Italy Vodafone ) Host Software」 をダウンロードして解凍し、 出てきたファイルをmicroSDにコピーして、 ボリューム上と終話ボタンを押しながら電源を入れればファームアップが始まります。 一度目最後に失敗したら一度電池を抜いてもう一度同じ操作をすれば、ファームアップが正常に完了です。
ファームアップ後は、 まっさらに初期化されるため、 最初から設定しなおしです。 とはいえ、Andorid2.2とマーケットアプリのおかげで、 Googleアカウントを入れれば、 アプリも全部自動で勝手にマーケットからダウンロードして復活してくれるので楽です。 ファームアップ後のrooted も可能です。

さらに、 元のファームウェア V100R001C127B818SP05 も Huawei サイトからダウンロードできるようなので、 最悪の事態になっても元に戻せるだろうと思い、 上記ファームウェアを IDEOS に書込んでみた。 ファームウェアに同梱されている 「U8150 Software Upgrade Guideline」 に書込み方法の説明がある (英語版とイタリア語版の説明書が同梱)。

こばこのひみつ」 に書かれている通り、 書き込みの最後で 「失敗」 するのだが、 いったん電池を抜いて再起動すると正常に起動した。 また RageAgainstTheCage exploit を利用して root 権限を再取得できた。

更新後のバージョンは以下の通り:

Androidバージョン2.2
ベースバンドバージョン22201003
カーネルバージョン2.6.32.9-perf
huawei@product #1
ビルド番号U8150V100R001C02B827SP01

ダウンロードしたファームウェアのファイル名は末尾が 「...7SP02」 だったのに、 ビルド番号が 「...7SP01」 なのが気になるが、 ソフトバンクの銀SIM や WILLCOM CORE 3G SIM で試してみたところ、 正常に 3G 通信できている。

Filed under: Android,香港 — hiroaki_sengoku @ 01:11
2010年12月4日

Android 端末 IS01 のカーネルを入れ替えてみた 〜 さよならデッカード LSM

先週末 IS01 で root 権限が必要なアプリが使えるようになったばかりなのに、 そのわずか 4日後、 スマートフォン@2ch掲示板に以下の書き込みがあり、 カーネル空間への侵入口が明らかにされてしまった。 一番乗りを果たした goroh_kun さんに敬意を表しつつも、 IS01 のプロテクトがこの程度だったことが残念でもある。 「root を取られても大丈夫な作りになっている」 と開発者が豪語し、 しかも IS01 の発売から 5ヶ月間も破られなかったのだから、 さぞかし鉄壁の守りなのだろうと思っていたのに、 こんな分かりやすい穴があったとわ... (負け惜しみ ^^;)。

【ROM焼き】au IS01 root2 〜わたくし達も未来へ〜

...

317 :goroh_kun:2010/12/01(水) 03:14:21 ID:LGLTLBmZ

自動起動仕込むところを大体見つけました。
どなたか協力お願いします。

/data/の直下にlocal.propを置く
ここで、内容をoverrideできます。
サービス起動時のおダイナミックライブラリがある
サービスプログラムを探す。

getpropしてみると、

rild.libpathが/system/lib/libril-qc-1.soといかにも起動時に使っていそう。
rildaemonから使われているプラグインと思われます。

そこで、
local.propの中身を
rild.libpath=/data/lib/libril-wrapper.so
rild.wrapper.cmd=/data/rootkit.boot.sh
rild.libpath2=/system/lib/libril-qc-1.so

で、このプラグインを/data配下から読み出されるようにして、
libril-wrapper.soにて、ダイナミックロード時に
自動実行したいプログラムを指定できるようにします。
で、自動実行したいプログラムをlibril-wrapper.soで実行後
本来のrild用のプラグインをロードして、rildに引き渡すようにします。

これでいけるのではないかと思っているのですが、やるよって方は
いらっしゃいますか?

rild.libpath プロパティは、 RIL (Radio Interface Layer) デーモン rild が参照している。 rild は rild.libpath の値をダイナミックライブラリのパス名として dlopen(3) に渡し、 dlsym(3) でライブラリ内の関数 RIL_Init 呼び出す。 つまり、 rild が起動される前に rild.libpath の値を書き換えておけば、 rild に任意のプログラムを起動時に実行させることが可能になる。

したがって守る側 (つまり開発者の立場) から考えると、 (1) rild.libpath が書き換えられないようにするか (2) 任意のプログラムの実行が脅威にならないよう、 rild 起動前に防御を固めてしまえばよい。

プロパティは、 以下の 4 つのファイルで設定できる。 どれか一つでも改変可能であれば、 (1) の方法は使えない。

  • /default.prop
  • /system/build.prop
  • /system/default.prop
  • /data/local.prop

IS01 の場合、 (たとえ root が奪取されても) /system ディレクトリ下は改変できないようになっている。 また、 /default.prop は initramfs 内のファイルなので、 改変しても再起動すれば揮発してしまう。 だから上記 4ファイルのうち /data/local.prop 以外は改変不可能。

Android では /system ディレクトリ下は read only でも構わないが、 /data ディレクトリ下は Android 動作中に書き換えることが前提となっている。 もちろん /data/local.prop だけは書き換え不可能にするとか、 あるいは再起動時に自動的に /data/local.prop を書き戻す、 という方法も考えられなくもないが、 そもそも論で言えば /data/local.prop は、 システムのプロパティをオーバーライドするための設定ファイルであって、 その本来の趣旨からいえば書き換え不可能にすることは望ましくない。

したがって守るなら (2) の方法が自然。 というか、 /data/local.prop で rild.libpath の値を変更されても、 rild はユーザ空間で動くデーモンなのだから、 本来は 「任意のプログラムの実行」 が可能でも脅威にはならないはず。

ところが IS01 の開発チームは致命的なミスを犯した。

もっと読む...
Filed under: Android — hiroaki_sengoku @ 12:59
2010年11月27日

月額8円で運用できる Android 端末 IS01 で、root 権限が必要なアプリを使えるようにしてみた

Android 搭載スマートブック IS01 by SHARP (愛称 「メガネケース」) が、 本体価格 0円 + 契約事務手数料 2835円 + 月々 8円 * 2年しばり = 3027円 で入手できるとネット上で話題になっていたので買ってみた。 メジャーアップデート対応なしと公式に宣告されてしまった IS01 ではあるが、 「ワンセグ放送が視聴できて WiFi 通信もできるポメラもどき」 と割りきったとしても激安なので 「ふたつでじゅうぶんですよ」 と言われつつ 3つ入手した。

「グローバル展開もしていないから xda の助けも得られない」 と総統閣下に嘆かれつつも、 RageAgainstTheCage exploit を利用すれば root になることは一応できる (ベースバンドバージョン / ビルド番号 01.00.07 で確認):

senri:/home/sengoku % adb push rageagainstthecage /data/local/tmp/
senri:/home/sengoku % adb shell
$ cd /data/local/tmp
$ chmod 755 rageagainstthecage
$ ./rageagainstthecage
[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C

[*] checking NPROC limit ...
[+] RLIMIT_NPROC={1856, 1856}
[*] Searching for adb ...
[+] Found adb as PID 8223
[*] Spawning children. Dont type anything and wait for reset!
[*]
[*] If you like what we are doing you can send us PayPal money to
[*] 7-4-3-C@web.de so we can compensate time, effort and HW costs.
[*] If you are a company and feel like you profit from our work,
[*] we also accept donations > 1000 USD!
[*]
[*] adb connection will be reset. restart adb server on desktop and re-login.
$ 
senri:/home/sengoku % adb shell
# 

ただし rageagainstthecage はタイミングが良くないと失敗するので、 少なくとも数回は繰り返し実行しないと root が取れない (運が悪いと何度実行しても成功しない上に adb で接続できなくなって再起動する羽目に陥ることも)。 IS01 を再起動するたびに rageagainstthecage を実行して root を取り直すのは著しく手間なので、 root が取れたら su コマンドをインストールしておくべき。

インストール先は、 一般ユーザ (shell 権限) でアクセス可能で、 かつ再起動しても消えない (つまり tmpfs とかでない) ボリュームで、 かつ mount 時に nosuid オプションをつけられていないことが必須条件になるが、 幸い IS01 ではフラッシュメモリ /dev/block/mtdblock7 が /sqlite_journals に nosuid 無しで mount されている:

senri:/home/sengoku % adb shell
$ grep -v '\(tmpfs\|nosuid\)' /proc/mounts
rootfs / rootfs ro 0 0
devpts /dev/pts devpts rw,mode=600 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
/dev/block/mtdblock7 /sqlite_journals yaffs2 rw 0 0
/dev/block/mtdblock5 /system yaffs2 ro 0 0
$ ls -l /sqlite_journals
drwxrwx--x system   system            1980-01-06 09:00 com.android.providers.settings
drwxrwx--x system   system            1980-01-06 09:00 com.android.providers.settingsex
drw-rw-rw- root     root              2010-11-26 18:51 lost+found

sqlite_journals というディレクトリ名からして SQLite 用のディレクトリと思われるが、 それならなぜ nosuid がつけられていないのだろうか? ガチガチに防御を固めているはずの IS01 にしては珍しい抜け穴の一つ。

ちなみに上記のように /dev/block/mtdblock5 も /system ディレクトリに nosuid オプション無しでマウントされているが、 こちらはカーネルの MTD ドライバで書き込みが禁止されている (SHARP が公開している IS01 のカーネル kernel/drivers/mtd/devices/msm_nand.c 参照) ので、 簡単には変更できそうにない (少なくとも rw で remount するだけでは書込めない)。

というわけで /sqlite_journals に bin ディレクトリを作って ChainsDD の su コマンド (android_system_extras に含まれる) を置いてみる。

# ls -l /sqlite_journals/bin/su
-rwsr-sr-x    1 root     root         26256 Aug 17 17:27 /sqlite_journals/bin/su

ところが!

もっと読む...
Filed under: Android — hiroaki_sengoku @ 15:46
2010年11月13日

iモード.net と imoten を使って iモードメールを自動送信する 〜 SubEtha SMTP のバグ

iモードメールを Android 端末で送受信するには、 sp モードiモード.net を利用する。 前者は sp モード専用の APN (Access Point Name) spmode.ne.jp に接続しないと利用できない (つまり無線LAN 経由では使えない) 上に、 ドコモが IMEI (International Mobile Equipment Identity, 端末識別番号) をチェックしていて、 契約端末でないとこの APN に接続できない (らしい)。 なので私は後者を利用している。

iモード.net は、 PC から iモードメールを読み書きできる Webメールサービス (月額210円)。 PC の代りに Android 端末上のアプリ (iMoNi が有名) からアクセスすれば、 Android 端末で iモードメールを読み書きできる。 あるいは iモード.net からメールを読み取って別のメールアドレス (例えば Gmail) へ転送するプログラムを (どこかのサーバで) 動かしておけば、 iモードメールを Android 端末で普通のメールとして受信できる。

前者の方法は、 メールの新着をどうやって知るか、 という問題がある。 Android 端末上のアプリが定期的に iモード.net にアクセスして新着をチェックすることになるが、 電池の消耗を考えればせいぜい 15分に一度チェックするのが限界で、 それ以上の頻度でチェックするのは現実的ではない。 つまり最大 15分間は新着を知るのが遅れる。 iMoNi にはドコモから送られてくる WAP PUSH を検知してメールをチェックしにいく機能もあるが、 3G 接続中は (なぜか) WAP PUSH を受け取れないらしい。

それに対し後者の方法は、 転送プログラムは Android 端末上で動くわけではないので、 電池のことを心配せずに iモード.net を頻繁にチェックできる。 iモード.net で取得したメールを Gmail へ転送するようにすれば、 Android 端末は Gmail の着信をリアルタイムで知ることができる。

前者の方法は Android 端末だけで iモードメールを読み書きできるのに対し、 後者の方法は転送プログラムを動かすサーバ (24時間稼働が望ましい) が必要になるので万人向けではないが、 そういったサーバを確保できるのなら、 iモードメールを普通のメールと同様に扱える後者の方法が圧倒的に便利。

というわけで、 私は imoten (imode.netのメールをSMTPで転送するプログラム) を使っている (まだ使い始めて 2日だが)。 1分も遅れずに Android 端末で着信を知ることができるので、 とても便利。

imoten には iモードメールを送信する機能もある。 すなわち imoten が SMTP サーバとして振る舞い、 受け取ったメールを iモード.net へ転送する。

ということはつまり Android 端末から iモードメールを送ることができるのはもちろん、 PC 上のメーラや任意のメール配信プログラムから iモードメールが送ることが可能。 試しにテストプログラムを書いてみた:

#!/usr/bin/perl
use strict;
use warnings;
use Net::SMTP;

my $smtp = Net::SMTP->new('senri.gcd.org', Port => 42525, Debug => 1);
$smtp->auth('imoten', 'xxxxxxxx');
$smtp->mail('sengoku');
$smtp->to('sengoku@gcd.org');
$smtp->data();
$smtp->datasend("To: sengoku\@gcd.org\n");
$smtp->datasend("\n");
$smtp->datasend("A test message\n");
$smtp->dataend();

imoten を動かしているサーバ senri.gcd.org の 42525 番ポートに SMTP 接続してメールを送信するプログラム。 imoten は SMTP PLAIN 認証をサポートしているので、
「$smtp->auth('imoten', 'xxxxxxxx');」 で、
ユーザ 「imoten」、 パスワード 「xxxxxxxx」 (伏字) を指定している。

ところが!

もっと読む...
Filed under: Android — hiroaki_sengoku @ 08:19
2010年10月25日

Android 端末 (Nexus One) でアプリを SDカードの ext3 パーティションにインストールする (Apps 2 SD)

新しい Android 端末が次から次へと発表される今日このごろ、 1月6日に発表された Nexus One は (まだ一年たっていないのに) 旧型機といった感じになりつつある。 特に端末内部メモリ容量が 200MB しかないのは致命的。 正確に言えば内蔵フラッシュメモリは 512MB だが、 システム側で 300MB ほど使うので、 ユーザが自由に使えるのは残り 200MB 程度となる (もちろんそれとは別に SD カードが使える)。

いまどき 200MB というのはいかにも少ない。 例えば私が Nexus One で使ってるアプリのうち、 サイズの大きいものを一部ピックアップしてみると、

アプリappdata合計
Google Earth20.32MB0.24MB20.56MB
Twitter2.70MB14.57MB17.27MB
Skype10.68MB3.40MB14.08MB
OpenWnn Flick対応版7.68MB4.78MB12.46MB
Adobe Flash Player12.40MB0.00MB12.40MB
Google Map8.09MB1.44MB9.52MB
Graffiti5.14MB0.84MB5.22MB
K-9 Mail2.67MB1.07MB3.74MB

わずか 8個のアプリだけで 100MB 近く使っている。 200MB に収めようと思えば、 インストールするアプリを相当厳選する必要がある (100個程度で限界)。

さすがにこれでは使い物にならないということで、 アプリを SDカード上にインストールできるようにする仕掛け Apps 2 SD (A2SD) が提案されてきた。 A2SD には App2sd など、 いろんな実装があるが、 基本的には SDカードに ext3 (あるいは ext4) パーティションを切り、 /system/ext あたりにマウントして (実はこれが難しい, 後述)、 /data/app などからシンボリックリンクを張る。

つまり、こんな感じ:

# cat /proc/mounts
	...
/dev/block/mtdblock3 /system yaffs2 ro,relatime 0 0
/dev/block/mtdblock5 /data yaffs2 rw,nosuid,nodev,relatime 0 0
	...
/dev/block/mmcblk0p2 /system/ext ext3 rw,nosuid,nodev,relatime,errors=continue,data=writeback 0 0
/dev/block/vold/179:1 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
/dev/block/vold/179:1 /mnt/secure/asec vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
	...
# cd /data
# ls -l
drwxrwxr-x    1 system   system        2048 Oct 23 19:33 anr
lrwxrwxrwx    1 root     root            15 Oct 23 20:45 app -> /system/ext/app
lrwxrwxrwx    1 root     root            23 Oct 23 20:46 app-private -> /system/ext/app-private
lrwxrwxrwx    1 root     root            18 Oct 23 20:47 backup -> /system/ext/backup
lrwxrwxrwx    1 root     root            24 Oct 24 11:23 dalvik-cache -> /system/ext/dalvik-cache
drwxrwx--x    1 system   system        2048 Oct 24 11:59 data
drwxr-x---    1 root     log           2048 May 13 08:47 dontpanic
lrwxrwxrwx    1 root     root            17 Oct 23 20:02 local -> /system/ext/local
drwxrwx---    1 root     root          2048 May 13 08:47 lost+found
drwxrwx--t    1 system   misc          2048 Oct 24 14:02 misc
drwx------    1 root     root          2048 Oct 23 19:33 property
drwxrwxr-x    1 system   system        2048 Oct 24 14:20 system
lrwxrwxrwx    1 root     root            22 Oct 23 21:11 tombstones -> /system/ext/tombstones

アプリを Android 端末にインストールすると、 パッケージファイル (*.apk) が /data/app ディレクトリに格納 (「コピー防止」 アプリは /data/app-private ディレクトリに格納) され、 クラスファイル (classes.dex) が /data/dalvik-cache ディレクトリに格納される。 上記のように /data/app および /data/dalvik-cache はそれぞれ /system/ext/app および /system/ext/dalvik-cache へのシンボリックリンクにしてあるので、 内蔵フラッシュメモリ (/data パーティション) の代わりに SDカード (/system/ext パーティション) へ格納される、という仕掛け。

/data/data ディレクトリにはアプリが使用するデータが格納される。 /data/app および /data/dalvik-cache が、 アプリのインストール/アンインストール時のみ書き込みが行なわれるのに対し、 /data/data はアプリ動作中も読み書きが行なわれるわけで、 /data/data を SDカードへ移すとアプリの実行速度が低下したり、 あるいは頻繁な書込みによって SDカードの寿命が短くなったりする恐れがある。 もし /data/data も SD カード上に置こうとするなら、 使っている SDカードが充分高速 (Class6 以上?) で、 かつウェアレベリング (wear leveling) をサポートしているか確認したほうが無難。

私は /data/data は /system/ext へのシンボリックリンクを張らずに /data パーティションをそのまま使用している。 現時点での各パーティションの空き容量は次のような感じ:

# df
/dev:               197552K total,       0K used,  197552K available (block size 4096)
/mnt/asec:          197552K total,       0K used,  197552K available (block size 4096)
/system:            148480K total,  127580K used,   20900K available (block size 4096)
/data:              200960K total,  102148K used,   98812K available (block size 4096)
/cache:              97280K total,    9128K used,   88152K available (block size 4096)
/system/ext:       3690832K total,  269908K used, 3420924K available (block size 4096)
/mnt/sdcard:      11547432K total, 6808720K used, 4738712K available (block size 8192)
/mnt/secure/asec: 11547432K total, 6808720K used, 4738712K available (block size 8192)

私が使ってる SD カードは 16GB Class6 だが、 /data パーティションはまだ 99MB の空きがあるので /data/data を無理に /system/ext へ移動させることもないと判断した次第。

なお、Android 2.2 froyo から OS 標準で、 アプリを SD カードへ移動できるようになった。 しかし対応アプリでないと移動できないし、 現時点では多くのアプリが対応していない。 さらに 「USBストレージをON にする」 と SD カードへ移動したアプリは USB ストレージが ON の間は使えなくなるので注意が必要。

というわけで、 アプリを SDカード上にインストールできるようにするのは、 シンボリックリンクを /data から /system/ext へ張るだけのことなので全く難しくない。 しかしながら、 どうやって SDカード上の ext3 パーティションをマウントするか、 という問題がまだ残っている。

もっと読む...
Filed under: Android — hiroaki_sengoku @ 09:56
2010年7月26日

Nexus One で Android 2.2 froyo のマルチタッチを試してみる

Android は 2.1-update1 以降でマルチタッチ (Multi-touch) をサポートしている。 ところがマルチタッチといっても、 ピンチイン/ピンチアウトなどのジェスチャをサポートしているだけのアプリが大半で、 複数のタッチを独立に扱えるアプリはいまだほとんどなく、 iPhone と比べるとその差が際立っている。

どうして Android にはマルチタッチを活用したアプリケーションが無いのだろう? と思ったので、 マルチタッチを試すテストアプリ MultiTouch.java (apk) を書いてみた:

MultiTouch

タッチした位置にタッチの強さに応じた大きさの円を表示するだけの単純なアプリ。 指を移動すれば円も追随する。 Android ではタッチID が順に割り振られるので、 ID が 0 のタッチを赤色の円で、 ID が 1 のタッチを緑色の円で描いている。

プログラム上は ID が 2 のタッチを青色の円で描くことになっているが、 残念ながら現行の Android で同時に扱えるタッチは 2箇所のみ (追記: Samsung Galaxy S は 5箇所のマルチタッチが可能らしい) なので、 3箇所にタッチしても三つ目の円が描かれることはない。 だから例えば iPhone のアプリにあるような鍵盤楽器アプリを作ろうと思っても、 三つ以上の音を同時に鳴らすことはできない。

とはいえ、 2箇所のタッチを独立に扱えれば、 いろいろ応用が効くだろうにと思いつつ、 このテストアプリをいじっていると...

もっと読む...
Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 08:57
2010年7月17日

Nexus One の近接センサ/環境光センサは、どこにあるのか?調べてみた

Nexus One など最近のスマートフォンには、 加速度 (Accelerometer)、 環境光 (照度, Ambient Light)、 磁場 (磁界, Magnetic Field)、 方位 (電子コンパス, Orientation)、 近接 (Proximity) など、 様々なセンサがついている。 いろいろ応用できそうで夢がふくらむが、 携帯電話本来の使い方 (つまり通話すること) において、 使い勝手に直接影響する重要なセンサが近接センサ。

Nexus One や iPhone など全面タッチパネルの携帯電話だと、 (受話器として使うために) 耳に近づけたときタッチパネルが反応しては困る。 そこで近接センサを使って顔が接近してくることを感知し、 タッチパネルを無効にする (ついでにディスプレイをオフにして消費電力を抑える)。

私は Proximity なんて聞くと、 Proximity Warning System (接近警報システム) を思い浮かべてしまうくらいで、 携帯電話用の近接センサがどういうしくみか全く知らなかった。 今年1月の Nexus One の発表の時に近接センサのことを初めて知り、 その時はタッチパネル全体への接近を感知する (静電容量の変化を検知して?) のかと想像したが、 後述するように Nexus One の近接センサはタッチパネルの左上にしかなく、 タッチパネルの下方への接近は感知できないことが分かった。

Nexus One のどこに近接センサが搭載されているか、 センサの感応範囲がどれくらいなのか、 私には見当もつかなかったし、 google で検索してもその手の情報は見つからなかったので、 近接センサが感知した値を表示するだけの簡単なプログラムを書いてみた。

実は、 私にとって初めての android アプリ (^^;)。 しかも、 ここ数年 Java から遠ざかっていたので、 久々に書く Java プログラムだったりする。

お膳立ては Android SDK が全てやってくれるので、 わずか 74行のプログラム。 まず SensorManager#getSensorList メソッドで、 PROXIMITY タイプのセンサを取得し (sensor)、

	sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
	List<Sensor> sensors
	    = sensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
	Sensor sensor = sensors.get(0);

この sensor の値が変化したときなどにセンサの値を受け取るリスナ (SensorEventListener) を、 SensorManager#registerListener メソッドで登録するだけ。

public class Proximity implements SensorEventListener {
	...
	sensorManager.registerListener(this, sensor,
				       SensorManager.SENSOR_DELAY_NORMAL);
	...
    @Override
    public void onSensorChanged(SensorEvent event) {
	view.update(event.values);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

近接センサの値が変化するとリスナの onSensorChanged メソッドが呼び出されるので、 新しいセンサ値を描画する (view.update):

	void update(float[] values) {
	    Canvas canvas = getHolder().lockCanvas();
	    if (canvas == null) return;
	    canvas.drawColor(Color.WHITE);
	    Paint paint = new Paint();
	    paint.setColor(Color.BLACK);
	    paint.setTextSize(40);
	    float height = paint.getTextSize();
	    for (int i = 0; i < values.length; i++) {
		canvas.drawText(" "+values[i], 0, height * (i + 1), paint);
	    }
	    getHolder().unlockCanvasAndPost(canvas);
	}

Nexus One のタッチパネル左上隅近く (黒枠部分) にセンサがあるらしく、 パネルまで 2cm ほどの距離に物体を近づけると反応する (センサの値が 9.0 から 0.0 へ変化する)。 また、 パネルと並行に物体を動かす場合、 センサの真上から 1cm ほど外れると反応しなくなる。

Proximity Sensor  ←  鈴を近づけたことにより、
近接センサが反応して、
値が 0.0 になっている

赤外線型の近接センサ (赤外線を照射し、近接する物体からの反射光を測定するセンサ) なので、 凸面の物体など赤外線があさっての方向へ反射してしまって受光素子に正しく届かない場合や、 あるいは黒色の物体などあまり反射しない場合などでは、 より近づけないと反応しない。

例えば、 黒く細い丸棒などだと 1cm 以下に近づけないと反応しない。 逆に白い紙 (凹面〜平面) など、 効果的に赤外線を反射し、かつ受光素子に反射光が効率的に届くケースだと、 2cm より遠くても (8cm くらいでも) 反応する。

ちなみに、 Nexus One に搭載されている近接センサは、 Capella MicrosystemsCM3602 という、 環境光センサ付短距離近接センサ (Short Distance Proximity Sensor with Ambient Light Sensor) であるようだ。 名前の通り環境光も測定できる。 おそらく近接センサの受光素子をそのまま使って照度を測定しているのだろう。

前述したプログラムにおいて、 「Sensor.TYPE_PROXIMITY」 を 「Sensor.TYPE_LIGHT」 に置き換えれば、 環境光センサの値を読み取ることができる。

もっと読む...
Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 08:55
2010年7月8日

Google カレンダーの過去の予定を自動的に削除する方法

ここ 2ヶ月ほど、 Willcom の HYBRID W-ZERO3 から Nexus One への移行を徐々に進めてきた。 Windows Mobile (HYBRID W-ZERO3) は PC とのデータ同期が基本なので、 PC にスケジュールや電話帳など丸ごと入れておけば済むが、 Android (Nexus One) の場合は 「クラウド」 との同期が基本なので一筋縄にはいかない。 つまり 「クラウド」 に全てのスケジュールを置いていいのか? という問題。 私の場合、 「スケジュール」 といいつつ会議の議事メモから個人的な日記まで、 プライベートな情報を全て集積しているので問題がより深刻になる。

ちなみに私は、 1999年に WorkPad 30J を使い始めて以来、 プライベートなデータを PDA / スマートフォンに集積してきた。 今まで使ってきた PDA / スマートフォンをまとめてみる:

購入月機種OS
1999-04 WorkPad 30J PalmOS 3.1J
2000-03 TRGpro PalmOS 3.5
2000-08 Palm m100 PalmOS 3.5.1
2002-09 Zaurus MI-E1 ZaurusOS
2002-12 Linux Zaurus SL-C700 Linux 2.4 Embedix
2003-06 Linux Zaurus SL-C750 Linux 2.4 Embedix
2006-07 W-ZERO3[es] Windows Mobile 5.0
2007-07 Advanced W-ZERO3[es] Windows Mobile 6 Classic
2008-12 HTC P3600 Windows Mobile 5.0
2010-01 HYBRID W-ZERO3 Windows Mobile 6.5 Professional
2010-04 Nexus One Android 2.2 froyo
2010-06 iPhone 4 iOS 4
2010-11 IS01 Android 1.6 donut
2010-12 Galaxy S Android 2.2 froyo
2010-12 IDEOS U8150-B Android 2.2 froyo
2011-03 Nexus S Android 2.3 gingerbread
2011-05 nook color Android 3.0 honeycomb
2011-12 Galaxy Nexus Android 4.0 ice cream sandwich
2012-12 Galaxy S3 Android 4.0 ice cream sandwich
2013-07 Galaxy Mega 5.8 Android 4.2 jelly bean
2013-11 Nexus 5 Android 4.4 kitkat
2014-03 iPhone 5s iOS 7.0
2016-01 Nexus 5X Android 6.0 marshmallow
2016-12 ZenFone 3 ZS570KL Android 6.0.1 marshmallow

WorkPad 30J から HYBRID W-ZERO3 に至るまで、 全て PC とのデータ同期が基本だったし、 それぞれデータ移行ツールが用意されていたので移行は容易だった。 ところが Nexus One で同じような同期を行なうには、 データを PC ではなく Google Calendar へ置かなければならない。

もちろん、 Google Calendar は共有設定さえ行なわなければ他人に読まれることはないだろうし、 「don't be evil」 と言ってるくらいだから、 Google が勝手にユーザのデータを活用する可能性も無い (と信じたい)。

だからといって、 個人的なデータや会社の超機密事項 (議事メモにはそういった情報も含まれる) も洗いざらい Google に預けてしまう、 なんてことは小心な私にはとてもできない。 よく知られているように Google Calendar は 「限定公開 URL」 が漏れるだけで一巻の終わりであるわけで、 漏れることを前提でリスク評価すべき。

というわけで、 議事メモや日記を Google Calendar に置くことはハナからあきらめて、 Google Calendar には直近の予定だけを置くことにした。 万一漏れても、向こう一週間くらいの予定だけであれば、 致命的というほどでもない。

ところが驚いたことに Google Calendar には過去の予定を一括削除する機能がない。 手作業でいちいち消していかない限り、 データは残り続けるようだ。 当たり障りのない 「予定」 でも積み重なればいろいろ見えてくることがあるわけで、 長年にわたって溜った予定データは脅威となりうる。

どうしてこんな超基本機能が無いのだろうと思いつつ google で検索してみると、 見つかるのは 「どうやったら過去のデータを (一括) 消去できるのか?」 という質問のページばかり。 過去データを削除したい、 というニーズは確実にありそうなのに、 なぜ Google は実装しようとしないのか? そういえば gmail も過去のメールを溜め続けるのが基本だし、 Google Calendar に自動的に削除する機能がないのは意図的なのかもしれない。

無い機能は作ってしまえと、 Google Calendar API のドキュメントを眺めてみる。 API を叩くためのクライアントライブラリが用意されているようだ。 が、.NET とか Java とか Python とか PHP とか、 あまり気の進まない (^^;) 言語ばかりが並んでいる。 Perl は無いのかっと思って CPAN を検索したら、 Net::Google::Calendar があっさり見つかった。

マニュアル片手にテストプログラムを書いてみる:

use Net::Google::Calendar;
use DateTime;
my $cal = Net::Google::Calendar->new;
$cal->login('sengoku@gmail.com', 'xxxxxxxx');
for my $event ($cal->get_events('start-max' => DateTime->now
				- DateTime::Duration->new(days => 7))) {
    $cal->delete_entry($event);
}

たったこれだけ。 最初に login($user, $pass) して、 get_events(%opts) で「予定」データを取り出して、 delete_entry($event) で削除。 get_events の引数で、 開始時刻が一週間以上過去の予定のみ取り出すよう指定している。

ちょっと書き足して、 ユーザID やパスワードを設定ファイルで設定できるようにしたスクリプト gcal_remove を作ってみた。

% gcal_remove -c /path/to/config.yaml -d 7

などと実行すると、 7日間以上前の予定を削除する。

設定ファイル config.yaml は以下のような感じ:

google_user: sengoku@gmail.com
google_passwd: xxxxxxxx
Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 09:44
2010年5月26日

サイボウズ オフィス8 のカレンダーを iCalendar 形式に変換するスクリプトを書いてみた

私の職場ではグループウェアとしてサイボウズを利用している。 自分のスケジュールを iCalendar 形式で取得してみたくなった (Nexus One を買うとそういう気分になる ^^;) ので、 google で検索してみたところ次の二つのスクリプトが見つかった:

あいにく職場のサイボウズはオフィス8 なので、 オフィス8 に未対応の前者は使えない。 後者はオフィス8 用だが、 「暫定版」 と書いてある通りいろいろバグがある。 さくっと修正してみて一応それっぽく動かすことはできたのだが、 使い続けていくとなると一から書き直したほうがいいような気がしてきた (わたし的にはこの手のものを PHP スクリプトでは書きたくない) ので、 perl で書き直してみた

この perl スクリプト cybozu8_ical を、

% cybozu8_ical --conf /path/to/config.yaml

などと実行すると、 オフィス8 の 「月予定」 (月間スケジュール) ページをアクセスして、 iCalendar 形式に変換する。 向こう一週間以内の予定については 「予定の詳細」 ページもアクセスして、 「メモ」 および 「設備」 も取得する。 「月予定」 の全ての予定について 「予定の詳細」をアクセスすると、 オフィス8 サーバに負荷をかけすぎる懸念があったので、 このような仕様にしてみた。

設定ファイル config.yaml は以下のような感じ:

cybozu_url: http://intra.klab.org/cgi-bin/klcb/ag.cgi
calname: cybozu
userid: 73
password: xxxxxxxx
time_zone: Asia/Tokyo
input_encoding: shiftjis
output_file: /home/sengoku/tmp/cybozu.ics

「cybozu_url」 はオフィス8 の URL を指定する (もちろん intra.klab.org は KLab 社内LAN からしかアクセスできない)。 「userid」 と 「password」 はスケジュールを取得したいユーザの ID とパスワード。 「output_file」 に指定したファイルへ iCalendar 形式のスケジュールを出力する。 「output_file」 を省略すると標準出力へ書き出す。

なお 「--conf /path/to/config.yaml」 オプションを省略すると、 cybozu8_ical と同じディレクトリにある config.yaml が読み込まれる。

cybozu8_ical で生成した iCalendar 形式のファイルは、 とりあえず Google カレンダー および Thunderbird + Lightning で読み込めることは確認したが、 なにぶんまだ RFC 2445 を真面目に読んでいないので、 不具合などあったらご指摘頂けると有難い。

私はオフィス8 の API を知らないので、 「月予定」 「予定の詳細」 のページを取得してきて scrape しているだけ。 「繰り返し予定」 は個々の予定として扱っている。 iCalendar の UID (各予定固有のID) が同じままだと 2個目以降の予定が無視されてしまうので、 UID の末尾に通し番号をつけて互いに区別できるようにしている。

サイボウズのスケジュールは 「場所」 を登録できない。 その代わり 「施設」 として会議室を登録する。 ところが私の職場の場合 「施設」 には会議室だけでなく 「ビデオ会議システム」 などもあったりするので、 「施設」 に登録されているデータをそのまま iCalendar の LOCATION として使うわけにもいかない。 そこで 「施設」 に会議室の名称が登録されている場合のみ、 その会議室名を LOCATION として出力するようにしている。

iCalendar の LOCATION として出力すべき会議室名の一覧を、 cybozu8_ical スクリプトの始めの方で、

my @facility = (
	'20F 大会議室1', '20F 大会議室2', '20F 大会議室3',
	'20F 中会議室1', '20F 中会議室2',
	'20F 小会議室1', '20F 小会議室2',
	'20F 和室', '22F 社長室前MTGスペ-ス'
);

などと定義している。 ここに列挙されていない会議室等は 「施設」 に登録されていても単に無視される。

Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 16:01
2010年5月3日

米国のプリペイド SIM カードで Nexus One を使ってみた

私は米国ではプリペイド SIM カードを購入して携帯電話を使っている。 AT&T の Pay As You Go $1 Mobile to Mobile Plan は、 携帯電話同士の通話が 1日 1ドルで使い放題になるので便利。 一方データ通信は、 米国だとホテルをはじめ多くの場所で無線LAN が使えるので、 今までは必要性をあまり感じなかったのだが、 今回のハワイ滞在で Nexus One を入手したら、 携帯電話でもデータ通信をしてみたくなってしまった。

日本でも HTC Desire が発売された今となっては 「いまさら」 感もあるが、 SIM ロック フリーな Nexus One を入手する意味はあると思い、 渡米前に Web サイトで Nexus One を購入し (日本からは、 このサイトへアクセスできないと書いてあるページを見かけるが、 トップページ以外は普通にアクセスできるような...?)、 配送先として滞在する予定のホテルを指定しておいた (配送先に日本の住所は指定できない)。 ただし米国のホテルはテロを警戒して、 非滞在者宛の荷物の受け取りを (事前連絡しても) 拒否するそうなので、 購入手続きは出発直前に行なった。
日本を発つ日 (フライトは晩の 20:50) の早朝 4:57 JST (12:57 PDT) に購入手続きを行なったら、 その翌日 0:02 JST (8:02 PDT) に発送され (FedEx overnight)、 ハワイ到着日の翌日昼間 13:30 HAST にホテルに配達された (日本時間でいうと購入日の 2日後の朝 8:30 JST)。 出発日の前日に購入手続きを行なっていれば到着日に受け取れたかもしれないが、 ホテルにチェックイン (12:00 HAST) する前に配達されるとマズイかも?

ホテルのフロントで Nexus One の小包を受け取って、 SIM を入れて電源 ON したら、 勝手に 3G (W-CDMA) でデータ通信を始めてしまった (当たり前 ^^;)。 Pay As You Go (AT&T のプリペイドSIM) は、 Data Package を追加購入せずにデータ通信を行なうと、 1KB あたり 1セントも課金されるので一瞬で (例えば、わずか 10MB で約1万円) パケ死してしまう。 慌てて無線LAN に切り替えたが既に 87セント課金されていた。 さらに、 位置情報を取得するために 1セント (基地局ID の取得?)、 無線LAN 経由だと google アカウントの同期が行なえなかった (ホテルの LAN の問題?) ので 3G でつなぎ直したら 29セントかかった。 この時点で残度数 $22.73 (SIM カード利用開始時は $25)。

Pay As You Go Online で、 「Login」 ボタンの上にある 「Forgot your password?」 をクリックすると 「Request Your Password」 ページに遷移するので、 AT&T プリペイド SIM の携帯電話番号を入力して 「Send Password」 ボタンを押す。 すると、

1111509064: S: Your account status has changed.
ATT Free Msg: Your password has been changed to 8181.

などといった SMS (ショートメッセージサービス) が送られてくる。 このメッセージ中に書いてある password を使ってログインし、 任意の password に設定し直せばよい。 いったんログインできるようになれば、 米国内外を問わず Web 上で度数の追加 (refill) や追加パッケージ (feature packages) の購入が可能。

追加パッケージには、 Unlimited Talk & Text ($60 で通話と SMS が 30日間使い放題), Messaging Packages ($19.99 で SMS 無制限), Data Packages (データ通信), Night and Weekend Minutes Package ($19.99 で通話と SMS が夜間・週末無制限) があって、 Data Package は、 1MB ($4.99) と 100MB ($19.99) のいずれかを選べる。

Nexus One をいじっていたらすぐ 100MB くらいはいくだろうと思い 100MB を購入した。 実際、 google map をナビ代わりに使う (海外旅行ではすごく便利!) と、 20MB くらいはすぐ使ってしまう。 また google アカウントとの自動同期を行なうと、 何もしなくても一日 1MB くらいは使ってしまうようだ。 あっというまに 100MB を使い切りそうなので、 Data Package 100MB を追加購入することにした。

追加パッケージは、 Credit/Debit カードで支払うこともできるが、 SIM カードの残度数で払うこともできる。 つまり、 いったん度数を購入 (refill) してから、 その度数で追加パッケージを購入するということができる。 なんでそんな回りくどいことを?と思うかも知れないが、 refill だと SIM カードの有効期限を延長できるという違いがある。 例えば $100 refill すると有効期限が 1年後になる ($25〜$75 だと 90日後、$15 だと 30日後)。

有効期限は追加できないので、 例えば有効期限が 3ヶ月後の場合 (新規に SIM カードを購入した場合など) に、 $100 refill したからといって有効期限が 15ヶ月後 (=3ヶ月+1年) になるわけではなく、 refill した時点の 1年後が有効期限になる。 有効期限はもちろん短くはならない。 例えば $15 refill だと有効期限は 30日後だが、 refill 以前の有効期限が 3ヶ月後なら、 3ヶ月後のままで変わらない。
一方、 refill 以前の度数も新しい期限まで有効期限が延長される。 つまり有効期限に達する前に refill し続ければ、 度数を失効させずに済む。 ただし一枚の SIM カードに溜められる度数は $500 が上限。

というわけで、 最初に購入した Data Package 100MB の残りがわずかになった段階で、 $100 refill し、 その度数から $19.99 を使って Data Package 100MB を追加購入した。

Filed under: Android,Hawaii,SIM — hiroaki_sengoku @ 18:04
« Newer Posts