Android

2015年06月17日

Android で今後ネイティブ実行形式を扱う際に注意すべきこと

はてなブックマークに登録

2010年の年末、このブログに以下の記事を掲載しました。

Android NDK でネイティブ CUI プログラムを書く!

当時は Android 2.3 を搭載した機種がぽつぽつ出始めたころでした。2015年6月現在の最新バージョンは Android 5.1.1 であり、Android 5.0/ 5.1 (Lollipop) からは PIE (Position Independent Executable) 以外のネイティブ実行形式がサポート外となったため注意が必要です。

$ ./hello 
error: only position independent executables (PIE) are supported.

PIE にはプロセス空間上のどのアドレスに配置されてもメモリ上のアドレステーブル等を書き換えることなくそのまま実行できるという特長があり、アドレス空間レイアウトのランダム化(ASLR Address Space Layout Randomization)と組み合わせて利用すればプロセス上の所定のアドレスを想定したセキュリティ攻撃への対策として有効とされています。(ただし、PIE の生成・実行にはコンパイラと実行環境がこれに対応している必要があります)

A look at ASLR in Android Ice Cream Sandwich 4.0 - www.duosecurity.com

先年公開した Google Play ストア上のアプリ「stone for Android」には パケットリピータプログラム「stone」のネイティブ実行形式を梱入しています。ここしばらく Android から手が離れていたのですが、利用者の方からこのアプリが Android 5.0 環境で正常に動作しない旨のフィードバックを受けて上の事情を知り調査と対応を行いました。Lollipop で非 PIE を実行できないことは随所で言及されているものの、後方互換性やアプリでの対処方法に関する具体的な話題は意外と見あたらないため備忘をかねて以下に情報を控えます。

まずは動作確認

まず各プラットフォームで首実検を行いました。現時点で最新の NDK-r10 を使って stone と小さな "hello!" プログラムの PIE 版と 非 PIE 版をビルドしました。それを Android 各バージョンの ARM エミュレータ上で実行した結果を以下に示します。

NDK用 プロジェクト一式 : stone & hello

https://github.com/mkttanabe/stone-Android-NDK/stone

https://github.com/mkttanabe/stone-Android-NDK/hello
hello/jni/hello.c
#include <stdio.h>

int main()
{
    puts("hello!");
    return 0;
}
hello/jni/Android.mk
※太字 2行で PIE をビルド、外せば非 PIE をビルド
LOCAL_PATH := $(call my-dir)

TARGET_PIE := false
NDK_APP_PIE := false

include $(CLEAR_VARS)

LOCAL_CFLAGS += -fPIE
LOCAL_LDFLAGS += -fPIE -pie

LOCAL_MODULE    := hello
LOCAL_SRC_FILES := hello.c

LOCAL_LDLIBS += -ldl

include $(BUILD_EXECUTABLE)
hello/jni/Application.mk
APP_PLATFORM := android-19

# build for arm
APP_ABI := armeabi

# build for x86
#APP_ABI := x86

各プラットフォームでの実行結果

  • Android 5.0 (Lollipop) 以降は PIE のみに対応しており非 PIE の実行はブロックされる
  • Android 4.1 (Jelly Bean) 〜 Android 4.4 (KitKat) では PIE / 非 PIE のいずれも実行可
  • Android 4.0 (Ice Cream Sandwich) 以前のすべてのプラットフォームでは PIE を実行することができない
hello hello (PIE) stone stone (PIE)
5.1.1 (API 22) Lollipop "Not
supported"
OK "Not
supported"
OK
5.0.1 (API 21) "Not
supported"
OK "Not
supported"
OK
4.4W.2 (API 20) KitKat OK OK OK OK
4.4.2 (API 19) OK OK OK OK
4.3.1 (API 18) Jelly Bean OK OK OK OK
4.2.2 (API 17) OK OK OK OK
4.1.2 (API 16) OK OK OK OK
4.0.3 (API 15) Ice Cream
Sandwich
OK segfault OK segfault
4.0 (API 14) OK segfault OK segfault
3.2 (API 13) Honeycomb OK segfault OK segfault
3.1 (API 12) OK segfault OK segfault
3.0 (API 11) OK segfault OK segfault
2.3.3 (API 10) Gingerbread OK segfault OK segfault
2.2 (API 8) Froyo OK segfault OK segfault
2.1 (API 7) Eclair OK segfault OK segfault
1.6 (API 4) Donut OK segfault OK segfault
1.5 (API 3) Cupcake OK segfault OK segfault

5.1.1 (API 22) 環境で実行時の様子

root@generic:/data/local/tmp # ./hello 
error: only position independent executables (PIE) are supported.

root@generic:/data/local/tmp # ./hello_pie                                   
hello!

root@generic:/data/local/tmp # ./stone
error: only position independent executables (PIE) are supported.

root@generic:/data/local/tmp # ./stone_pie                                   
May  1 10:40:28.556080 start (2.3e) [375]
May  1 10:40:28.570865 stone 2.3e  http://www.gcd.org/sengoku/stone/
May  1 10:40:28.572752 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
May  1 10:40:28.573123 using OpenSSL 1.0.1j 15 Oct 2014 http://www.openssl.org/
Usage: ./stone_pie <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt

4.1.2 (API 16) 環境で実行時の様子

root@android:/data/local/tmp # ./hello                                       
hello!

root@android:/data/local/tmp # ./hello_pie                                 
hello!

root@android:/data/local/tmp # ./stone
May  1 19:50:32.740789 start (2.3e) [647]
May  1 19:50:32.756406 stone 2.3e  http://www.gcd.org/sengoku/stone/
May  1 19:50:32.759034 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
May  1 19:50:32.760995 using OpenSSL 1.0.1c 10 May 2012 http://www.openssl.org/
Usage: ./stone <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt

root@android:/data/local/tmp # ./stone_pie                                   
May  1 19:50:40.402476 start (2.3e) [651]
May  1 19:50:40.412950 stone 2.3e  http://www.gcd.org/sengoku/stone/
May  1 19:50:40.413214 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
May  1 19:50:40.415012 using OpenSSL 1.0.1c 10 May 2012 http://www.openssl.org/
Usage: ./stone_pie <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt

4.0.3 (API 15) 環境で実行時の様子

# ./hello
hello!

# ./hello_pie
[1] + Stopped (signal)        ./hello_pie
[1]   Segmentation fault      ./hello_pie

# ./stone
May  1 10:53:22.265410 start (2.3e) [112]
May  1 10:53:22.423641 stone 2.3e  http://www.gcd.org/sengoku/stone/
May  1 10:53:22.425563 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
May  1 10:53:22.425869 using OpenSSL 1.0.0e 6 Sep 2011 http://www.openssl.org/
Usage: ./stone <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt

# ./stone_pie
[2] + Stopped (signal)        ./stone_pie
[2]   Segmentation fault      ./stone_pie

アプリでの対処のしかた

上の結果の通り、Android 4.0 以前の環境では PIE を実行することはできません。そのため、ネイティブ実行形式を含むアプリケーションを Lollipop 以降の環境と Ice Cream Sandwich 以前の環境の両方に対応させるためには相応の対処が必要となります。

方法 1 : PIE と 非 PIE のふたつを使い分ける

stone for Android の場合、アプリに内蔵しているネイティブの実行形式は stone 本体のみで、そのファイルサイズは 140KB 程度です。そのため アプリに PIE 版と非 PIE 版の両方を持たせ、起動時に当該プラットフォーム用の実行形式を切り出して使用するというもっとも素朴な対応を選びました。
※このアプリは現時点では ARM 専用です

stone-for-Android/src/jp/klab/stone/stone.java#L103

                         :

    String MyDir = mUtil.GetMyResFileDirectory();
    if(Build.VERSION.SDK_INT >= 21) { // LOLLIPOP = android 5.0
        // only Position Independent Executables (PIE) are supported
        mUtil.ExtractMyResFile(R.raw.stone_pie, "stone", MyDir, "744");
    } else {
        // for backward compatibility
        mUtil.ExtractMyResFile(R.raw.stone, "stone", MyDir, "744");
    }
                         :

方法 2 : "run_pie" ユーティリティを利用する

run_pie は Android 4.0 (Ice Cream Sandwich) 環境で PIE を実行することを目的に Chromium プロジェクトの primiano@chromium.org 氏が開発したラッパプログラムです。手元にあるもっとも旧い Android 1.6 環境で試したところ、このツールを利用することで所定の PIE を正常に実行することができました。これをアプリに同梱し ICS 以前の環境で PIE を実行する際には そのフルパスと引数を run_pie へ渡してラップしてやれば互換性のジレンマをスマートに解決できそうです。

Android 1.6 環境で実行した様子
(直接 PIE を実行すると異常終了、run_pie 経由なら OK)

# /data/local/tmp/hello_pie2
[1] + Stopped (signal)        /data/local/tmp/hello_pie2
[1]   Segmentation fault      /data/local/tmp/hello_pie2

# ./run_pie /data/local/tmp/hello_pie2
hello!

#  /data/local/tmp/stone_pie2
[1] + Stopped (signal)        /data/local/tmp/stone_pie2
[1]   Segmentation fault      /data/local/tmp/stone_pie2

# ./run_pie /data/local/tmp/stone_pie2
Jun 17 08:02:07.942424 start (2.3e) [212]
Jun 17 08:02:07.950579 stone 2.3e  http://www.gcd.org/sengoku/stone/
Jun 17 08:02:07.950843 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
Jun 17 08:02:07.951066 using OpenSSL 0.9.8h 28 May 2008 http://www.openssl.org/
Usage: /data/local/tmp/stone_pie2 <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt
run_pie のソースは以下の場所にあります。
Index of /trunk/src/tools/android/run_pie - src.chromium.org

run_pie でラップして実行する PIE は以下のフラグを付与してビルドする必要があります。

LOCAL_CFLAGS +=-fvisibility=default -fPIE
LOCAL_LDFLAGS += -rdynamic -pie

※ビルドした PIE は Android 5.0 以上の環境ではもちろんそのまま実行できます

ビルドずみの run_pie 本体と上記のフラグでビルドした stone, hello の PIE を以下の場所に置いています。

https://github.com/mkttanabe/stone-Android-NDK/run_pie

run_pie 開発者による書き込み:
Issue 373219: Android binaries must now be PIE (but ICS doesn't support them) - code.google.com

Project Member     Reported by primi...@chromium.org,    May 14, 2014

Recent versions of Android require our native binaries 
(forwarder, md5sum, adb_reboot, purge_ashmem, memdump) to be PIE.
However, the same binaries must be also able to run on our bots 
running previous versions of Android all the way down to ICS.
Sadly, ICS doesn't seem to support PIE (see b/6587214 and crbug.com/147832).
                       :
3) I managed to write a "run_pie" wrapper (cl coming soon) for supporting
PIE on ICS. The idea is to just wrap commands with run_pie /actual/binary args.
The (small) price to pay is two extra gyp flags (only for android tools exe
targets) to force the PIE executable to export "main", so the pie wrapper
can dlsym it (at least, as long as we'll support ICS).

run_pie 利用者からのコメント:
Issue 888: Blocking non-PIE binaries breaks the ABI - code.google.com

#9 androtu...@gmail.com   Nov 7, 2014

I've been shipping executable in my apps for about 4 years. Now I build all
binaries with PIE option and bundle a "run_pie" non-PIE binary for older
Android versions.

This are the build option for all binaries except the run_pie:

LOCAL_LDLIBS := -pie -rdynamic
LOCAL_CFLAGS := -fPIE -fvisibility=default

When PIE will be used as default build option, I'm not sure which option
will disable it so I can still build the run_pie binary.

So far it seems to be working fine for all my users. 

That's where I found the run_pie information:
https://code.google.com/p/chromium/issues/detail?id=373219

方法 3 : Multiple APK を使用する

Google Play ストアには、同一のアプリケーションについて所定の環境に対しそれぞれ実体の異なる所定の apk を配布することのできる Multiple APK という機能があります。Multiple APK を使用すると apk を開発・管理する上でのシンプルさが損なわれるため Google は可能な限り Single APK を使うことを推奨しており、下記ドキュメントの最初の注釈には「only when your APK is too large (greater than 50MB) 」とコメントを添えています。ただし、何らかの積極的な動機があればこの手法もまた PIE/ 非 PIE とプラットフォームの間の不整合を解決するための選択肢のひとつとなり得るかも知れません。

公式ドキュメント:

Multiple APK Support - developer.android.com
参考訳:
(tanabe)

klab_gijutsu2 at 20:24|この記事のURLComments(3)TrackBack(0)
2014年10月15日

Dropbox アカウントひとつで利用できるプッシュ通知機構

はてなブックマークに登録

Dropbox 社は広く知られるファイル系のサービスとは別に 2013年より非ファイル形式の構造化データの保存・読み出しに対応するデータストアサービスを公開しており、Dropbox アカウントを持っていれば Dropbox Datastore API 経由でこのサービスを利用できます。同 API は全体的にシンプルで SDK のサポート範囲も広いため自作のソフトウェアへ手軽に組み込むことが可能です。
自前でサーバ環境を構築・運用する手間なしにレコードイメージのデータをネットワークストレージ上で取り回せるのは便利で、また多くの人がアカウントを持っていることへの安心感もあり、この Dropbox のデータストアサービスはさまざまな用途に柔軟に活用できそうです。
Dropbox データストアの弱点としてストア上のデータのアカウント間での共有に未対応である点がしばしば挙げられていましたが、2014年9月に同社は New Datastore features! として Shared datastores のリリースをアナウンスしています。

Dropbox Datastore API のデータ変更通知機能

Dropbox Datastore API はアプリケーションをオフラインで操作した後のデータの自動同期や競合の解決を行うための機能や、ストア上のデータに変化があった際にそれをアプリへ通知するといった BaaS(Backend as a Service)風味の機能を備えています。

後者はリスナとして登録した所定の関数をデータ変更発生時に API がコールバックするしくみで、そこでの通知を適切にハンドリングすることによりあるインスタンスが行ったデータ変更を他のインスタンスへすみやかに反映することが可能です。その実装例を Dropbox Datastore API の JavaScript SDK 付属のサンプルアプリケーションで試すことができます。公式のリンクを以下に掲載します。

最初に表示される「Link to Dropbox」ボタンを押下して自分の Dropbox アカウントでログインすると UI が開きます。二台の PC の web ブラウザを使って操作を行うとわかりやすいでしょう。
※このアプリはログインしたアカウントのデータストア以外にはアクセスしません

データストアの通知機構を単体で使用?

サンプルアプリの動きはなかなか面白く新鮮に感じられました。反応も早くストアからの変更通知がアプリ内で適切に処理されている様子が見てとれます。

ところで、「データに変更が発生したら変更内容をクライアントへ知らせる」という通知のしくみはこのデータストアの機能の構成要素のひとつとして用意された独自の機構であるわけですが、その部分は単独でも実用性があるように思いました。つまり、データストアを本来の記憶領域として使うことよりも、そこでのプッシュ通知のしくみを利用することを主目的とするアプリケーションもあり得るのではないかということです。

単にストア上のデータを更新すれば通知処理が発動するシンプルさも好ましく、送信側は伝えたいメッセージの内容を所定のテーブルのレコードにデータとして乗せることでトリガーを送り、通知を受けた側はその内容に応じて所定の処理を行う形にすればよさそうです。もちろん機能的には専用のプッシュ通知機構に及びませんがメッセージの送受信さえできればあとはアプリ側の工夫次第でしょう。

わざわざそんなことをしなくても所定のプラットフォームのスタンダードな通知機構を使えばよさそうなものですが、そういうことを考えたのには理由があります。プッシュ通知というものはごくプライベートな用途で使いたい場合もあります。たとえば、以前 Android の標準のプッシュ通知機構である GCM を利用して 自宅の遊休端末を留守中の監視カメラWake on Lan マジックパケット発信器として遠隔操作するためのアプリを手がけたことがあります。通知は正しく機能したものの、GCM はこういう小さな要件に軽く使うには必要な手順や手続きにいささか牛刀の感があり、処理用の中間サーバやサーバ用のコードなども面倒に思いながら仕方なく用意していました。

だからと言って自分で似たしくみを作ることは保守・運用面で良い判断とも思えず、よくわからないサードパーティ製品に依存するのも気が進みません。あまり切実でも緊急でもないもののそういう緩いジレンマが時おり首をもたげていたのです。そんなわけで、なるべく手間をかけずシンプルに通知をハンドリングできるものがあればと思っていました。
探せば他にも便利で面白いものが色々ありそうですが、別件でたまたま触れた Dropbox Datastore API の通知のしくみはちょうどそこにあてはまる気がしました。広範なプラットフォームで利用できることにも夢があります。

ブラウザ用アプリを試作

そんなわけで、まずは JavaScript サンプルを土台に手軽に実行できるブラウザ用アプリを作ってみることにしました。「URL プッシュ機能つきオンラインブックマークアプリもどき」とでも言うべきものです。
ブラウザで下記の URL を開き Dropbox アカウントでログインすれば動かしてみることができます。もちろん自分のデータストア以外には一切アクセスしません(と言うかできません^^;)のでご安心下さい。

使い方を図に示します。「選択されたページをこのブラウザで開く」ボックスをチェックしておくと、自分を含むいずれからのインスタンス上で「選択」または「追加」された URL が現在のブラウザで開かれます。

ブラウザ版の動作の様子(動画 39秒 無音)

左:Windows 上の Firefox      右:Mac 上の Safari

Android 用アプリも試作

手元での使用頻度の高い Android 端末用にもアプリを作ってみました。インストール用の QR コードを掲載します。

使い方を図に示します。
Android 版は受信専用です。「通知への待機を開始」ボタンを押下すると自前のサービスが起動し待機状態へ移行します。サービスはシステムから強制終了されにくいフォアグラウンドサービスとしており稼動中は端末の通知領域にアイコンが常駐します。サービスを停止するには「通知への待機を停止」ボタンを押下します。


Android 版への通知はブラウザ版から行います。待機状態の Android 版は通知を受信すると次のように振舞います。
  • 通知内容が http(s)://... の形式なら URL とみなしブラウザへ渡す
  • それ以外なら Android 標準の am (activity manager) コマンドへのパラメータとみなし内容を整形して同コマンドへ渡す

Android 版の動作の様子(動画 60秒 無音)

左:Android 端末実機      右:Mac 上の Safari

付録:am コマンド書式とパラメータ記述例

$ am
usage: am [subcommand] [options]

    start an Activity: am start [-D] [-W] <INTENT>
        -D: enable debugging
        -W: wait for launch to complete

    start a Service: am startservice <INTENT>

    send a broadcast Intent: am broadcast <INTENT>

    start an Instrumentation: am instrument [flags] <COMPONENT>
        -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)
        -e <NAME> <VALUE>: set argument <NAME> to <VALUE>
        -p <FILE>: write profiling data to <FILE>
        -w: wait for instrumentation to finish before returning

    start profiling: am profile <PROCESS> start <FILE>
    stop profiling: am profile <PROCESS> stop

    start monitoring: am monitor [--gdb <port>]
        --gdb: start gdbserv on the given port at crash/ANR

    <INTENT> specifications include these flags:
        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]
        [-c <CATEGORY> [-c <CATEGORY>] ...]
        [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
        [--esn <EXTRA_KEY> ...]
        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
        [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
        [-n <COMPONENT>] [-f <FLAGS>]
        [--grant-read-uri-permission] [--grant-write-uri-permission]
        [--debug-log-resolution]
        [--activity-brought-to-front] [--activity-clear-top]
        [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
        [--activity-launched-from-history] [--activity-multiple-task]
        [--activity-no-animation] [--activity-no-history]
        [--activity-no-user-action] [--activity-previous-is-top]
        [--activity-reorder-to-front] [--activity-reset-task-if-needed]
        [--activity-single-top]
        [--receiver-registered-only] [--receiver-replace-pending]
        [<URI>]
パラメータ記述例
  • start -n jp.klab.nopass/.MainActivity
    - 指定されたアプリのアクティビティを開く
  • start -a android.intent.action.VIEW -d geo:0,0?q="枕崎市"
    - 指定された場所をマップで表示
  • start -a android.intent.action.VIEW -d tel:000000000
    - 指定された電話番号の発呼準備
  • start -a android.intent.action.SENDTO -d mailto:nobody@example.com --es android.intent.extra.SUBJECT テスト --es android.intent.extra.TEXT インテント経由でメールを作ってみたり
    - 指定された宛先・件名・本文の新規メールを生成
  • start -a android.intent.action.VIEW -d vnd.youtube:qpjw62OK9Tw
    - 指定された動画を Youtube で再生

余談ながら、、

今回掲載したアプリ「NotifyIt」は Dropbox 社による事前審査を通過しています。


(tanabe)
klab_gijutsu2 at 14:03|この記事のURLComments(0)TrackBack(0)
2014年08月06日

「パスワードの管理を避ける」という考え方

はてなブックマークに登録

手持ちのアカウントが増えてくるとパスワードの管理方法が考えどころとなります。このところメジャーなネットサービスへの不正ログインやアカウントの悪用が相次いで報じられていることもあり パスワード管理の重要性は「古くて新しい問題」としてあらためて広い層に浸透しつつあります。

その一方でパスワードをきっちり管理することは必ずしも簡単ではありません。平易な内容だと第三者によって類推・導出されるリスクが大きいものの複雑にすると覚えにくい。だからと言って複数のサービスで同じものを使いまわすとそれが漏洩した場合に一斉に攻撃を受ける危険がある。結局、安全度の高いパスワードをサービスごとに使い分けることは人間の記憶だけでは困難なので何らかの外部記憶を利用することになります。ただし、それが何らかのデバイスであれコンピュータデータの形式であれ、情報としてそこへ保存した時点で盗難・流出の可能性はゼロではなくなります。多くの場合、そういった諸々の事情を含んだ上で手元の実用上の便宜とのバランスを判断して管理方法を決めることになりますね。

社内でそういう話をしていた時にふと思いました。一般に秘密文字列の内容は文字の並びとして意味をなさないものであるほど効果的なわけで、そのような情報はあまり「人間向き」とは言えないでしょう。もともと人間向きではない上に内緒にしておきたい情報を人間が丸ごと管理しようとすること自体に無理があるのではないか?その情報を直接管理せずに済ませることはできないか?生々しいパスワードをどこにも保管せずにすむのならそれにこしたことはありません。

ソースコードと実行形式の関係を連想しました。プログラムのソースコードとそれをビルドする環境があればビルドずみの実行形式そのものを保持し続けることは必須ではありません。そのことと同様に所定の情報もまた必要な時に正しく用いることができれば十分であり、それを再現可能とする材料をあらかじめ揃えておけば情報そのものを恒常的に保持することを免れるはずです。
その切り口で方法のひとつを考えてみました。一方向関数の応用です。

  • 利用者が任意のフレーズ (A) を灰色の変換器へ投入すると文字列 (D) が出力される
  • 処理 (B) の変換規則は決め打ちではなく、ランダムな内容で生成ずみの所定のフィルタ (C) を参照して決定される
  • (C) には (D) へ含める文字のバリエーションと (D) の文字列長を定義可能
  • 処理に際し利用者は任意の (C) を指定する
  • 固有の (A) (B) (C) の組合せは固有の (D) を生成する
  • (A) から (D) への変換は不可逆
出力文字列 (D) は意味を持つ内容ではないためパスワード等の秘密文字列として使うことができます。一般的なパスワード生成器等で生成したパスワードは同じものを再発行できないためそれ自体を慎重に保管しておかなければなりませんが、この変換器では固有の (A) (B) (C) が揃っていれば常に固有の (D) を得ることが可能であるためその必要はありません。また、(C) のデータが単体で漏れたとしても正しい (A) (B) がなければ (D) を再現することはできません。さらに、この変換器は機能の性質上基本的に個人用であるため、処理 (B) の内容へ利用者が本人固有の要素を加味することを可能とするといった工夫も考えられそうです。

このようにいたってシンプルな内容ですが、同様のアイディアが形になったものを今のところ知らずこうした話題には興味を感じます。前述のようにパスワードの管理に外部記憶を使わざるを得ない最大の理由は「それがパスワードとして適切な内容であればあるほど人の記憶に馴染まないから」という皮肉な事情にありますが、たとえばこういった方法なら人間の記憶というとても貴重なリソースを最大限に活かせるのではないか?という気がします。

実装例

試みに上のアイディアを Mac OS X 用 / Android 用アプリとして実装してみました。より便利に使うための機能を付加する余地が随所にあるものの この内容でも相応の実用性はありそうです。

  • Mac OS X 版: NoPass100.dmg
    (535,655 bytes md5sum: 95bf7543509f4752b2da204fc545106b)
  • Android 版: NoPass - Google Play ストア
  • ソースコード: NoPass - GitHub

  • アプリでは前述 (C) のフィルタデータを「マップデータ」の呼称で統一しています
  • Max OS X 版は "~/Library/Application Support/NoPass/NoPass.dat" へマップデータを出力します
  • Dropbox 経由で複数の環境から同じマップデータを利用できます
  • Mac OS X 版はマップデータの 使用/ 作成/ 編集/ 削除/ アップロード/ ダウンロード/ 同期 が可能です
  • Android 版はマップデータの 使用/ ダウンロード のみが可能であり Mac OS X 版の併用と Dropbox アカウントが必要です

  • 動作の様子(動画 1分19秒 無音)
  • アプリ画面

(tanabe)
klab_gijutsu2 at 18:01|この記事のURLComments(0)TrackBack(0)
2014年06月11日

Android NDKで使えないシステムコール・ライブラリ関数一覧

はてなブックマークに登録

Android NDKでCのコードを書いていると、普段のCプログラミングでは悩まないことで悩むことがあります。たとえば、AndroidのlibcはGoogle製でPOSIXに準拠していません。他のUnix系環境であれば必ず実装されているライブラリ関数が存在しないなどの罠があるため、メジャーなツールをビルドするのにもconfigure;makeが素直に通らなかったりします。

それだけでなく、Android NDKが提供する開発環境にも問題があります。特に、NDKで配られているヘッダファイルとビルド用の共有ライブラリで対応が取れていないのは頭痛のタネです。どういうことかというと、ヘッダファイルに定義されているシステムコールを使おうと思ったらリンカエラーが出ることがあります。

また、システムコールの一部については、カーネルレベルでは正しく実装されているもののlibcにインターフェース実装がなく、呼びだすのに不便だったりもします。

本稿では、この混乱した状況を多少なりとも整理してみます。根性系の調査結果なので、抜け漏れに気づいた方はぜひ教えてください。

Android NDKとは

まずAndroid NDKについて軽く紹介しておきます。

Android NDK(Native Development Kit)はAndroid用のC/C++開発環境で、中身はAndroid向けのクロスコンパイラ、ヘッダファイル、ライブラリの環境一式です。x86、ARM、MIPS各アーキテクチャ用の実行バイナリや共有ライブラリを作れます。

Androidアプリの開発はJavaで記述し、OSの機能へのアクセスはAndroid SDKが提供するJavaインターフェースを呼び出すのが標準的です。しかし、ネイティブコードを共有ライブラリの形で作成し、これをJavaから呼び出すこともできます。C/C++の資産を活用したり性能を稼いだりする場合のために提供されているのがNDKというわけです。

bionic C library概要

AndroidのカーネルはLinuxがベースになっています。LinuxのノウハウがそのままAndroidで利用できるとすればNDKのメリットも大きくなります。

ところが、ここで問題になるのが冒頭で紹介したlibcの問題です。Androidでは各種Linuxディストビューションで一般的なglibcは採用されておらず、bionic C libraryと呼ばれるGoogle製のlibcが使われています。Googleがわざわざ独自のlibcを作った理由は筆者の理解では2点、性能面とライセンスの問題です。

この2点のうち、性能面はわかりやすい理由でしょう。モバイルOSの場合、サーバー機と比べれば貧弱なCPUと少ないメモリで動作する必要がありますから、オーバースペックなglibcを嫌ってシンプルなlibcを作り直すという選択も理解できます。

もう一点はライセンス上の問題です。筆者も理解度が低いのですが、スマートフォンベンダーやチップセットベンダーが一部のソフトウェア(デバイスドライバ類?)をクローズドソースにすることを許すために、ユーザー空間からGPL・LGPLのソフトウェアを排除する必要があったようです。glibcもuClibcもLGPLなので、新規実装するしか無かったというのも説得力があります。

そんなわけで、多くのソースコードをBSD系から輸入しつつ、Linux系カーネルのシステムコールを呼び、一部は完全に新規実装したキメラ的なプロダクトがbionic C libraryです。今回指摘する内容もこのような出自によるところが大きそうです。

ヘッダファイル中でコメントアウトされている関数一覧

bionic C libraryではモバイルOSとして不要な機能はバッサリ削ってあります。ざっくり言うと、アカウント管理、システム管理などに関連する関数はヘッダファイル中でコメントアウトされており、実体も存在しません。

以下はヘッダファイル中でコメントアウトしてあるシステムコール・ライブラリ関数の一覧です。

  • ctermid(3) - 制御端末名の取得
  • cuserid(3) - プログラムを実行しているユーザー名を取得する
  • endgrent(3) - グループファイルエントリの取得
  • endhostent(3)- ネットワーク上のホストのエントリを取得する
  • endnetent(3) - ネットワークエントリを取得する
  • endnetgrent(3) - ネットワーク・グループのエントリを操作する
  • endprotoent(3) - プロトコルのエントリを取得する
  • execvpe(3) - ファイルを実行する
  • freehostent(3) - ネットワークホストの名前とアドレスの取得
  • getdomainname(2) - NIS ドメイン名の取得・設定をする
  • getgrent(3) - グループファイルエントリの取得
  • getgrgid_r(3) - グループファイルエントリの取り出し
  • getgrnam_r(3) - グループファイルエントリの取り出し
  • gethostbyaddr_r(3) - ネットワーク上のホストのエントリを取得する
  • gethostbyname2_r(3) - ネットワーク上のホストのエントリを取得する
  • gethostent_r(3) - ネットワーク上のホストのエントリを取得する
  • getipnodebyaddr(3) - ネットワークホストの名前とアドレスの取得
  • getipnodebyname(3) - ネットワークホストの名前とアドレスの取得
  • getlogin_r(3) - このセッションにログインしているユーザー名を取得する
  • getnetbyaddr_r(3) - ネットワークエントリを取得する (リエントラント版)
  • getnetbyname_r(3) - ネットワークエントリを取得する (リエントラント版)
  • getnetent(3) - ネットワークエントリを取得する
  • getnetent_r(3) - ネットワークエントリを取得する (リエントラント版)
  • getnetgrent(3) - ネットワーク・グループのエントリを操作する
  • getprotobyname_r(3) - プロトコル エントリを取得する (リエントラント版)
  • getprotobynumber_r(3) - プロトコル エントリを取得する (リエントラント版)
  • getprotoent(3) - プロトコルのエントリを取得する
  • getprotoent_r(3) - プロトコル エントリを取得する (リエントラント版)
  • getpwent(3) - パスワードファイルのエントリの取得
  • getpwnam_r(3) - パスワードファイルのエントリの取得
  • getpwuid_r(3) - パスワードファイルのエントリの取得
  • getsid(2) - セッション ID を取得する
  • getsubopt(3) - 文字列中のサブオプション引き数の解釈を行う
  • innetgr(3) - ネットワーク・グループのエントリを操作する
  • on_exit(3) - プロセスが正常に終了した際に呼ばれる関数を登録する
  • pivot_root(2) - root ファイルシステムを変更する
  • setdomainname(2) - NIS ドメイン名の取得・設定をする
  • setfsgid(2) - ファイルシステムのチェックに用いられるグループ ID を設定する
  • setfsuid(2) - ファイルシステムのチェックに用いられるユーザ ID を設定する
  • setgrent(3) - グループファイルエントリの取得
  • sethostent(3) - ネットワーク上のホストのエントリを取得する
  • sethostname(2) - ホスト名の取得・設定をする
  • setnetent(3) - ネットワークエントリを取得する
  • setnetgrent(3) - ネットワーク・グループのエントリを操作する
  • setprotoent(3) - プロトコルのエントリを取得する
  • setpwent(3) - パスワードファイルのエントリの取得

また、また、下記のセマフォや共有メモリに関するヘッダファイル、および対応するライブラリ関数は存在しません。

  • <sys/sem.h> /* SysV semaphores */
  • <sys/shm.h> /* SysV shared memory segments */
  • <sys/msg.h> /* SysV message queues */
  • <sys/ipc.h> /* General IPC definitions */

これらが削られている理由はNDKのdocs/system/libc/SYSV-IPC.htmlにも書いてありますが、セマフォなどのシステムグローバルなリソースのリークがあった場合に、システムリブート以外の方法で解放できないのがリスクだから、ということのようです。

ヘッダファイルに定義があるのにリンクできない関数一覧

Android NDKで開発していると、ヘッダファイルにプロトタイプ宣言が存在するのにリンカがエラーを出すことがあります。例えば次のような状況になります。

$ arm-linux-androideabi-gcc -Wall /tmp/bcmp-test.c
/Users/hnw/Development/arm-android-19-toolchain/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: /var/folders/_6/384fllzd5ys3mjqgk1xfmrnc0000gp/T//ccUhYkjo.o: in function main:bcmp-test.c(.text+0x34): error: undefined reference to 'bcmp'
collect2: ld returned 1 exit status
$

<strings.h>にbcmpの定義があるのにリンクできないのは理不尽な気がしますが、実際NDKのlibc.soにはbcmpが含まれていないので仕方がありません。このようなシステムコール・ライブラリ関数は以下の通りです。

  • atexit(3) - プロセスが正常終了した時に呼び出される関数を登録する
  • bcmp(3) - バイト列を比較する
  • getw(3) - ワード(int)の入出力
  • malloc_usable_size(3) - obtain size of block of memory allocated from heap
  • mlockall(2) - メモリのロックとロック解除を行う
  • munlockall(2) - メモリのロックとロック解除を行う
  • pvalloc(3) - アラインメントされたメモリの割り当てを行う
  • rindex(3) - 文字列中の文字の位置を示す

また、下記のロケール関連およびワイドキャラクタ系のライブラリ関数も同様の状況です。これらはヘッダ内に「サポートしてないけどlibstdc++-v3のコンパイルを通すために定義してある」的なことが書いてありますが、われわれ一般人からするとライブラリをビルドしたらコメントアウトしておいて欲しい気がします。

  • localeconv(3) - 数値に関する書式情報を得る
  • mblen(3) - 次のマルチバイト文字のバイト数を返す
  • mbtowc(3) - マルチバイト列をワイド文字に変換する
  • towctrans(3) - ワイド文字の変換
  • wctomb(3) - ワイド文字をマルチバイト列に変換する
  • wctrans(3) - ワイド文字変換マッピング

これらはconfigureが混乱する原因になることがあります。例えば<locale.h>の存在チェックに成功するとロケール関連をデフォルトで有効にするものがありますので、明示的にconfigureオプションで無効にする必要があったりします。

NDKのlibcには存在しないためリンクエラーになるシステムコール一覧

システムコールはカーネル側で実装されており、カーネル側はほぼLinuxであるため、AndroidではLinuxのシステムコールの多くが呼び出せるはずです。しかし、実際には呼び出せないシステムコールが数多く存在します。これは、対応するCの関数がlibcに実装されていないためです。(例:「ftruncate64 linker error on NDK r8b」)

このようなシステムコールを網羅的にリストアップする方法は思いつかなかったのですが、近いリストとして、Nexus5実機(Android 4.4)のlibcでは実装されているけれども、NDKのlibcには無いシステムコールを取り出してみました。

  • faccessat(2) - ユーザのファイルへのアクセス権をチェックする
  • fgetxattr(2) - 拡張属性の値を取得する
  • flistxattr(2) - 拡張属性の名前リストを得る
  • fremovexattr(2) - 拡張属性を削除する
  • fsetxattr(2) - 拡張属性の値を設定する
  • ftruncate64(2) - 指定した長さにファイルを切り詰める
  • getsid(2) - セッション ID を取得する
  • getxattr(2) - 拡張属性の値を取得する
  • lgetxattr(2) - 拡張属性の値を取得する
  • listxattr(2) - 拡張属性の名前リストを得る
  • llistxattr(2) - 拡張属性の名前リストを得る
  • lremovexattr(2) - 拡張属性を削除する
  • lsetxattr(2) - 拡張属性の値を設定する
  • mlockall(2) - メモリのロックとロック解除を行う
  • munlockall(2) - メモリのロックとロック解除を行う
  • perf_event_open(2) - set up performance monitoring
  • personality(2) - プロセスを実行するドメインを設定する
  • pread64(2) - 指定したオフセットでファイルディスクリプタを読み書きする
  • pwrite64(2) - 指定したオフセットでファイルディスクリプタを読み書きする
  • readahead(2) - 前もってファイルをページ・キャッシュに読み込む
  • removexattr(2) - 拡張属性を削除する
  • sched_getaffinity(2) - スレッドの CPU affinity マスクを設定・取得する
  • sched_setaffinity(2) - スレッドの CPU affinity マスクを設定・取得する
  • setxattr(2) - 拡張属性の値を設定する
  • signalfd(2) - シグナル受け付け用のファイルディスクリプタを生成する
  • signalfd4(2) - シグナル受け付け用のファイルディスクリプタを生成する
  • swapoff(2) - ファイル/デバイスへのスワップを開始/停止する
  • swapon(2) - ファイル/デバイスへのスワップを開始/停止する
  • tgkill(2) - スレッドにシグナルを送る
  • timerfd_create(2) - ファイルディスクリプタ経由で通知するタイマー
  • timerfd_gettime(2) - ファイルディスクリプタ経由で通知するタイマー
  • timerfd_settime(2) - ファイルディスクリプタ経由で通知するタイマー
  • unshare(2) - プロセス実行コンテキストの一部を分離する

このリストは「arm-linux-androideabi-nm -D」でシンボルを取り出し、Linux環境で一般的と思われるシステムコール・ライブラリコールのみ抽出したものですので、過不足があるかもしれません。

どうやらAndroid NDK付属のlibc.so(android-19プラットフォームなので、Android 4.4に対応)はかなり古いもののようで、bionic C libraryのリポジトリ上では2010年頃に修正されている内容が反映されていなかったりします。一方で、Nexus 5実機のlibc.soには多くの修正が反映されているようです。bionic C libraryの改善は続けるけど、Android NDKプログラミングでは後方互換性のために古いインターフェースで頑張ってね、というメッセージなのかもしれません。

libcで実装されていないシステムコールを直接呼び出す方法

ところで、システムコールの実体がカーネルに実装されていれば、libcに実装がなくても自分でCインターフェースを実装することができます。例えば、getsid(2)であれば次のようにして呼び出すことができます。

#ifdef __BIONIC__
#include 

pid_t getsid(pid_t pid);
pid_t getsid(pid_t pid) {
  return syscall(__NR_getsid, pid);
}
#endif

syscall(2)システムコールは任意のシステムコールを呼び出すためのシステムコールで、第一引数にはシステムコール番号を渡します。ここで使われている__NR_getsidなどの各システムコール番号は<asm/unistd.h>で大量に定義されており、これを使って任意のシステムコールを呼び出すことができます。

ただし、システムコール番号が定義されていても、正しく呼び出せる保証はありません。システムコールの実装本体はカーネルにあり、カーネル次第では未実装のこともあります。未実装の場合にはシステムコールがerrnoとしてENOSYSを返すことに注意してください。

とはいえ、カーネルバージョンに対応したシステムコールであれば大抵うまく動く印象です。筆者はNexus 5(Android 4.4, Linux 3.4.0)でprocess_vm_readv(2)を呼び出すことができました。

まとめ

  • Android NDKでは多くの環境で利用できるライブラリ関数であっても使えないものがあります
    • アカウント管理関連、システム管理関連、ロケール関連、ワイドキャラクタ系、セマフォ、共有メモリなど
  • Android NDKのヘッダファイル、NDKのlibc、実機のlibcで関数の対応が取れていないことがあります
  • libcで実装されていなくても、syscall(2)を使えば任意のシステムコールを呼び出すことが可能です

本稿の内容は、筆者が各種Unix系ライブラリ・ツールをAndroid NDKでビルドしたときに気になった点をまとめたものです。makeがうまく通らないときなどに役立つと想像しています。

ちなみに、Android NDKのlibc.soはプラットフォームandroid-9(Android 2.3相当)からandroid-19(Android 4.4相当)までほとんど変わっていないようです。ですから、上記内容はプラットフォームバージョンによらず共通の内容と言えそうです。


@hnw

klab_gijutsu2 at 19:13|この記事のURLComments(0)TrackBack(0)
2014年04月21日

スマホアプリと米国輸出規制に関するメモの続き

はてなブックマークに登録

前回の記事の続きです。今回は、前回の記事において米国商務省によるフローチャートで輸出規制への該非判定を行った iOS アプリ「stone for iOS」を App Store へ登録申請し、それが一度のリジェクトを経て公開されるまでの経緯を紹介します。

記事へ引用の原文および参考訳は 2014年4月7日時点のものです
※引用文中の赤字・強調表示は筆者によるものです。「:」は引用の中略を示します

許可例外「TSU」とは?

さて、暗号を使用するオープンソースの iOS アプリ「stone for iOS」は、ソースコードが「一般に入手可能(publicly available)」であることから 米国輸出管理規則上の許可例外のひとつである「TSU」の適用が可能で、その措置により商務省の輸出許可を得ることなく米国内の App Store からの輸出が可能であることがわかりました。

一連の許可例外は EAR Part 740 "License Exception"(参考訳:EAR Part 740 "許可例外")に定められています。「TSU」は「Technology and Software Unrestricted(規制されない技術及びソフトウェア)」の略です。前出のフローチャートの指示に添って §740.13(e) の内容を確認してみましょう。

原文:§740.13 Technology and Software Unrestricted (TSU)(以下抜粋)

§ 740.13 TECHNOLOGY AND SOFTWARE UNRESTRICTED (TSU) 
 
This license exception authorizes exports and reexports
of operation technology and software; sales technology
and software; software updates (bug fixes);“mass market”
software subject to the General Software Note; and
encryption source code (and corresponding object code) 
that would be considered publicly available under
§734.3(b)(3) of the EAR. 

                         :

(e) Publicly available encryption source code. 
 
 (1) Scope and eligibility.  Subject to the notification
     requirements of paragraph (e)(3) of this section, 
     this paragraph (e) authorizes exports and reexports
     of publicly available encryption source code
     classified under   ECCN 5D002 that is subject to
     the EAR (see §734.3(b)(3) of the EAR).   
     Such source code is eligible for License Exception
     TSU under this paragraph (e) even if it is subject
     to an express agreement for the payment of a licensing
     fee or royalty for commercial production or sale of
     any product developed using the source code.   
  
 (2) Restrictions.  This paragraph (e) does not authorize: 
 
  (i) Export or reexport of any encryption software
      classified under ECCN 5D002 that does not meet the
      requirements of paragraph (e)(1), even if the
      software incorporates or is specially designed to
      use other encryption software that meets the
      requirements of paragraph (e)(1) of this section; or

  (ii) Any knowing export or reexport to a country listed
       in Country Group E:1 in Supplement No. 1 to part 740
       of the EAR.   
 
 (3) Notification requirement. You must notify BIS and
     the ENC Encryption Request Coordinator via e-mail of
     the Internet location (e.g., URL or Internet address)
     of the publicly available encryption source code or
     provide each of them a copy of the publicly available 
     encryption source code. If you update or modify the
     source code, you must also provide additional copies
     to each of them each time the cryptographic 
     functionality of the source code is updated or modified.
     In addition, if you posted the source code on the
     Internet, you must notify BIS and the ENC Encryption
     Request Coordinator each time the Internet location is 
     changed, but you are not required to notify them of
     updates or modifications made to the encryption source
     code at the previously notified location. In all
     instances, submit the notification or copy to
     crypt@bis.doc.gov and to enc@nsa.gov. 

原文:EAR§734.3(b)(3) より
EAR Part 734 "Scope of the Export Administration Regulations"

                         :
  Publicly available encryption object code software
  classified under ECCN 5D002 is not subject to the EAR
  when the corresponding source code meets the criteria
  specified in §740.13(e) of the EAR.  

参考訳:§740.13 規制されない技術及びソフトウェア(TSU)(以下抜粋)

§740.13 規制されない技術及びソフトウェア(TSU) 

この許可例外は、使用に係る技術及びソフトウェア、販売に
係る技術及びソフトウェア、ソフトウェアのアップデート
(バグの修復) 、General Software Noteの対象となる
"マスマーケット"ソフトウェア、並びに暗号ソースコード
(及び対応するオブジェクトコード)であって、
EAR §734.3(b)(3)のもとに一般に入手可能とみなされるものの
輸出及び再輸出を是認するものである。 

                         :

(e) 一般に入手可能な暗号ソースコード 

 (1) 適用範囲及び適格性 
     本節の(e)(3)項の届出要求事項を条件として、
     この(e)項は、ECCN 5D002のもとに番号分類される一般に
     入手可能な暗号ソースコードであって、EARの対象となる
     ものの輸出及び再輸出を是認する (EAR§734.3(b)(3)を
     参照のこと)。このようなソースコードは、たとえ、
     そのソースコードを用いて開発される製品の商売を目的
     とする製造又は販売のためにライセンス料金又は
     ロイヤリティの支払いに関する明確な契約に従う場合で
     あっても、この(e)項のもとに許可例外 TSUが適用できる。 

 (2) 制限事項 
     この(e)項は、次の(i)又は(ii)については是認しない: 

   (i) (e)(1)項の要求事項を満たさない ECCN 5D002 の
       もとに番号分類される暗号ソフトウェアの輸出
       若しくは再輸出(たとえ、そのソフトウェアが
       本節の(e)(1)項の要求事項を満たす他の暗号ソフト
       ウェアを組み込んでいたり、他の暗号ソフトウェア
       を使用するために特別に設計したものであっても
       同様である);又は 

   (ii) EAR§740 Supplement No.1 のカントリーグループ
        E:1に掲載されている国に輸出若しくは再輸出する
        ことを知っている場合。 

  (3) 届出要求事項 
     あなたは、一般に入手可能な暗号ソースコードの
     インターネットロケーション(例えば、URL若しくは
     インターネットアドレス)を電子メールで BIS及び
     ENC暗号請求コーディネータに届け出るか、或いは一般に
     入手可能な暗号ソースコードのコピーを両者のそれぞれに
     提供しなければならない。あなたがソースコードを
     アップデートするか変更する場合、あなたは、当該ソース
     コードの暗号機能がアップデート又は変更されるたびに、
     両者のそれぞれに追加のコピーを同様に提供しなければ
     ならない。それに加えて、あなたがインターネットに
     ソースコードを掲示する場合、インターネットロケーション
     が変わるたびに、BIS及び ENC 暗号請求コーディネータに
     届け出なければならない、しかし、以前に届け出た
     ロケーションにある暗号ソースコードに対して行うアップ
     デート又は変更については、両者に届け出る必要はない。
     すべての場合において、届出又はコピーを crypt@bis.doc.gov
     及び enc@nsa.govに提出しなさい。 

参考訳:EAR§734.3(b)(3) より
EAR Part 734 "EARの管轄範囲"

                         :
 ECCN 5D002 のもとに番号分類される一般に入手可能な暗号
 オブジェクトコードソフトウェアは、対応するソースコードが
 EAR§740.13(e)で指定される基準を満たしている場合、EARの
 対象とならない。

「ソースコードを一般に入手可能な暗号ソフトウェアとその実行形式は、当該ソースコードのインターネットロケーションを電子メールで当局へ届け出れば輸出規制対象にならない」ということですね。なお、§740.13(e)(2)(ii) に言及のある「カントリーグループ E:1」は米国政府指定の「テロ支援国家」であり、これらの国から App Store を利用することはできません。

暗号処理を内蔵・使用していても米国当局に対しメールでの届け出を行うだけで許可例外 TSU を適用できることはオープンソースソフトウェアの強みのひとつと言えるかもしれません。では、具体的にはどのようなメールを送ればいいのでしょう?

TSU NOTIFICATION メールの書き方と送り方

以前は米国商務省(BIS)のサイトに「Notification Requirements for "Publicly Available" Encryption Source Code("一般に入手可能な" 暗号ソースコードの届出要求事項)」という記事があり、そこにオープンソース暗号ソフトウェアのソースの場所を当局へ届け出るための要領が判りやすく説明されていたのですが、今回何年かぶりに上のリンクの URL へアクセスしてみるとご覧の通り「404 File Not Found」でした。また、サイト内をざっと検索したところでは新しい記事は見当たりませんでした。

上の記事が削除された理由は判然としませんが(事情をご存知の方はお知らせ下さい)、内容は規則の一部ではなく届け出の要領の説明であり、別の記事を手早く見つけることができない以上、仮に多少旧くなっていたとしても届出義務そのものを果たすためにそこでの説明に準じることは当事者としての善意に基づく自然な判断と言えるでしょう。web.archive.org に残っている 2012-01-31 付の最後のキャッシュへのリンクを以下に掲載します。

http://web.archive.org/web/20120131171318/http://www.bis.doc.gov/encryption/pubavailencsourcecodenofify.html

(上の記事の全文)

参考訳(筆者)

"一般に入手可能な" 暗号ソースコードの届出要求事項

このページ上のEARの引用箇所は、Government Printing Office の
Webサイトの「December 9, 2004 Encryption Rule」で見つけること
ができます

ステップ1:輸出管理規則(EAR)の関連部分を読む

このガイダンスは、許可例外TSUのもとで、一般に入手可能
(EARセクション734.3(b)(3) 参照)とみなされる暗号ソース
コードを輸出を進行する前の注意事項を提供するために設計
されており、輸出管理規則 (EAR) の関連箇所と組み合わせて
使用されるべきです。

EARのセクション740.13 (e)は、許可例外TSUを使用して
"一般に入手可能な"暗号ソースコードを輸出する際の主要な
規制基準です。このセクションでは、ECCN 5D002 で規制
されており、また、EAR セクション 734.3(b)(3) で一般に
入手可能とみなされる暗号化ソースコードの輸出(例えば 
インターネットへのポスト)と再輸出のための届出について
説明します。こういった暗号化ソースコード(および対応
するオブジェクトコード、供給されたオブジェクトコードも
また一般に入手可能とみなされます)は、BIS(および
ENC Encryption Request Coordinator)へインターネット
ロケーション(例:URL または インターネットアドレス)
または輸出時点のソースコードのコピーを届け出ることで、
審査なしに輸出・再輸出されることがあります。

一般に入手可能とみなされる暗号化ソースコードは、たとえ
それが商業生産やそのソースコードを使って開発された製品の
販売へのライセンス料やロイヤリティの明示的な同意を受ける
場合においても、許可例外TSUのこの規定の対象となります。
そうしたソースコードをコンパイルした結果の対応する
オブジェクトコードも、それがまた一般に入手可能とされて
いれば許可例外TSUの対象となります。
たとえ一般に入手可能とみなされる暗号ソフトウェアに
組み込まれていたりそれを使うことに特化されていても、
一般に入手可能とはみなされない ECCN 5D002規制下の
暗号ソフトウェアは、許可例外TSUのこの条項
(セクション740.13(e))のもとに輸出や再輸出の対象とは
なりません。

ステップ2:URLロケーション(またはソースコードのコピー)の
           届け出を BIS および ENC Encryption Request
           Coordinator へ提出

あなたは、輸出の時点でソースコードのインターネット
ロケーション(例:URL またはインターネットアドレス)
(またはソースコードのコピー)の記された届出を BIS へ
提供しなければなりません。届け出を E-Mail で BIS へ提出し 
ENC Encryption Request Coordinator へ写しを送ってください。

[注:この段落内のリンクは、自動的に両方の電子メールを
 生成します]
また、このガイダンスのページの末尾に記載されている
アドレスにメールで届け出を提出することができます。

BIS と ENC Encryption Request Coodinator の両方へ
E-Mail で届け出のための書式指定:

あなたの電子メールの件名に、次のように入力します。
"TSU NOTIFICATION"
[注:上記の電子メールのいずれかのリンクをクリックする場合、
これは既に実行されています。]

メールの本文に、次の情報を入力します。

SUBMISSION TYPE: "TSU"と入力
SUBMITTED BY:
SUBMITTED FOR: (暗号品目を輸出する会社または個人の名前)
POINT OF CONTACT:
PHONE and/or FAX:
MANUFACTURER: (該当する場合)
PRODUCT NAME/MODEL #:
ECCN: 5D002

NOTIFICATION:ソースコードのURLまたはインターネットアドレス、
              または他のソースコードのコピーを提供する

FAXまたは手紙による通知による代替の書式の指定:
(以下略)

「stone for iOS」のソースコードを GitHub のリポジトリへコミットした後で以下の内容でメールを送りました。これでこのソフトウェアに許可例外 TSU を適用するために必要な届出要件は満たされました。ちなみにこれは「申請」ではなく「届出」なので送付先からのレスポンスはありません。

From: Tanabe, Makoto
Sent: Thursday, March 13, 2014 3:09 PM
To: crypt@bis.doc.gov; enc@nsa.gov; web_site@bis.doc.gov
Cc: 'Tanabe, Makoto (KLab Inc.)'
Subject: TSU NOTIFICATION - Encryption


SUBMISSION TYPE: TSU
SUBMITTED BY: Tanabe, Makoto
SUBMITTED FOR: KLab Inc.
POINT OF CONTACT: K-Laboratory, KLab Inc.
FAX: +81-3-▒░░▒-▒░░▒
MANUFACTURER(S): The OpenSSL Project
PRODUCT NAME/MODEL #: stone for iOS
ECCN: 5D002

NOTIFICATION:https://www.openssl.org/source/ (OpenSSL)
NOTIFICATION:https://github.com/mkttanabe/stone-for-iOS (stone for iOS)
NOTIFICATION:http://sourceforge.jp/cvs/view/stone/stone/ (stone)

'stone for iOS' is an iOS application.
'stone for iOS' is an open source software.
'stone for iOS' is a GUI version of the open source packet repeater 'stone'.
'stone for iOS' is static linked with the OpenSSL Library. 
'stone for iOS' uses the OpenSSL functions.

App Store への登録申請

以上の手続きで「輸出」を行う準備が整いました。いよいよ「stone for iOS」を App Store へ登録申請します。アプリケーションの説明欄には以下のように簡単にアプリの内容と OpenSSL に関する表記、ソースコードの URL を書きました。

一連のメタデータの記入を終えたあとで「Ready to Upload Binary」ボタンを押下し Export Compliance の確認画面へ移行します。

上の設問の参考訳(筆者)

Export Compliance

あなたのアプリは暗号技術を使用するように設計されているか、あるいは暗号技術を含んだり組み込んでいますか?(たとえあなたのアプリが iOS や OS X で利用可能な暗号化のみを利用していても Yes を選んで下さい)

あなたのアプリは米国輸出管理規則のカテゴリ 5, パート 2 で規定されたいずれかの適用除外の資格がありますか?

あなたのアプリがここにリストされている適用除外の基準に適合していることを確認してください。 あなたはあなたのプロダクトの適切な分類に責任があります。あなたのアプリの不正確な分類は 米国輸出法違反につながる可能性があり、あなたのアプリが App Store から削除されることを含め あなたを処罰の対象と成し得ます。質問に答える前に十分に FAQ を読んで下さい。

もしあなたのアプリの暗号化が次のいずれかであれば、あなたは 質問 #2 に Yes を選択できます:
(a) 医療のエンドユースのために特別に設計されている
(b) 知的所有権と著作権 の保護に限定されている
(c) 認証、デジタル署名、またはファイルのデータの復号 に限定されている
(d) 銀行業務または"金銭取引" のために限定され特別に設計されている;または
(e) "固定式" のデータ圧縮または符号化技術 に限定されている

あなたのアプリが米国輸出管理規則のカテゴリ 5, パート 2 の注 4 に規定された記述に適合している場合にもあなたは Yes を選択できます。

適用除外に関するその他のガイダンスについては FAQ を参照してください。

※サインインの必要なページなので引用をさし控えますが、iTunes Connect の FAQ > Export Compliance はわかりやすく書かれた良い記事です

最初の質問の「Yes」をチェックすると表示されるふたつめの質問 は、前回の記事で何度も見てきた米国商務省規制リストの「カテゴリ 5 パート 2」参考訳)の規定により当該アプリが規制の対象外となるか否かを問うものですね。
カテゴリ 5 パート 2 の ECCN 5D002 のエントリより、オープンソースの暗号ソフトウェアを規制対象外と規定している箇所の原文と参考訳の抜粋を以下に示します。

5D002 “Software” as follows
       (see List of Items Controlled)

              :

Note: Encryption source code classified under this entry
remains subject to the EAR even when made publicly
available in accordance with part 734 of the EAR. 
However, publicly available encryption object code
software classified under ECCN 5D002 is not subject to
the EAR when the corresponding source code meets the
criteria specified in §740.13(e), see also
§734.3(b)(3) of the EAR.

5D002  "ソフトウェア"であって、次のいずれかに
       該当するもの (規制品目リスト参照)

              :

注:このエントリーのもとに番号分類される暗号ソース
コードは、たとえ EAR§734により一般に入手可能にされたと
しても、依然として EAR の対象である。しかし、ECCN 5D002
のもとに番号分類される一般に入手可能な暗号オブジェクト
コードソフトウェアは、対応するソースコードが
EAR§740.13(e)で指定される基準を満たしている場合、EARの
対象とならない(EAR§734.3(b)(3)についても参照のこと) 。

このように、カテゴリ 5 パート 2 には、前出の §740.13(e) での基準を満たすオープンソースの暗号ソフトウェアを規制の対象外とすることが明確に規定されています。したがって、Export Compliance 確認での「あなたのアプリは米国輸出管理規則のカテゴリ 5, パート 2 で規定されたいずれかの適用除外の資格がありますか?」というふたつめの質問に対する「stone for iOS」のステータスは「Yes」です。

こうして質問への回答を終えアプリ本体のアップロードを完了しました。後は審査待ちです。

「stone for Android」での経験とふたつのアプリケーションストア

実のところ、ここまでのプロセスは先年 Android 用アプリとして Google Play ストアで公開した「stone for Android」でのケースとほとんど同じでした。同アプリもまたオープンソースソフトウェアです。iOS 版が OpenSSL ライブラリをスタティックリンクしているのに対し Android 版はシステムに含まれている OpenSSL の共有ライブラリをダイナミックリンクしているといった構成上の違いはあるものの、これまで見てきたように EAR は暗号処理の実体がどこにあっても暗号を使うソフトウェアを等しく「暗号ソフトウェア」として扱うため、米国から輸出を行う上でこの Android 版と今回の iOS 版に法律上の区別はありません。

また、前述のように App Store の Export Compliance 確認画面に「あなたはあなたのプロダクトの適切な分類に責任がある」と明記されているのと同様に、Google 陣営の [ support.google.com - Android デベロッパー > ヘルプ > 販売者のためのガイドライン > 輸出法の遵守 ] のページにも「コンプライアンス要件を判断するのはあくまでもデベロッパーの責任です」と記述されており、その部分についてはこのふたつのストアの基本的なスタンスは共通していると考えてよさそうです。

逆に、アプリケーション登録まわりでの両ストアの対応の大きな違いとしては、まず、App Store では登録時の Export Compliance の確認が複数の質問で念入りに行われその過程で対応指針の指示までもが行われるのに対し、Google Play では「当該アプリは米国の輸出法を遵守している」旨の宣言文に添えられたチェックボックスのクリックひとつで完了する(※)点と、広く知られているように App Store ではアプリの事前審査を専任のスタッフが実施しているのに対し、Google Play では自動化されたシステムが処理している(※)点のふたつが挙げられるでしょう。 (※2014年4月時点)

いずれにせよどちらも同じ米国内に拠点を置くメジャーなアプリケーションストアであることは共通しているわけで、数年前に Android 版を何事もなくリリースした背景もあり、同様の対応を行った今回の iOS 版についてもコンプライアンス面での不安はあまり感じていませんでした。ところが・・・

リジェクトされました (^^;

申請から一週間ほど経ったところで App Store からメールが届きました。思惑とは裏腹にリジェクトされたようです。あれれと思いながら詳細を iTunes Connect の Resolution Center で確認しました。次の内容です。

Reasons

Program License Agreement

----- PLA 2.3 -----


We found that your Application Description states:

"This product includes cryptographic software..."

However, your app does not have Export Compliance, which does not comply with the iOS Developer Program License Agreement, as required by the App Store Review Guidelines.

Section 2.3 of the iOS Developer Program License Agreement specifies,

"You certify that (i) none of the Licensed Applications contains, uses or supports any data encryption or cryptographic functions; or (ii) in the event that any Licensed Application contains, uses or supports any such data encryption or cryptographic functionality, You will, upon request, provide Apple with a PDF copy of Your Encryption Registration Number (ERN), or export classification ruling (CCATS) issued by the United States Commerce Department, Bureau of Industry and Security and PDF copies of appropriate authorizations from other countries that mandate import authorizations for that Licensed Application, as required."

Please review your app's encryption ability, and when resubmitting your binary, check the appropriate answers to the questions in the Export Compliance section of iTunes Connect. You may be asked some follow-on questions to determine the level of encryption in your app; you may also be asked to provide a copy of your CCATS.

If you have questions related to export compliance and your app's use of encryption, please contact the App Store Export Compliance team at ▒░░▒░▒░@apple.com.
ざっくり要約すると、「アプリの説明に "This product includes cryptographic software..." とあるが、あなたのアプリには米国商務省から発行された暗号登録番号(ERN)や製品分類自動追跡システム番号(CCATS)の PDF コピーが添えられておらず iOS Developer Program License Agreement に準拠していない。コンプライアンスの不備である。Export Compliance の質問へ適切に回答し対処して下さい」という内容です。しばらく考えました。

そもそも App Store の拠点は米国内にあり、それ故にそこから製品を配布したければ米国法に従う必要があるわけですが、その一方で当の App Store 自体もまた米国法に準じての運営が前提であるはずです。これまでの話題の通り、EAR は決して「暗号ソフトウェア」のすべてを規制しているわけではありません。したがって、「cryptographic software」の文言が説明文に含まれているからと言ってそのアプリが常に「カテゴリ 5 パート 2」での規制対象に該当するとみなすことは法に照らして適切とは言えませんし、ただちに ERN や CCATS の提示ばかりを求めるのもいささか短絡的でしょう。この指摘にはそういう違和感がありました。

なお、"This product includes cryptographic software..." のくだりは OpenSSL ライセンス中のテキストです。あらためて同ライセンスを確認してみると、こういった場所への記載を求められているのは "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)" の一文のみのようです。そのためこのテキストを外すことは簡単ですが、仮にその即物的な編集を行った結果審査を通ったとしてもそれは本質的な解決ではありません。暗号処理を使いながらも当該ソフトウェアが法規制の対象外となる条件を満たしていることを確認ずみである以上、本来であれば暗号を使っていることを真正面から記述しても何の問題もないはずです。

先方からの連絡には「the App Store Export Compliance team」の紹介もありますが、ここはまず自分の頭で考えることにしました。今回の件の輪郭をなぞると、ストア側がアプリの審査において Export Compliance 確認への回答内容よりも「アプリの説明」の内容に注目した構図が浮き上がります。ということは、その状況に対して効果のあるアプローチは、「アプリの説明」においてこのアプリが米国法に適切に対応していることを積極的に主張することではないかと考えました。

説明文の変更〜再申請

そんなわけで、アプリケーションの説明文に「このソフトウェアは暗号を使っているが米国輸出法上の "規制されない技術及びソフトウェア" に該当するものであり法に従って配布している」という旨を明記することにしました。

当初その文章をゼロから作りかけていたのですが、ふと、法や規則への言及を含むこういった文章にはたいてい定型的なパターンがあることを思い出しました。身近なところではソフトウェアの使用許諾契約書の文章などがそうですね。この手の定型的な文章には多くの場合先人の知恵と経験知が凝縮されていることに加え表現そのものに対する社会的な認知度が概して高い側面があるため適切に使えばあえて自分ではじめから作文をするよりも効果的でしょう。

ネットを探索し、所定のソフトウェアが TSU であることを表明する目的のために The Apache Software Foundation をはじめ複数のソフトウェアベンダが使用している定型的な表現の存在を知りました。その内容を確認して手を加え、最終的に説明文を次の内容に変更して再び App Store への登録申請を行いました。なお、Export Compliance の確認には迷いなく初回と同様に「Yes - Yes」と回答しました。

(「* Cryptographic Software Notice *」以降の参考訳)

* 暗号ソフトウェアに関する注意 *

このソフトウェアは暗号ソフトウェアを含んでいます。あなたが現在住んでいる国は暗号ソフトウェアの輸入、所持、使用、および/または他国への再輸出 に制限があるかもしれません。いかなる暗号ソフトウェアも使用する前に、許可されているかどうかを確かめるために、暗号ソフトウェアの 輸入、所持、または使用、そして再輸出に関するあなたの国の法律、規則および政策をご確認下さい。詳細は http://www.wassenaar.org/ を参照して下さい。

アメリカ合衆国商務省産業安全保障局 (BIS) はこのソフトウェアを、非対称アルゴリズムで暗号機能を使用または実行する情報セキュリティソフトウェア を含む 輸出規制分類番号 (ECCN) 5D002.C.1 として分類しました。この配布の形式と作法は オブジェクトコードとソースコードの両方に許可例外 Technology Software Unrestricted (TSU) のもとでの輸出のための資格を与えています。(BIS 輸出管理規則のセクション 740.13 を参照して下さい)

このソフトウェアは無料で配布され 全体のソースコードは以下のように一般に入手可能です:
- stone for iOS (オリジナルの 'stone' を含む)
https://github.com/mkttanabe/stone-for-iOS
- OpenSSL
https://www.openssl.org/source/

※「ECCN 5D002.C.1」の詳細は前回の記事の「『ECCN 5D002』とは」の項を参照して下さい

結果と所感

後日再びストア側のレビューが実施され、今回は何事もなく審査を通過し App Store へ「stone for iOS」が掲載されました。リジェクト時にストア側から連絡された内容には従わなかったものの結局こちらの判断は間違っていなかったようです。App Store へ暗号を使うアプリを登録したのはこれが初めてでしたが、今回のような経緯でリジェクトされたことは良い経験でした。前述のとおり同じアプリであるにも関わらず Google Play ストアの場合にはコンプライアンスまわりが自己申告のみで通ったことを考え合わせると両ストアの個性の違いが興味深く感じられます。

このふたつのアプリケーションストアに閉じた話題に留まらず、今回の経験からあらためて教訓にしたいと思ったのは、何かを公にする場合にはその行為やその内容が所定のルールを適切に遵守しているという正当性を積極的にアピールすべきだということです。そういった表示や宣言を行うことがルールで義務づけられている場合にはもちろんのこと、そうでない場合や事情が判然としない場合にも、殊に文化・習慣の土壌の異なる環境においては何らかの形で主体的に表明を行う判断が安全でしょう。App Store にしても今回のように暗号を使用しながらも規制対象外であるアプリの申請方法を明示しているわけではありませんが、そのことが「説明を必要としない」という意味ではなかったことはここに書いた通りです。ひとことで言えば、ルールを適切に守ることと同様に、それを適切に伝えることもまた自分にとっても他者にとっても重要だということに他ならないと思います。つまるところルールを間に挟んだ両側にいるのはどちらも「人間」なのですよね。


二度に渡り記事の前半では主に米国輸出管理規則の概要と読み方や扱い方、後半では実在のスマホアプリへ許可例外を適用し App Store で公開するまでの具体的な話題を中心に紹介しました。TSU 以外の様々な許可例外を適用するケースや米国商務省への申請が必要となるケースなど実務上のバリエーションは多岐に渡りますが、まずはこの記事が EAR と米国のアプリケーションストアに向き合いながら対応を行う上での参考情報のひとつとなれば何より幸いです。

前回の記事へ

(tanabe)
klab_gijutsu2 at 08:30|この記事のURLComments(0)TrackBack(0)
2014年04月09日

スマホアプリと米国輸出規制に関するメモ

はてなブックマークに登録

※2014-04-21 追記:続きの記事を公開しました
「スマホアプリと米国輸出規制に関するメモの続き 」へ

記事の概要

スマートフォンの普及に伴いそれをターゲットとするソフトウェアベンダや開発者が増えています。スマートフォン向けのアプリケーションを公開する際には通常 Google Play や App Store 等のアプリケーションストアを使用しますが、これらのストアの拠点は米国にあるためそこでアプリを配布することは米国からの輸出に該当し 同国の輸出法の適用対象となることに注意が必要です。

米国の輸出規制の内容は EAR (Export Administration Regulations 輸出管理規則) に定められており、規制に該当するか否かは「何」を「どこへ」輸出しそれを「誰」が「何のため」に使用するかによって決まります。規制対象にあたる輸出を行う際には米国商務省へ申請を行い有効期限を伴う 輸出許可(Export License)を得なければならないケースもありますが、EAR にはある品目の輸出が規制の対象外となるケースや 所定の条件のもとに許可不要となる 許可例外(License Exception)について個別の規定があり、実際には案件の多くにそのいずれかを適用できるようになっています。ただし、その判断と対応を適切に行うためには関連する規則を適切に読み解くことが必要となります。

ソフトウェアに関しては、EAR は「一般に入手可能な技術及びソフトウェア」を輸出規制の対象外とする一方で 暗号処理を包含または使用するソフトウェア(「暗号ソフトウェア」)については規制対象として詳細な規則を設けています。暗号品目に関する規制内容は EAR の「商務省規制品リスト」の中の「カテゴリ 5 パート 2」同参考訳)に定められています。

先日、手元で開発した iOS 用のアプリ「stone for iOS」を App Store で公開しました。このアプリは OpenSSL ライブラリをスタティックリンクしそこに含まれる暗号関数を使用しています。そのため EAR の規制対象にあたりますが、許可例外のひとつである「規制されない技術及びソフトウェア」(=「TSU」)に該当する要件をあらかじめ備えていたため米国からの輸出に際し要した手続きはわずかなものでした。そういった具体的な事例の情報はあまり見かけないため、この記事では関連する規則の説明とともに今回のケースにおいて手元で行った対応内容を紹介します。筆者は取り立てて法律に詳しいわけではなく単に自分の手がけたソフトウェアを公開するために必要な実務上の手続きの一環として所定の規則を読みかじっているに過ぎませんが、情報のひとつとして、特にオープンソースソフトウェア開発者の方のご参考となれば幸いです。

なお、EAR は暗号処理を本体に含むか否かにかかわらず暗号を使用する品目全般を対象としています。ソフトウェアの場合だと OS 組み込みの暗号機能のみを使っているソフトウェアも原則的に規制の対象となります。記事ではその話題にも触れてみることにします。

資料

米国輸出規制に関する資料へのリンクです。 続きを読む

klab_gijutsu2 at 10:09|この記事のURLComments(0)TrackBack(0)
2013年05月28日

GCM で Wake On WAN

はてなブックマークに登録

ソーシャルな用途ばかりではなく GCM (Google Cloud Messaging for Android) は自分が自分の端末へ通知を送るために使うのにも便利です。その応用例として、以前このブログで 遊んでいる端末を遠隔操作可能な監視カメラとして留守宅で使うアイディア を紹介しました。 そこでは Android 端末を "ファイアウォール越しに外からのプッシュ通知を受信できる消費電力の小さなコンピュータ" として利用したわけですが、今回個人的な必要からそれと同じ考え方で別のアイディアを形にしてみました。GCM 経由で端末へ指示を送り所定の PC を Wake On LAN させるというものです。

Android 端末を一台 LAN に接続した状態で待機させておけば、Wake On LAN の設定とアプリへの登録を済ませた任意の PC をルータの設定に手を加えることなく外から起動することができます。起動さえできれば PC の遠隔操作そのものには TeamViewer など既存のソリューションを柔軟に利用できますね。

このところ出先から自宅 PC へのアクセスが必要となるケースが増えていたもののルータに穴を開けるのはあまり気が進まずやむなく PC を起ち上げっぱなしにしたりしていたのですが、不経済な上にこれからの夏場には密室での連続稼動に一抹の不安がありました。興味のある方はお試し下さい。

アプリ本体: Google Play - WakeOnLan GCM

ソースコード: GitHub - WakeOnLanGCM

使い方

以下に使い方を簡単に控えます

対象 PC をアプリへ登録

  • 「WakeOnLan GCM」のインストール後、Wake On LAN 設定済みの対象 PC の情報をアプリに登録する
  • エントリ名には重複のない任意の名前を指定する
  • エントリのタップでマジックパケットを送出、長押しすると編集・削除へ

端末を GCM へ登録〜遠隔操作用 HTML フォームの生成と使用

  • 対象 PC の登録を終えたらアプリケーションメニューから「GCM 登録画面」へ移動
  • 端末を GCM へ登録するとパスワード設定を経て遠隔操作用 HTML フォームとそれを添付した自分あての新規メールが生成される
  • このフォームにはアプリに登録した PC の「エントリ名」のチェックボックスが並ぶ
  • ネットへ接続した環境でブラウザへこのフォームをロード。ボックスを適宜チェックしパスワードを投入してサブミット〜GCM 経由で LAN 上の Android 端末がメッセージを受信しマジックパケットを送出する


(tanabe)
klab_gijutsu2 at 19:47|この記事のURLComments(0)TrackBack(0)
2013年05月02日

QR コードを機器間での秘密情報の輸送に利用する試み

はてなブックマークに登録

QR コードのもうひとつの利点

QR コードはインターネットアドレスなど所定のテキストデータを簡単に機器に取り込むための手段として広く利用されています。印刷物に限らずさまざまな媒体で扱えるのも便利ですね。

先日、公開鍵暗号を使う Android アプリを試作していた折に、自分の端末 A で生成した秘密鍵を手持ちの別の端末 B へ安全に輸送する手段の検討が必要になりました。やり方はいろいろありそうですが、よりシンプルな方法をとあれこれ考えている内に、机上に無造作に置いたチラシの QR コードにふと目が留まりました。

QR コードからのデータ取り込みは通常光学的に行われます。このことは電子機器の視覚を利用したデータ通信と考えることができるでしょう。第三者による情報窃取の可能性がしばしば問題となるネットワーク通信とは異なり、「目」を使っての情報伝達には物理的に介入することが難しいため、デジタルデータを画像で表現する QR コードは秘匿性の求められる局面での通信手段としても有用と考えられます。

今回の話題はふたつの端末の間でのデータ授受なので、送り側の端末で QR コードの生成と画面表示を行い、受け側の端末でそれを読み取ればよさそうです。情報の受け渡しさえできればコードを残しておく必要はないので QR コードは常に動的に生成し使い捨てとすればよいでしょう。

留意すべき点

一件の QR コードに格納できるデータの量には上限があります。QR コードの開発元である株式会社デンソーウェーブ様のサイトの記事から概要を示す表を以下に引用します。

QRコード規格化・標準化|QRコードドットコム|株式会社デンソーウェーブ

コードの大きさ 21セル×21セル〜177セル×177セル(4セル /辺毎に増加)
情報の種類及び 情報量(混在も可)
QRコードの情報量とバージョンについて
数字
英数字
8ビットバイト(バイナリ)
漢字
最大7,089文字
最大4,296文字
最大2,953文字
最大1,817文字
誤り訂正能力
(データ復元機能)
誤り訂正能力について
レベルL
レベルM
レベルQ
レベルH
コードワードの約 7% が復元可能
コードワードの約 15% が復元可能
コードワードの約 25% が復元可能
コードワードの約 30% が復元可能
コード連結機能 最大16分割 (細長いエリアなどへの印刷)

表には、セル(QR コードを構成する最小の点)構成の最大値である 177 セル×177 セルから成る「バージョン 40」の器に、誤り訂正用の冗長データの最も薄い「レベル L」でエンコードしたデータを格納する際の最大文字数が記載されています。すべての半角英数記号を使う場合は 2953 文字です。この容量の多寡はさておき、普段目にする QR コードのそれとはかけ離れた規模のデータを納めた場合、実用的にはどうなのでしょう。

下の QR コードには半角英数記号 1675 文字を格納しています。これを一般的なサイズ・解像度の PC のモニタで表示した状態であればおそらく多くの携帯端末の QR コードリーダーで読み取ることが可能でしょう。しかし、スマートフォンの小さな画面にこれを表示した状態だと読み取りに失敗するリーダーが出てくるかもしれません。また、一般に情報の密度が高くなるほど読み取りにより多くの時間がかかるため、たとえ成功しても別のストレスが残る可能性もあります。

そういった事情を考え合わせると、今回のような目的で QR コードを利用する場合は、一件のコードに強引に多くのデータを押し込むのではなく、複数のコードに分けて利用することを前提に余裕を持ってデータを扱うほうが賢明でしょう。

実は上の表にも記述があるように、QR コードには一件のデータを最大 16 件のコードに分割して格納することの可能な「コード連結」という仕様があります。次の記事には分割 QR コードの実例が掲載されています。 「IT4206,QRコードの連結機能に対応していますか。」 - 株式会社エイポック様 公式サイトより -

残念ながらすべてのリーダー・ライブラリがこの機能に対応しているわけではなく、現在手元で使っているメジャーな ZXing のライブラリも未対応のようです。しかし、今回はリーダーだけではなくライターも自作することが前提なので、両者間で整合性のとれる内容で独自の分割プロトコルを用意すれば事足りると判断しました。また、二台の端末を操作しながら複数のコードを順番に処理していくのは想像するだけで非常に面倒なので、両者の連携にネットワーク通信を併用し、リーダーが正しくコードを読み取ったらライターが自動的に次のコードを表示することにしました。

ちなみに、先日の記事で操作性の良い QR コードリーダーを自作するために行った取り組みを紹介しましたが、そのきっかけは今回の一連の話題にありました。リーダーを自分で実装すれば上記のような細かい取り回しも柔軟に実現できるわけですね。QR コードの読み取りが主目的ではないアプリへ補助機能としてリーダー処理を組み込むことにはちょっと新鮮な印象があります。

試作と実験

そんなわけで次のような QR コードライターアプリと QR コードリーダーアプリを作ってみました。

  • ライターとリーダーは QR コードの出入力処理とネットワーク通信の併用によって連携する
  • リーダーは起動時に UDP ブロードキャストにより LAN 上のライターを探索、ライターはこれに呼応する
  • ライターはダミーの RSA 秘密鍵 PEM データを 200 文字ごとに分割しそれを格納した QR コードを順次生成・表示する
  • QR コードリーダーはライターの表示するコードをカメラ経由で順次読み取る
  • ライターは生成するコードに毎回連番情報とランダム文字列を挿入、読み取りを終えたリーダーがそれに一致する文字列をネットワーク通信でライターへ返した場合のみライターは次のコードを生成・表示する
  • リーダーは最後のコードの読み取りを終えると受信した秘密鍵情報を表示する

デモ動画

動作の様子です。奥の端末で QR コードライター、手前の端末でリーダーを動かしています。

リソース

今回試作したソフトウェア一式を公開します。興味のある方はお試し下さい。


QR コードライター・リーダーのソースコード
MyQRCodeReaderEx - github
QR コードライターのビルドずみ apk

http://dsas.blog.klab.org/data/qr_secret/QR_writer.apk

QR コードリーダーのビルドずみ apk

http://dsas.blog.klab.org/data/qr_secret/MyQRCodeReaderEx.apk


(tanabe)
klab_gijutsu2 at 14:25|この記事のURLComments(0)TrackBack(0)
2013年04月15日

「ZXing QR コードスキャナー」の内部処理を追う

はてなブックマークに登録

ZXing ("Zebra Crossing") Team による「QR コードスキャナー (以下、"ZXing スキャナ"と略記) は操作性が良く人気の高い定番 Android アプリのひとつです。同アプリでは ZXing Team 自らが開発を継続しているオープンソースのバーコード処理用ライブラリが使用されています。
zxing Multi-format 1D/2D barcode image processing library with clients for Android, Java
このライブラリの優秀さはスマートフォンでバーコード/QR コードを処理する際の実質標準の座にある状況からも裏打ちされていますが、ZXing スキャナの「使いやすさ」は本ライブラリの性能のみに依るものではなくアプリの実装に大きく支えられています。ZXing スキャナのソースコードは公開されているので、それに学べば本家と同等の使いやすさを備えた QR コードリーダーの自作が可能となるはずですね。操作性の良いリーダーを自作できるのであれば QR コード読み取り機能をアプリへ組み込む際にわざわざ ZXing スキャナをインテントで呼び出す必要はなく、独自の処理を柔軟に組み込むこともできるでしょう。また、上記ライブラリを知り尽くした ZXing 謹製スキャナのコードはライブラリの性能を最大限に引き出すための最良のお手本となりそうです。

ZXing スキャナのソースコードを正面から分析した情報はほとんど見当たらないようですが、手元での調査を通じて得られた情報と、そのエッセンスを小さくまとめた形で試作した自作リーダーのリソース一式を公開します。なお、この記事では ZXing 2.1 Release 中の zxing-2.1/android/ 配下のソースコードを対象としています。

注目すべき要素

なぜ ZXing スキャナは QR コードリーダーとして使いやすいのでしょう?その要素こそが実装を調べる上で注目すべきポイントとなるでしょう。特長をみっつピックアップしてみます。

  • コードの認識が早い
    ターゲットにレンズを向けてじっとしていればすぐに認識が完了する
  • 対象とするコードをファインダ枠内に留める以外の操作が不要
    フォーカスを合わせたり画面をタップするといった操作をする必要がない
  • コード識別パターン検出位置を示すポイントが描画される
    プレビュー表示にコード識別パターンを発見した位置を示すポイント (ResultPoint) がリアルタイムで描画されるため端末の位置・角度を加減し易い
ResultPoint の描画

ソースコードの追跡とまとめ

コード読みの途中で迷子にならないための道具としてホワイトボード代わりにエクセルシートを使いました。あくまでも作業用なので決して見た目の良いものではありませんが記録としてそのまま掲載します。

   コードリーディング時のメモ

以下、ソースコードから得られた情報を整理してみます。

※クリックすると大きな図が開きます

  • プレビュー表示中に裏側で走っている太い処理の内容
    ※カメラには 2 秒ごとに autoFocus() が適用される
    1. 現在プレビュー表示中のフレームイメージを取得
    2. 取得したイメージをバックグラウンドスレッドへ渡す
    3. バックグラウンドスレッドはイメージを ZXing ライブラリ処理に渡す
    4. ライブラリはコードの検出過程で QR コード画像の識別パターンである可能性のある箇所(ResultPoint)を見つけるとその座標を UI 側へ逐次通知する
    5. ResultPoint を受け取とった UI 側はそれを表示中のプレビュー画面に重ねて描画する
    6. ライブラリ側処理が今回のフレームイメージから QR コードを検出しなかった場合、1. からの処理が繰り返される
    つまり、ZXing スキャナ は QR コードを見つけるまで絶えずフレームイメージの取得とその画像分析を繰り返している

  • 重要なクラスとその処理の概要
    - com.google.zxing.client.android
    • CaptureActivity
      QR コードスキャナーの Activity クラス
    • CaptureActivityHandler
      CaptureActivity のハンドラ。DecodeThread インスタンスを生成し DecodeHandler からライブラリ処理による QR コード認識結果を受け取る。コードが認識されなかった場合はこのクラスが CameraManager.requestPreviewFrame() を叩くことで、次のフレームイメージ取得〜QR コード認識という全体のループを継続させる
    • DecodeThread
      プレビュー中のフレームイメージから QR コードを検出するための手続きを連続的に繰り返すバックグラウンドスレッド
    • DecodeHandler
      DecodeThread のハンドラ。ZXing ライブラリの MultiFormatReader クラスの QR コード検出メソッドを呼び、認識結果を CaptureActivityHandler へ伝える
    • ViewfinderView
      View の継承クラス。プレビュー画面の描画、ファインダ矩形上に捕捉したイメージについての識別パターン検出点(ResultPoint)の描画を onDraw() メソッド内で実施
    • ViewfinderResultPointCallback
      ZXing ライブラリ内に宣言のある「com.google.zxing.ResultPointCallback」インターフェイスの実装クラス。DecodeThread 経由でライブラリ側処理へ登録され、ライブラリが ResultPoint 認識時にこのクラスの foundPossibleResultPoint() メソッドをコールバックする。同メソッドは ViewfinderView クラスへ ResultPoint を伝達する
    - com.google.zxing.client.android.camera
    • CameraManager
      カメラのオープン・クローズや PreviewCallback へのフレームイメージ取得指示など
    • PreviewCallback
      Camera.PreviewCallback の実装クラス。onPreviewFrame() 発生時に DecodeHandler へキャプチャイメージを渡す
    • AutoFocusManager
      バックグラウンドで 2 秒ごとにカメラに autoFocus() を適用
    • CameraConfigurationManager
      カメラまわりの解像度や各種パラメータの取得・設定

  • 使いやすさを支えているもの
    • 「コードの認識が早い」
      → プレビュー表示中、コードを検出するまで絶えずフレームイメージの取得とバックグラウンドスレッドでの分析を繰り返している
    • 「対象とするコードをファインダ枠内に留める以外の操作が不要」
      → カメラへのオートフォーカスの適用を効果的に利用している
    • 「コード識別パターン検出位置を示すポイントが描画される」
      → 検出位置を直ちに通知するためにライブラリの深部に用意されたコールバック機構と UI への描画処理を適切に組み合わせて使用している

リーダーの試作

以上のように、全体像が見えてしまえば ZXing スキャナの操作性を支えているのは意外なほどシンプルなしくみであることがわかります。それを自作のコードに組み込むことは難しくなさそうですね。そこで ZXing スキャナの実装に倣いつつできるだけ短いコードでざっくりとリーダーを作ってみることにしました。認識結果はダイアログ表示のみとしています。動作確認は一部の環境でのみ行っており、不具合があれば適宜手を入れて下さい。

MyQRCodeReader - github

ソースコードの一覧と概要を以下に示します。

  • MyActivity.java
    本アプリの Activity クラス。implements SurfaceHolder.Callback, Handler.Callback, Camera.PreviewCallback
  • MyCameraConfigurationManager.java
    ZXing スキャナの CameraConfigurationManager より。カメラパラメータの設定を決め打ちに
  • MyDecodeThread.java
    ZXing スキャナの DecodeThreadより。対応コードフォーマットを決め打ちに
  • MyDecodeHandler.java
    ZXing スキャナの DecodeHandlerより。コード認識成功時に MyActivity へ Bitmap は送らず検出したテキストの情報のみに
  • MyFinderView.java
    ZXing スキャナの ViewfinderView および ViewfinderResultPointCallback より。オリジナルの ViewfinderView はプレビュー画面全体を覆っているが単純化のためここではファインダ矩形と一対一に変更。ResultPoint が 4 以上になるとファインダ内に「Warning!」と表示
試作リーダーの動作の様子

ビルドずみの apk はここにあります

http://dsas.blog.klab.org/data/zxing_qr_scanner/MyQRCodeReader.apk


(tanabe)
klab_gijutsu2 at 17:00|この記事のURLComments(0)TrackBack(0)
2012年12月11日

Android の GCM をプライベートな目的に使う

はてなブックマークに登録

GCM (Google Cloud Messaging) は所定の端末へメッセージを送るための有用なしくみですが、何も一斉通知やソーシャルな用途ばかりではなくもちろんきわめて個人的な目的のためにも利用できます。今回はその方面でのアイディアをひとつざっくり形にしてみた例を紹介します。いろいろ応用もできるでしょう。

自分の端末を遠隔操作

Android 界隈は依然にぎやかで次々に新しい製品が発売されています。そのため複数の端末を持っている人も少なくないでしょう。まだまだ使える端末を遊ばせておくのはもったいないので、これを外出中の自宅の監視カメラとして使うことにしました。 端末を室内の対象物に向けて固定しておき、出先や仕事場から GCM 経由で端末へメッセージを送出、それをトリガーにアプリが撮影したスナップを Dropbox 経由で確認します。

シンプルな実装の割に結構役に立っています。 アプリ本体とソースコードを以下で公開しています。

RemoteWand - Google Play
RemoteWand - github

処理の流れ

端末の登録

遠隔操作の対象とする端末上で「RemoteWand」を起動して「登録」ボタンを押下すると GCM サーバへ端末が登録されトリガー発信用のフォームを含む HTML ファイルの添付された自分あてのメールが生成されます。

トリガーの送出〜撮影

PC やスマホのウェブブラウザに上記のフォームをロードし登録時に指定したパスワードを添えてサブミットするとアプリケーションサーバから GCM サーバへ要求が送出され GCM サーバが当該端末へ所定のメッセージをプッシュします。端末にインストールずみの RemoteWand はこのメッセージをトリガーにカメラで静止画の撮影を行います。Dropbox の「カメラアップロード」機能は自動的に撮影画像のアップロードを行うためあらかじめ端末にインストールしておけばリモートで簡単に写真を確認できます。


(tanabe)


klab_gijutsu2 at 15:39|この記事のURLComments(2)TrackBack(0)
2012年09月21日

Android のプッシュ通知用コネクションに関するメモ(補記)

はてなブックマークに登録

前回の記事では、Android のプッシュ通知機構 (GCM) が 「Google 所管のサーバと Android 端末との間の持続接続」 をどのように使用しているかを調べてみました。
DSAS 開発者の部屋 : 「Android のプッシュ通知用コネクションに関するメモ」
この記事の文中に、上記の持続接続が「プッシュ通知以外の用途にも利用されている様子でありそちらもおって調査したい」 と注釈を添えています。今回はその内容と関連する話題を控えます。

まとめ

端末と mtalk.google.com:5228 との間の接続はプッシュ通知以外に次の用途で使用される

  • Android 版 Google Talk クライアントのメッセージ送受信 (ボイスチャットを除く)
  • Google Play アプリのウェブページから端末へのプッシュインストール指示

- 他にもあるかも?
- 非公開の内部仕様につき今後変更される可能性も

Google Talk について

  • Android 版 Google Talk クライアントは、com.google.process.gapps (GoogleServicesFramework) プロセスの管理する 「mtalk.google.com:5228」 とのコネクションを使用する。このコネクションを使用できない場合はエラーとなり、独自に代替ポートが使用されることはない

  • ちなみに PC 版 Google Talk クライアント (googletalk.exe) はメッセージ送受信に 「talk.google.com:5222」 とのコネクションを使用する。5222 番ポートを使用できない場合は代わりに 「talk.google.com:443」 を使用する

  • 「mtalk.google.com」 には 「mobile-gtalk.l.google.com」 という別名がある。ネーミングから想像すると、このホストとのコネクションは本来モバイル端末において Google Talk からの利用を主目的とするものであり、プッシュ通知機構からの利用はあるいはむしろ副次的・応用的なものだったのかも?
    $ dig mtalk.google.com
    
    ; <<>> DiG 9.2.1 <<>> mtalk.google.com
                             :
    ;; ANSWER SECTION:
    mtalk.google.com.       81547   IN      CNAME   mobile-gtalk.l.google.com.
    mobile-gtalk.l.google.com. 291  IN      A       74.125.31.188
                             :
    

Google Play ページからのプッシュインストールについて

    

  • Google Play アプリケーションページからのインストール指示は mtalk.google.com:5228 コネクション経由で端末へ送出されパッケージマネージャへ渡される

  • このプッシュインストールは感覚的には GCM によるプッシュ通知とあまり変わらないが、GCM の端末要件が Android 2.2 以上であるのに対し、Play ストアページからのプッシュインストールは 2.2 未満のバージョンでも機能するという違いがある

  • Android 1.6 環境を確認したところ、システムブート後に mtalk.google.com:5228 コネクションが自動的に確立する点は共通しているが、違いとして目に付くところでは 2.2 以降での com.google.process.gapps プロセスの実体である "/system/app/GoogleServicesFramework.apk" が存在しない。中核となる持続接続のハンドリング自体は旧バージョンにおいても実装されているわけで、C2DM, GCM の要件が 2.2 以上とされた背景にはおそらくはモジュール設計上の複合的な要因があるものと察せられる

mtalk.google.com:5228 に接続できない場合は?

端末上の com.google.process.gapps プロセスが 「mtalk.google.com:5228」 へコネクションを確立できない場合の所作を確認した

  • 実験環境のルータの設定で、「mtalk.google.com」 の既知の分散先サーバである 「173.194.72.188」 「74.125.31.188」 の 5228 番ポートへの LAN 上の各ノードからのアクセスを遮断。この状態で Android 端末を起動し観察を行った

  • 端末から mtalk.google.com:5228 へ接続不可につき connect タイムアウトまでステートは SYN_SENT のまま変化せず。タイムアウト後は何度かリトライが発生。対象は常に 5228 番ポート
    Active Internet connections (w/o servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 10.10.0.10:43695        173.194.35.15:80        TIME_WAIT   -
    tcp        0      0 ::ffff:10.10.0.10:38835 ::ffff:74.125.235.130:443 ESTABLISHED 776/com.android.ven
    tcp        0      0 ::ffff:10.10.0.10:46503 ::ffff:74.125.235.185:80 ESTABLISHED 881/berserker.andro
    tcp        0      1 ::ffff:10.10.0.10:34224 ::ffff:173.194.72.188:5228 SYN_SENT    256/com.google.proc
                            :
    
                       (時間の経過・・・)
    Active Internet connections (w/o servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 10.10.0.10:43695        173.194.35.15:80        TIME_WAIT   -
    tcp        0      0 ::ffff:10.10.0.10:38835 ::ffff:74.125.235.130:443 ESTABLISHED 776/com.android.ven
    tcp        0      0 ::ffff:10.10.0.10:46503 ::ffff:74.125.235.185:80 ESTABLISHED 881/berserker.andro
                            :
    
                       (時間の経過・・・)
    Active Internet connections (w/o servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 10.10.0.10:43695        173.194.35.15:80        TIME_WAIT   -
    tcp        0      0 ::ffff:10.10.0.10:38835 ::ffff:74.125.235.130:443 ESTABLISHED 776/com.android.ven
    tcp        0      0 ::ffff:10.10.0.10:46503 ::ffff:74.125.235.185:80 ESTABLISHED 881/berserker.andro
    tcp        0      1 ::ffff:10.10.0.10:32648 ::ffff:173.194.72.188:5228 SYN_SENT    256/com.google.proc
                            :
    

  • 開発者向けの公式ページ に以下の記述が見られるためリトライ時に 5229 or 5230 番ポートが対象となることに期待したが今回の手順による観察ではその所作は見られなかった
     Note: If your organization has a firewall that restricts the traffic
     to or from the Internet, you'll need to configure it to allow
     connectivity with GCM. The ports to open are: 5228, 5229, and 5230. 
     GCM typically only uses 5228, but it sometimes uses 5229 and 5230.

  • 以上の結果から Android 端末の参加するネットワークでは必ず外向きの 5228 ポートへのアクセスを許可しなければならないことがわかる

  • この調査の過程で、「mtalk.google.com」 の分散先として、前出の 2 サーバに加え 「173.194.79.188」 の存在を確認した (2012 年 8 月末時点)

付録:端末上で拾ってみたパケットの様子

Google Talk     

プッシュインストール     


(tanabe)
klab_gijutsu2 at 13:36|この記事のURLComments(3)TrackBack(0)
2012年08月07日

Android のプッシュ通知用コネクションに関するメモ

はてなブックマークに登録

Android のプッシュ通知機構(GCM, 旧 C2DM)は有用なしくみですが、オープンソースではないソフトウェア要素が関わっているためか内部仕様に近い情報をあまり見かけないのが残念です。手元での観察結果をもとにプッシュ通知で使用されるネットワークコネクションまわりの情報をいくつかまとめてみました。

まとめ

  • Android 端末上の com.google.process.gapps プロセス は mtalk.google.com:5228 へ TCP コネクション [A] を張る
    (通常は 5228 番ポートだが 5229, 5230 番ポートが使用される場合もある)
  • com.google.process.gapps プロセスは基本的に [A] をずっと張りっ放しにしており接続維持のため無応酬 15分ごとに Keep-Alive パケットを流す
  • GCM, C2DM のプッシュ通知はいずれも [A] 経由で端末へ送られる

※[A] はプッシュ通知以外の用途にも利用されている様子でありそちらもおって調査したい

端末− Google サーバ間のコネクションについて

コネクションの存在確認
GCM に関する開発者向け情報へアクセスすると次の記述が見つかります。

[GCM Architectural Overview | Android Developers] より

Note: If your organization has a firewall that restricts the traffic to or from the Internet, you'll need to configure it to allow connectivity with GCM. The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but it sometimes uses 5229 and 5230.
GCM では端末と所定の外部サーバとの接続に通常 5228 番ポートを使うということですね。 さっそく Android 端末実機上で netstat コマンドを実行してみます。

※ここで使用しているのは root 化ずみの実験用端末であり、
各コマンドは Android オリジナルのものではなくBusyBox 版を利用しています。

pid=3547 のプロセスが 「173.194.72.188:5228」との間に TCP コネクションを張っていることがわかります。

# netstat -p
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 10.10.0.10:60272        74.125.235.111:80       ESTABLISHED 6337/berserker.andr
tcp        1      0 ::ffff:10.10.0.10:33375 ::ffff:74.125.235.145:443 CLOSE_WAIT  3547/com.google.pro
tcp        0      0 ::ffff:10.10.0.10:22    ::ffff:10.10.0.6:3667   ESTABLISHED 6385/dropbear
tcp        1      0 ::ffff:10.10.0.10:52412 ::ffff:74.125.235.180:443 CLOSE_WAIT  7318/com.google.and
tcp        0      0 ::ffff:10.10.0.10:22    ::ffff:10.10.0.6:3911   ESTABLISHED 6879/dropbear
tcp        0      0 ::ffff:10.10.0.10:44984 ::ffff:173.194.72.188:5228 ESTABLISHED 3547/com.google.pro
tcp        0      0 ::ffff:10.10.0.10:36091 ::ffff:74.125.235.122:80 ESTABLISHED 6337/berserker.andr
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags       Type       State         I-Node PID/Program name    Path
unix  2      [ ]         DGRAM                     10725 3445/system_server  /data/misc/wifi/sockets/wpa_ctrl_3445-307
unix  2      [ ]         DGRAM                     10727 3445/system_server  /data/misc/wifi/sockets/wpa_ctrl_3445-308
unix  2      [ ]         STREAM                     1293 103/rild            /dev/socket/rild-debug
unix  2      [ ]         STREAM                     1295 103/rild            /dev/socket/rild
unix  4      [ ]         DGRAM                     10722 3762/wpa_supplicant /dev/socket/wpa_wlan0
unix  3      [ ]         STREAM     CONNECTED      17159 108/adbd
unix  3      [ ]         STREAM     CONNECTED      17158 108/adbd
                           :

当該コネクションを保持するプロセスについて
pid=3547 は「com.google.process.gapps」というプロセス。
# ps | grep 3547
  3547 app_1      0:05 com.google.process.gapps

「com.google.process.gapps」は「Google Services Framework」のプロセス名。
(関連記事)
"Fix for Google services Framework (process com.google.process.gapps) has stopped unexpectedly"

「Google services Framework」の実体は以下のシステムパッケージ。(※ソース未公開)

 # ls -l /system/app/GoogleServicesFramework.apk
-rw-r--r--    1 root     root       2867063 Feb  6  2012 /system/app/GoogleServicesFramework.apk

接続先サーバについて
「173.194.72.188」に該当するホスト名は「mtalk.google.com」。
$ host -v mtalk.google.com
Trying "mtalk.google.com"
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30177
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;mtalk.google.com.              IN      A

;; ANSWER SECTION:
mtalk.google.com.       85662   IN      CNAME   mobile-gtalk.l.google.com.
mobile-gtalk.l.google.com. 66   IN      A       173.194.72.188

Received 79 bytes from 10.10.0.18#53 in 0 ms
※2012 年 8 月現在、「mtalk.google.com」の分散先として「173.194.72.188」「74.125.31.188」の 2 サーバの存在を確認

接続維持用の Keep-Alive パケットについて
tcpdump を走らせ 5228 ポートをめぐる応酬を監視。沈黙状態の 15 分ごとに端末から Keep-Alive パケットが送出される様子が見てとれます。
# tcpdump  -nl -s 256 port 5228
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlan0, link-type EN10MB (Ethernet), capture size 256 bytes
13:16:23.504179 IP 10.10.0.10.48304 > 173.194.72.188.5228: P 78:103(25) ack 541win 11236 <nop,nop,timestamp 9585688 3638038533>
13:16:23.560095 IP 173.194.72.188.5228 > 10.10.0.10.48304: P 541:566(25) ack 103 win 274 <nop,nop,timestamp 3638938605 9585688>
13:16:23.560168 IP 10.10.0.10.48304 > 173.194.72.188.5228: . ack 566 win 11236 <nop,nop,timestamp 9585694 3638938605>
13:31:23.568391 IP 10.10.0.10.48304 > 173.194.72.188.5228: P 103:128(25) ack 566 win 11236 <nop,nop,timestamp 9675694 3638938605>
13:31:23.624242 IP 173.194.72.188.5228 > 10.10.0.10.48304: P 566:591(25) ack 128 win 274 <nop,nop,timestamp 3639838674 9675694>
13:31:23.624371 IP 10.10.0.10.48304 > 173.194.72.188.5228: . ack 591 win 11236 <nop,nop,timestamp 9675700 3639838674>
13:46:23.637744 IP 10.10.0.10.48304 > 173.194.72.188.5228: P 128:153(25) ack 591 win 11236 <nop,nop,timestamp 9765701 3639838674>
13:46:23.696340 IP 173.194.72.188.5228 > 10.10.0.10.48304: P 591:616(25) ack 153 win 274 <nop,nop,timestamp 3640738749 9765701>
13:46:23.696469 IP 10.10.0.10.48304 > 173.194.72.188.5228: . ack 616 win 11236 <nop,nop,timestamp 9765707 3640738749>
14:01:23.712443 IP 10.10.0.10.48304 > 173.194.72.188.5228: P 153:178(25) ack 616 win 11236 <nop,nop,timestamp 9855709 3640738749>
14:01:23.770009 IP 173.194.72.188.5228 > 10.10.0.10.48304: P 616:641(25) ack 178 win 274 <nop,nop,timestamp 3641638829 9855709>
14:01:23.770164 IP 10.10.0.10.48304 > 173.194.72.188.5228: . ack 641 win 11236 <nop,nop,timestamp 9855715 3641638829>
14:16:23.783100 IP 10.10.0.10.48304 > 173.194.72.188.5228: P 178:203(25) ack 641 win 11236 <nop,nop,timestamp 9945716 3641638829>
14:16:23.841258 IP 173.194.72.188.5228 > 10.10.0.10.48304: P 641:666(25) ack 203 win 274 <nop,nop,timestamp 3642538904 9945716>
14:16:23.841349 IP 10.10.0.10.48304 > 173.194.72.188.5228: . ack 666 win 11236 <nop,nop,timestamp 9945722 3642538904>
14:31:23.853398 IP 10.10.0.10.48304 > 173.194.72.188.5228: P 203:228(25) ack 666 win 11236 <nop,nop,timestamp 10035723 3642538904>
14:31:23.912470 IP 173.194.72.188.5228 > 10.10.0.10.48304: P 666:691(25) ack 228 win 274 <nop,nop,timestamp 3643438980 10035723>
14:31:23.912555 IP 10.10.0.10.48304 > 173.194.72.188.5228: . ack 691 win 11236 <nop,nop,timestamp 10035729 3643438980>

GCM, C2DM で通知を送ってみる

以下は自作アプリから C2DM, GCM の順に端末へプッシュ通知を送った時の様子です。 いずれも「173.194.72.188:5228」経由で通知が端末へ配信されていることがわかります。

11:49:49.268419 IP 173.194.72.188.5228 > 10.10.0.10.44984: P 974497881:974498015(134) ack 436376500 win 272 <nop,nop,timestamp 1641184578 3415344>
11:49:49.269687 IP 10.10.0.10.44984 > 173.194.72.188.5228: . ack 134 win 11236 <nop,nop,timestamp 3483319 1641184578>

11:49:59.146970 IP 173.194.72.188.5228 > 10.10.0.10.44984: P 134:262(128) ack 1 win 272 <nop,nop,timestamp 1641194459 3483319>
11:49:59.147103 IP 10.10.0.10.44984 > 173.194.72.188.5228: . ack 262 win 11236 <nop,nop,timestamp 3484307 1641194459>



(tanabe)
klab_gijutsu2 at 17:20|この記事のURLComments(4)TrackBack(0)
2012年07月09日

Android パッケージインストール処理のしくみを追う

はてなブックマークに登録

身近な話題でありながら中身のよくわからないことを調べてみるのは興味ぶかいもので、そこから得た知識が意外なところで役に立つことも少なくありません。かねてより Android 環境へアプリケーションをインストールする際に内部でどのような処理が行われるのかに関心を持っていたのですが、知りたい情報がなかなか見当たらないため手元で調査を行いました。その内容を公開します。

まとめ

※クリックすると大きな図が開きます

※PackageInstaller を起動した状態での関連プロセスの例

$ ps
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
root      1     0     268    180   c009b74c 0000875c S /init
root      36    1     812    244   c02181f4 afd0b45c S /system/bin/installd
root      33    1     60900  16628 c009b74c afd0b844 S zygote
system    62    33    126452 28368 ffffffff afd0b6fc S system_server
app_30    372   33    74876  18852 ffffffff afd0c51c S com.android.packageinstaller
                              :
※Package Manager Service と installd プロセスの連携用 UNIX ドメインソケット
$ ls -l /dev/socket/installd
srw------- system   system            2012-07-04 11:43 installd
  • Android OS 上でパッケージインストールにもっとも深い関わりを持つシステムサービスは、system_server プロセス内で稼動する Package Manager Service と、単独のネイティブプロセスとして動作する installd デーモンである。両者はいずれもシステムブート時に動作を開始する。
  • Package Manager Service は起動時に /data/system/packages.xml ファイルより現システム上のパッケージに関する情報を読み込んで実状と照合し、何らかの不整合があれば再インストール処理を含む自動復旧を試みた上で同ファイルへ最新の状態を反映する。
  • Package Manager Service は起動時に /system/etc/permissions/platform.xml を参照し既定の Android パーミッションに関する情報をロードし、同ディレクトリ下の他の *.xml より当該端末がカバーする機能の情報をロードする。
  • installd デーモンは UNIX ドメインソケット /dev/socket/installd 経由で Package Manager Service から処理要求を受信し、パッケージのインストール・アンインストールまわりの一連の手順のうち root 権限を要する処理を主に担当する。

  • PackageInstaller は通常のパッケージを対話的にインストールするための Android 既定のアプリケーションである。ユーザからインストール指示を受けると PackageInstaller は InstallAppProgress アクティビティを呼び出し、InstallAppProgress は Package Manager Service の installPackage() API 経由で当該パッケージのインストールをシステムへ依頼する。
  • Package Manager Service はパッケージインストール要求を受けると当該パッケージの情報を取得し以下を実施する
    - インストール処理用のキューへパッケージ情報を追加〜順番待ち
    - 当該パッケージの適切なインストールロケーションを判定
    - 新規インストール/更新インストールの判別
    - 所定のディレクトリへの apk ファイルのコピー
    - 当該アプリの UID の決定
    - installd デーモンへ処理を要求
         - アプリケーションディレクトリの作成とパーミッション設定
         - dex コードのキャッシュディレクトリへの切り出し
    - 最新の状態を /data/system/packages.xml および packages.list へ反映
    - インストール完了の旨をパッケージ名を添えてシステムへブロードキャスト
     (新規の場合:Intent.ACTION_PACKAGE_ADDED
       更新の場合:Intent.ACTION_PACKAGE_REPLACED)

  • 上記の PackageInstaller 経由でのインストール経路とは別に、Package Manager Service は内部クラス「AppDirObserver」による非対話式のインストール経路を用意している。 AppDirObserver は android.os.FileObserver の派生クラスであり、所定のディレクトリ直下に .apk の拡張子を持つファイルが配置/削除されると自動的にシステムへのインストール/アンインストールを行う機能を持つ。(※別経路でインストールが行われた場合に重複処理発生を抑制するための機構を備える) Package Manager Service は起動時に /system/app, /data/app, /data/app-private, /system/framwrorks, /vendor/app の各ディレクトリを対象に AppDirObserver クラスのインスタンスを生成する。なお、これらのディレクトリへの書き込みには特権が必要であるため一般のアプリケーションプロセスから直接 AppDirObserver の提供する機構を利用することはできない。

コード読み

パッケージインストール処理を構成する一連のソースコードはかなりのボリュームがありますが、コードリーディングの一助として注釈を添えた抜粋を以下に掲載します。コードはいずれも 2012-07-06 現在の内容です。 なお、オンラインソースへのリンクには便宜上 http://android.git.linaro.org/ を使用しています。 続きを読む

klab_gijutsu2 at 12:45|この記事のURLComments(2)TrackBack(0)
2012年05月30日

inotify で Android 上のファイル I/O を監視する

はてなブックマークに登録

先日 Android のソースコードを読んでいた折に、所定の処理においてシステム上で発生するファイル I/O の流れを確認したいと思いました。処理の全体像を把握する上で有用と考えたためです。そこで inotify を利用することにしました。

Android の Linux カーネルには inotify が含まれており、Android SDK には inotify を利用した FileObserver クラスが用意されています。

Android では一般のアプリケーションプロセスからアクセスできるファイル・ディレクトリが制限されるため、FileObserver を使い Android アプリの形でツールを用意しても今回の目的にはあまり役に立たないでしょう。こういう時には CUI のコマンドラインツールの方が何かと融通がききます。Android エミュレータ環境への adb shell 接続で得られる root 権限のコンソール上でツールを動かすことにしました。

Android の notify コマンドを使う

Android には inotify API を使ってファイル I/O をモニタするコマンドラインツール「notify」が含まれています。 [platform/system/core/toolbox/notify.c]

# notify
Usage: notify [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]
この notify コマンドを使って "/data/local/tmp" ディレクトリを監視しつつ別プロセスから以下のコマンドを実行した時の出力例を示します。
# pwd
/data/local/tmp
# echo aaa > test.txt
# mv test.txt aaa.txt
# notify -c 10 /data/local/tmp
/data/local/tmp: 00000100 00000000 "test.txt"  ; 0x00000100:IN_CREATE
/data/local/tmp: 00000020 00000000 "test.txt"  ; 0x00000020:IN_OPEN
/data/local/tmp: 00000002 00000000 "test.txt"  ; 0x00000002:IN_MODIFY
/data/local/tmp: 00000008 00000000 "test.txt"  ; 0x00000008:IN_CLOSE_WRITE
/data/local/tmp: 00000040 00000033 "test.txt"  ; 0x00000040:IN_MOVED_FROM
/data/local/tmp: 00000080 00000033 "aaa.txt"   ; 0x00000080:IN_MOVED_TO
※ I/O イベントの定義は [platform/bionic/libc/kernel/common/linux/inotify.h]

inotifywait コマンドを使う

Android の notify コマンドはコンパクトで手軽なツールですが、出力の読みにくさに加え、ディレクトリ配下を再帰的に監視できない点が不便です。改造したりツールを自作するのも面白そうではありますが、Linux 界隈には inotify-tools というパッケージがあることを知り、そこに含まれる inotifywait コマンドを利用することにしました。

すでに誰かが Android 上で動作する inotifywait をビルドし配布しているのではないかとネット上を探したところ見当たらなかったため手元でビルドを行いました。inotifywait 本体と NDK でのビルド用に手を加えたプロジェクト一式を以下に公開します。

inotifywait_bin_android.zip
md5sum [55BF0FD8365A4139D679CE0D6A3A07B3]
プロジェクト一式(※) - github
(※) 以下の著作物が含まれます。利用に際しては各ソフトウェアのライセンス規約を遵守して下さい。

・ inotify-tools
  著作権者:Rohan McGovern, Radu Voicilas
  ライセンス:GPL version 2
  サイト:https://github.com/rvoicilas/inotify-tools/wiki/
・ GNU glibc POSIX 正規表現関数群 (glibc-2.11.3 より)
  著作権者:Free Software Foundation, Inc.
  ライセンス:LGPL version 2.1 or later
  サイト:http://www.gnu.org/software/libc/

inotifywait に /data /cache /system の各ディレクトリを再帰的に監視させた状態で、自作のアプリ「HelloApp」を起動した際の出力例を以下に示します。

# ./inotifywait -r -m /data /cache /system
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

/system/framework/ ACCESS framework-res.apk
/system/framework/ ACCESS framework-res.apk
/data/app/ OPEN com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ OPEN com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/dalvik-cache/ OPEN data@app@com.example.helloapp-1.apk@classes.dex
/data/dalvik-cache/ ACCESS data@app@com.example.helloapp-1.apk@classes.dex
/data/dalvik-cache/ ACCESS data@app@com.example.helloapp-1.apk@classes.dex
/data/app/ OPEN com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/system/framework/ ACCESS framework-res.apk
/data/app/ ACCESS com.example.helloapp-1.apk
/system/fonts/ OPEN DroidSans-Bold.ttf
/system/lib/hw/ OPEN gralloc.default.so
/system/lib/hw/ ACCESS gralloc.default.so
/system/lib/hw/ ACCESS gralloc.default.so
/system/framework/ ACCESS framework-res.apk
/system/framework/ ACCESS framework-res.apk
/data/app/ CLOSE_NOWRITE,CLOSE com.example.helloapp-1.apk
              :

(tanabe)
klab_gijutsu2 at 16:51|この記事のURLComments(0)TrackBack(0)
2012年05月21日

Android アプリ「SundayPad」を公開しました

はてなブックマークに登録

2012-06-07: バージョン 1.0.1 を公開しました


予定のない休日の午後に PC の前でごろりと横になって DVD を観たりあちこちのサイトを見て回ったりするのは楽しいのですが、ひとたび「のんびりモード」に浸るとキーボードに触るのが億劫になります。ウェブ検索用にちょっとキーワードを入力したいのだけれど今の体勢を崩すのはなんだか悔しい。こんな時スマホなら音声入力を手軽に使えるので便利です。でも外出先ならともかく休日の自宅でわざわざ端末の小さな画面を追いかけるのはあまり嬉しくない気もする。不精者はあれこれ悩みます。

ある時ふと思いました。スマホには得意で賢い音声認識だけをやらせて、その結果を簡単に PC 上で利用できるようにすれば何かと便利ではないか?
さっそく Google 音声認識と連携し認識結果文字列を送信する簡単な Android アプリと、それを受信して PC 上のアプリの所定のフィールドへその文字列を送出する Windows プログラムを作ってみました。期待通りそれはシンプルな割に便利なものになりましたが、しばらく使っている内に「PC のマウスカーソルを操作できればもっと便利になる」ということに気づき、タッチパッド機能と必要最小限のソフトウェアキーボードを加えることにしました。

同僚たちに試作版を見せるとなるほどと受けもよく、そこで出されたアイディアを含めて機能追加と改良を行いひと通り形になったのが Android アプリ「SundayPad」と、PC 用のエージェントプログラム「SpAgent」です。興味のある方はお試し下さい。

改訂履歴

v1.0.1 (2012-06-07)

  • 要求元の Android 端末のアドレスが前回認証ずみのものと同一であれば PC 側での受け入れ確認ダイアログの表示をスキップ
  • Windows 環境でのカーソルの細かい移動をなめらかに

インストール・ダウンロード

Android アプリ「SundayPad」 (2012-06-07 更新)


SundayPad - Google Play のページ

PC 用エージェントプログラム「SpAgent」

Windows 版 - SpAgent.zip (2012-06-07 更新)

Mac 版 - SpAgent.dmg (2012-06-07 更新)

動作環境

SundayPad は Android OS 2.1 以上に対応します。
バージョン 2.2 以上の環境をお勧めします。

SpAgent の動作は以下のそれぞれ単一のテスト環境においてのみ確認しています

Windows 版

  • Windows 8 Consumer Preview 32 bit/ 64 bit (デスクトップ環境)
  • Windows 7 Professional SP1 32 bit/ 64 bit
  • Windows Vista Business SP2 32 bit/ 64 bit
  • Windows XP Professional SP3 32 bit
  • Windows XP Home SP3
  • Windows 2000 SP4

Mac 版

  • Mac OS X バージョン 10.6.8
続きを読む
klab_gijutsu2 at 18:23|この記事のURLComments(2)TrackBack(0)
2011年12月26日

Android アプリ「AppNetBlocker」を公開しました

はてなブックマークに登録

(2015年5月追記)
この記事に掲載の「AppNetBlocker」は Android 5.0 以降の環境では正しく動作しません。記録としてアプリ本体へのリンクは当面残しますが、コメント欄に何度か記載の通りこのアプリケーションの開発はすでに終了しており、今後改訂を行う予定はありません。ご了承下さい。

以前から自分自身がほしいと思っていた Android アプリが形になったためマーケットで公開しました。 今回はそのアプリ、「AppNetBlocker」をご紹介します。

AppNetBlocker は、所定のアプリから「完全なインターネットアクセス」の許可を除去するツールです。実行に root 権限は必要ありません。Android 1.6 以上の環境で動作します。興味のある方はご利用下さい。もちろん無料です。

(2011/12/26 追記)
本アプリは、現時点では安全面において不安要素の少なくない Android をめぐる状況において Android 利用者が自分自身を守るためにとり得る対策のひとつを形にしたものであり、他者の権利を脅かすことを目的とするものではありません。
もし、Android を今よりもさらに安全に利用することが可能となればより多くの利用者・開発者の利益につながることでしょう。本アプリはたとえ僅かでもその一助になればと手がけたものであり、開発の動機もそこにあります。
しかしながら、一部の方から本アプリと Android マーケット規約とのかねあいを懸念するご指摘がありました。その話題については判断の余地があるものと認識していますが、少なくとも利用者の不安を誘引することはまったく本意ではなく、マーケットでの配布という形態は一時中断することとします。

実験用のいわゆる「野良アプリ」として apk のダウンロードリンクを当面残しておきます。このリンクから端末へ直接インストールすることはできません。意図を理解される方のみ自己責任でご利用下さい。

  
[ AppNetBlocker.apk ]
md5sum [CC8104C9DDE44AD308F09FF22B551575]

AppNetBlocker とは?

Android 端末上のデータを狙うマルウェアの問題が取り沙汰されていることもあり、アプリに付与された「許可」の内容は何かと気になります。 特に、それがネットワークアプリやバナー広告を表示するアプリではなく、また、機能面でインターネットへのアクセスが必須とは考えにくい内容のアプリであるにもかかわらず「完全なインターネットアクセス」許可を持っている場合は悩ましいですね。 そんな時には AppNetBlocker が役に立つかもしれません。 続きを読む

klab_gijutsu2 at 10:00|この記事のURLComments(33)TrackBack(0)
2011年11月22日

AndroidからL2TP/IPsec CRT VPNに接続する

はてなブックマークに登録

Android端末には、標準でVPN接続機能が搭載されています。

その中でもL2TP/IPsec方式では、x509証明書による認証がサポートされていて、端末ごとに個別の証明書を発行するなどの方法でセキュリティの高いVPN接続を行うことができます。

KLabでは、社内用の認証局を運用していて、社員が自分のユーザ名の入ったクライアント証明書を持てるようになっています。
管理用サイトへのブラウザでのアクセスのほか、社内の無線アクセスポイントもWPA2 Enterprise EAP-TLS認証に対応させて、クライアント証明書による強力な認証を活用しています。
もちろん、万が一の秘密鍵流出の際には、認証局側でCRLにより証明書を失効させることができるよう整備しています。

本記事では、クライアント証明書を使ったAndroid端末からのL2TP/IPsec VPN接続を試してみたいと思います。

Android 2.2系までの端末では・・・

残念ながら、Android 2.2系までの端末では、組み込まれている racoon(ipsec-tools)に問題があり、x509証明書による認証が実質的に使えない状態です。理由は以下のとおりです。

IPsecでは、IKEの認証フェーズ1にて、お互いのIDを交換します。
このIDには、「ID_IPV4_ADDR」・「ID_FQDN」などいくつかの種類があり、x509証明書を認証に使う場合には、証明書の識別名を使う「ID_DER_ASN1_DN」というIDタイプを使うことができます。

本記事のように、接続元IPアドレスを固定せずx509証明書を使う環境では、ID_DER_ASN1_DNタイプのIDを使うのがベストですが、Android 2.2系のracoonでは、「L2TP/IPsec CRT VPN」を選んでいても常にID_IPV4_ADDRタイプのIDがサーバに通知されてしまいます。

x509証明書による認証で、ID_IPV4_ADDR等をIDに使うことは可能ですが、この場合は使用する証明書のsubjectAltName拡張等のフィールドに「IP:192.168.0.1」等の値を含めておく必要があります。
そしてモバイル端末では、キャリアから動的に割り当てられるIPアドレスを使いますから、事前にIPアドレス等を記載した証明書を用意することも難しいです。
このため、Android 2.2までのOSを搭載した端末では、証明書によるIPsecは実質使えないのです。

この問題は、バグレポートされていて、Android 2.3では改善されています。 そのため、以降はAndroid 2.3系のOSを搭載した端末を前提とします。

認証に使う証明書とCA

認証に使うクライアント証明書・サーバ証明書、発行するCAには、Android端末の実装上、いくつかの制約があるため注意が必要です。

  • サーバ証明書のコモンネームには、サーバのFQDNを記載する

一般のWebサーバ用の証明書と同様に、コモンネームに記載されたFQDNと、端末の設定画面に入力した接続先サーバ名は一致する必要があります。

  • サーバ証明書は、シングルルートCAから発行する

サーバ証明書やクライアント証明書の発行元は、いわゆるオレオレ認証局を使うことができますが、自己署名しているルートCAから直接発行する必要があります。

Android端末のIPsecの処理では、端末ビルトインの証明書ストアは使われず、設定画面で指定したクライアント証明書とCA証明書だけが認証に使われます。ですから、サーバ証明書の検証に必要なルート証明書は、CA証明書としてインポートしてあげれば良いことになります。
しかし、Android端末のCertInstallerでは、複数の証明書をまとめてインポートできない仕様らしく、PEMフォーマットの証明書を複数連結したファイルをインポートしようとしても、「CA証明書1件」と表示されて、中間証明書を含めてインポートすることができません。
IPsecの認証プロトコル中でやり取りできる証明書も各方向1枚だけのため、インポートしたルート証明書から直接サーバ証明書を発行するしか手がありません。
(IKEのプロコトル仕様では、複数の証明書ペイロードを送ることができます。 実際にstrongSwanに改造を加えて実験してみましたが、なんとracoon側が1つ目の証明書ペイロード以外を無視する実装になっていたため失敗しました・・・)

  • クライアント証明書については、自由度は高い

クライアント証明書の検証は、サーバ側の仕事ですので、サーバに必要な中間証明書をインストールすればどのような証明書でも使用することができます。

openswanやstrongswanでは、クライアントのID_DER_ASN1_DNタイプのIDにワイルドカードを指定できるため、証明書の識別名についても自由度があります。
「rightid = "C=JP, ST=Tokyo, O=KLab Inc., CN=*」と設定すれば、各ユーザに個別に発行している「C=JP, ST=Tokyo, O=KLab Inc., CN=USERNAME」のような識別名の証明書をすべて使えるようになります。

端末への証明書・秘密鍵のインポート方法

インポートするクライアント証明書と秘密鍵は 「.p12」拡張子のPKCS#12形式、CA証明書は「.crt」拡張子のPEM形式もしくはDER形式にして、SDカードのルートかdownloadディレクトリに置きます。
Android端末のCertInstallerは「.p12」または「.crt」の拡張子の付いたファイルのみインポートしようとしますので、必ず所定の拡張子にする必要があります。

インポートは、設定の「現在地情報とセキュリティ」メニューの「SDカードからインストール」から行えます。

証明書のインポート画面

それぞれ読み込みできると証明書名を聞かれますが、これは後ほどVPN設定を行う際、証明書を選ぶメニューでの名前になりますので、わかりやすい名前を設定しておきます。

Android端末では、インポート済みの証明書の一覧表示や削除のインタフェースが充実していないので、証明書名には「シリアル番号+コモンネーム」等、証明書の個体を識別しやすい名前にするとよいでしょう。

サーバサイドの準備

Android端末からのVPN接続を受け入れるサーバ側の構築を行います。

使用するソフトウェアは、以下のとおりです。

  • openswan-2.6.36
  • xl2tpd-1.3.0 (後述のチューニングのためにパッチ適用あり)
  • ppp-2.4.5

それぞれ、ビルドオプションに特別なものを指定する必要はありません。

カーネルのESPサポートは、linux-3.0.4の標準のものを使用しています。
また、LinuxでのIPsec実装は、openswanのほかにstrongswanもありますが、strongswanではVPN切断後の再接続が上手くいかないという問題が発生したため、openswanを使用します。

Android端末からのL2TP/IPsec接続では、VPN切断時にサーバ側のSAをうまく削除してくれないという問題があるようで、サーバ側にlifetime時間までSAが残ってしまいます。strongswanを使用するとSAが残っている間は再接続(2本目のSA)が出来ず通信不能になってしまいました。

openswanの設定ファイル(ipsec.conf)は以下のとおりです。

config setup
    interfaces = %defaultroute
    syslog = auth.error
    plutodebug = "control"
    uniqueids = no
    nat_traversal = yes

conn Android-L2TP-IPsec
    auto = add
    type = transport
    keyexchange = ike
    auth = esp
    authby = rsasig
    pfs = no
    keyingtries = 1
    ikelifetime = 8h
    keylife = 8h
    rekeymargin = 10m
    left = %defaultroute
    leftid = "C=JP, ST=Tokyo, O=KLab inc., (略)"
    leftrsasigkey = %cert
    leftcert = Android_IPsec_Server.pem
    leftupdown = "/bin/true"
    leftprotoport = 17/1701
    right = %any
    rightca = "C=JP, ST=Tokyo, O=KLab Inc., (略)"
    rightid = "C=JP, ST=Tokyo, O=KLab Inc., (略)"
    rightrsasigkey = %cert
    rightprotoport = 17/%any

読み込む証明書の指定(leftcert)、各ID等は環境に合わせて調整する必要があります。

サーバ証明書を/etc/ipsec.d/certsに、秘密鍵を/etc/ipsec.d/private、クライアント証明書の検証に必要な中間CA証明書を/etc/ipsec.d/cacertsにそれぞれ設置します。
ファイル名は、OpenSSL流のハッシュ形式にしなくても自動的に読み込んでくれます。
また、秘密鍵の復号化に必要なパスワードは/etc/ipsec.secretsに以下のように記述します。
(leftcertに指定しているAndroid_IPsec_Server.pemに対応する秘密鍵が、/etc/ipsec.d/private以下にAndroid_IPsec_Server.keyの名前で保存されている前提です。)

: RSA Android_IPsec_Server.key "hogefuga"

xl2tpd側の設定ファイル(xl2tpd.conf)とpppオプション(ppp-options)は以下のとおりです。
トンネルに割り当てるIPアドレス(local ipやip range)もお好みで選んでください。

[global]
listen-addr                 = 0.0.0.0
port                        = 1701
debug network               = no
debug state                 = no
debug tunnel                = no

[lns default]
pppoptfile                  = /etc/xl2tpd/ppp-options
hostname                    = gw1
exclusive                   = No
local ip                    = 10.100.254.1
ip range                    = 10.100.254.128 - 10.100.254.254
length bit                  = yes
require chap                = yes
refuse pap                  = yes
require authentication      = yes

pppoptfileでは、認証可否の設定の他、Android端末に通知するDNSサーバを設定します。

auth
refuse-pap
require-chap
ms-dns 10.100.254.1

上記設定では、CHAP認証を行う設定になっています。許可するユーザ名・パスワードは/etc/ppp/chap-secretsに記述します。

USERNAME * "PASSWORD" *

PAP/CHAPともにrefuseとすると、L2TP上では認証しない設定にすることができますが、Android端末ではユーザ名・パスワードの入力が必須になっているため、ダミーのパスワードを入力する必要があります。

Android側からのVPN接続を行うと、端末からのすべての通信がトンネルを経由してサーバへルーティングされます。
サーバ側では、トンネルごとにPPPインタフェースが作られますので、適宜インターネットや社内のシステムに対してルーティング・アクセス許可してあげれば通信可能になります。
名前解決に使われるDNSサーバはpppoptfileのms-dnsで指定したサーバが使用されます。

Android端末サイドのVPN設定

Android端末に、VPN接続のプロファイルを作ります。

設定の「無線とネットワーク」から「VPN設定」とたどります。
「VPNの追加」をタップして「L2TP/IPsec CRP VPNを追加」を選びます。

各項目を以下のように設定して保存すると、プロファイルが作られます。

  • VPN名: 任意の名前を設定します
  • VPNサーバの設定: 接続するサーバのFQDNを指定します
  • L2TPセキュリティ保護を設定: チェックを外したままにします
  • 証明書を設定する:インポートしたクライアント証明書を選びます
  • CA証明書を設定する:インポートしたCA証明書を選びます
  • DNS検索ドメイン: 社内のドメイン名等を任意で設定します
VPNプロファイル画面

保存後、VPN欄に現れたプロファイルをタップして、サーバのchap-secretsに記述したユーザ名・パスワードを入力し接続します。

画面上部に「VPN [プロファイル名] が接続されました」と出ればVPN接続は完了です。
切断する場合には、プロファイルを再度タップすれば切断されます。

VPN接続中に通信できなくなる問題

筆者の環境では、長時間接続していると、突然通信不能になる症状が発生しました。

調査してみたところ、キャリアあるいは電波状況によっては、不定期的にパケロスする状況が発生するようで、L2TPサーバ側で定期的に送っているHelloメッセージがロスし最大再送回数に到達してしまったためにトンネルが閉じられているという状態でした。

Android端末側から、L2TPトンネルの切断を検知できるようなメッセージを送出できると良いのですが、端末標準の設定インタフェースでは実現できないため、サーバサイドで出来る限りの調整を行なってみます。

今回使用したL2TPサーバ(xl2tpd)では、HelloメッセージによるKeepaliveの間隔(60秒)、コントロールメッセージの再送間隔(1秒)・最大再送回数(5回)がソースコード中にハードコードされています。

この再送条件では、電波状況等により通信品質が変化するモバイル環境には厳しすぎますので、設定ファイルからパラメータを変更できるようにするパッチを書いてみました。

xl2tpd-1.3.0-add-ctrl-retrans-opt.patch
(2012/02/17:コメントにて、1.3.1では上記パッチが当たらないという連絡をいただきましたのでdiffを取り直しました: xl2tpd-1.3.1-add-ctrl-retrans-opt.patch )

このパッチを適用すると、設定ファイルのglobalセクションに以下の3つのパラメータを設定できるようになります。

  • hello delay (Helloメッセージの送信間隔)
  • ctrl retrans max (最大再送回数)
  • ctrl retrans delay (再送回数)

各パラメータをそれぞれ、Hello送信間隔を5分(hello delay = 300)、コントロールパケットの再送間隔を10秒(ctrl retrans delay = 10)で最大再送回数を18回(ctrl retrans max = 18)とすれば、最大3分程度通信不能な時間が発生してもトンネルを閉じられずにすみます。
長すぎる値を設定すると、本当に端末がオフラインになってしまっていても、長時間サーバ側にトンネルが残り続けてしまうため注意が必要です。

このパッチによるパラメータの変更は、あくまで切断される条件の緩和ですので、確実にVPNが切れなくなるわけではありませんが、多少の効果はあると思います。


#dSn

klab_gijutsu2 at 17:49|この記事のURLComments(9)TrackBack(0)
2011年10月25日

エンコードされた AndroidManifest.xml を読む

はてなブックマークに登録

アプリケーションマニフェストである AndroidManifest.xml をはじめ、Android アプリを構成する各種 XML ファイルは apk へのパッケージングの段階でパースされ独自のバイナリ形式にエンコードされます。 このファイルを扱う処理をコンパクトに実装したいと思ったのですが、現時点では形式に関する公式の資料が存在しないことがわかり aapt を参考に手元でフォーマットの分析を行いました。パーサ試作例とともにその内容を公開します。

1. データ例

(A) テストアプリ「MyApp」用に記述した生の AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.klab.sample.myapp"
    android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MyApp"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:excludeFromRecents="false"
            android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

(B) パッケージ後の MyApp.apk に含まれる エンコードずみ AndroidManifest.xml のダンプ

(C) 試作コードにより上記 (B) に含まれる情報を XML ツリー形式に再構成したもの

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="1"
    android:versionName="1.0"
    package="jp.klab.sample.myapp">
    <uses-sdk android:minSdkVersion="4" />
    <application android:label="@0x7F050001" android:icon="@0x7F020000">
        <activity
            android:label="@0x7F050001"
            android:name=".MyApp"
            android:excludeFromRecents="false"
            android:launchMode="2"
            android:configChanges="0x000000A0">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
続きを読む
klab_gijutsu2 at 08:47|この記事のURLComments(0)TrackBack(0)
2011年09月29日

Android アプリケーションが起動するまでの流れ

はてなブックマークに登録

プログラム開発のために Android 上でアプリが起動するまでの過程を調べてみました。備忘をかねて、ソースコードをひと通り追跡した記録をここに控えます。

まとめ

※クリックすると大きな図が開きます

  • Zygote(ザイゴート)プロセスは、Android システムブート時に起動し DalvikVM 本体と Android プログラムの実行に必要なダイナミックリンクライブラリと Java のクラスライブラリをロードした状態で待機する常駐プロセスである
  • Zygote プロセスの目的は、同プロセスを fork することによりプログラム実行用のプロセス環境を素早く効率的にシステムへ提供することにある
  • UNIX ドメインソケット /dev/socket/zygote が Zygote プロセスへのインターフェイスであり、同ソケットにプロセス生成要求を送出すると Zygote はプロセス fork を実行する
  • system_server プロセスは同じくシステムブート時に起動するシステムプロセスである。Activity Manager をはじめ数多くの Android サービスはこの単一プロセス内で稼動する
  • ユーザが Android アプリ起動の操作を行うと Activity Manager 経由で Zygote にプロセス生成要求が送出される。ここで fork されるプロセスがアプリ用のプロセスとなる。つまり、すべての Android アプリケーションプロセスは Zygote の子プロセスである
  • アプリケーションプロセスの uid, gid は、当該アプリのインストール時にシステムが割り当てたものを Activity Manager が Zygote へのプロセス生成要求メッセージ内で指定し、Zygote がそれを設定するしくみ
  • Activity Manager 経由で Zygote から fork された アプリ用新規プロセスは、初期処理の段階で Activity Manager とのプロセス間通信を通じて起動対象アプリの情報を取得し、それをもとに所定のアプリをロード〜実行する。また、起動後のアプリケーションプロセスはライフサイクル管理を含め Activity Manager を中心とする文脈の中で一元管理される
※プロセスの親子関係に注意
$ ps
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
root      1     0     268    180   c009b74c 0000875c S /init
root      33    1     60924  16448 c009b74c afd0b844 S zygote
system    62    33    130944 27412 ffffffff afd0b6fc S system_server
app_29    122   33    82996  21988 ffffffff afd0c51c S com.android.launcher
app_9     476   33    78500  20568 ffffffff afd0c51c S jp.klab.stone
                              :

コード読み

以下はソースコードを追った記録です。
なお、例の一件以来 android.git.kernel.org のダウン状態が続いているため、記事中のコードは android.git.linaro.org 上の最新版(2011年 9月末現在)からの引用です。
続きを読む

klab_gijutsu2 at 14:09|この記事のURLComments(0)TrackBack(1)
2011年08月31日

Android 上のアプリから SSL クライアント認証の必要なサーバへアクセスする方法

はてなブックマークに登録

Android の Web ブラウザが SSL クライアント認証に対応していないのは不便だという話を以前このブログに書きました。記事では、Android 上の stone に SSL まわりの処理を代行させる方法について触れました。
Android 上のブラウザから SSL クライアント認証の必要なサーバへアクセスする方法


それから半年、本家の Android 標準ブラウザは現在も未対応のままですが、「SandroB」のようにクライアント認証に独自対応したブラウザも登場し始めています。こうした動きは Android ユーザとしてとても喜ばしいことで、対応環境の拡大や機能面の向上など今後の進化が大いに期待されます。

一方で、たとえば KLab の「VPN-Warp」のような SSL-VPN システム経由での利用が想定されるアプリケーションは Web ブラウザばかりではありません。私の場合、出先から自宅 PC へのアクセスには VNC を VPN-Warp+stone 経由で使っています。VNC ビューワそのものは SSL に対応していませんが、通信に stone を介在させることで所定のアプリ本体が SSL まわりの実装を持っていなくてもそういう使い方が可能となるわけですね。もちろん、人によってはここで言う「所定のアプリ」に、「普段使い慣れているブラウザ」が該当する場合もあるでしょう。

冒頭の記事を書いた時点では Android 用 stone は何かと面倒な CUI ベースでしたが、現在は通常の Android アプリとしてマーケットで公開しています。社内からの声もあり、今回はこのアプリ版に固有の Android キーストアへ証明書をインストールする機能に関する話題に触れながら、stone for Android でクライアント証明書を利用する手順等を紹介します。 続きを読む
klab_gijutsu2 at 10:06|この記事のURLComments(0)TrackBack(0)
2011年08月09日

root 化ずみ端末に対応した Android アプリを書く方法

はてなブックマークに登録

Android 端末を root 化するユーザが増えています。
一部の開発者向け製品以外では root 権限を取得するための公式の手段は提供されていないので、その方法を自分で探索するにせよ誰かが開拓した手順をトレースするにせよすべては自己責任であるわけですが、一方で root 権限を必要とするアプリはマーケットにもあれこれ出回っており、つまりはこの件に関する安全性と利便性のトレードオフに満足できない向きが世界中に大勢いるということでしょう。

その状況自体もいろいろ興味ぶかいのですが、ところで、その「root 権限を利用するアプリ」というものはどうやって書けばいいのでしょう?Google 公式の開発者向け資料は言うに及ばず、その他のリソースにも今のところほとんどこの話題に関する情報は見当たりません。そこで今回は、実際に手元のアプリを root 権限での実行に対応させる試みを通じて得た情報やノウハウを紹介したいと思います。 続きを読む

klab_gijutsu2 at 21:00|この記事のURLComments(4)TrackBack(0)
2011年06月24日

GUI 版「stone for Android」公開のお知らせ

はてなブックマークに登録

SSL 対応のパケットリピータ「stone」を Android 上で動かすことにはさまざまなメリットがあります。 以前このブログに次の記事を書きました。

  ・「パケットリピータ stone を Android へポーティング
  ・「Android 上のブラウザから SSL クライアント認証の必要なサーバへアクセスする方法


当時は Android NDK でビルドしたネイティブの stone バイナリとビルド用リソースの一式を公開しましたが、その後 GUI 版の開発を手がけ、「stone for Android」として Android マーケットへの登録を行いました。

これはいわゆる「普通の Android アプリ」ですから端末へのインストールも取りまわしもとても簡単です。 興味のある方はぜひご利用下さい。ソースコードも公開しています。


ブラウザから端末へ PUSH インストールの可能なページ
続きを読む
klab_gijutsu2 at 17:55|この記事のURLComments(13)TrackBack(0)
2011年02月15日

Android上のブラウザからSSLクライアント認証の必要なサーバへアクセスする方法

はてなブックマークに登録

■ 初めての Android 端末(IS01)のブラウザで困ったこと

KLab では、社員が社外から社内 LAN 上のサーバへ安全にアクセスするために、自社製の SSL-VPN システムである「VPN-Warp」を使っています。これにより、社員はインターネット上の専用の中継サーバへアクセスすることで所定の社内サーバと通信することが可能です。この中継サーバは不正利用を防ぐためにクライアントからの接続要求時に所定の電子証明書の提示を求める「SSL クライアント認証」を行います。KLab の発行した有効な証明書を提示するクライアントからの要求のみがこれを通過できるというわけですね。

さて、筆者は 2010 年末に IS01 を入手し、あれこれ試していたところひとつ困ったことがありました。Web ブラウザが SSL クライアント認証に対応していないため上記の VPN-Warp 中継サーバ経由で社内のサーバへアクセスすることができないのです。スマートフォンに馴染みが薄いためとりあえずの実験用と割りきって確保した端末ではありますが、その点以外は結構気に入っていたため残念に思いました。

■ 「stone」を併用することで解決

先日このブログで Android 用の「stone」一式を公開しましたが、実のところ stone を Android 上で動かしたいと考えたのは、手元の IS01 のブラウザから VPN-Warp 経由で社内サーバへアクセスすることが目的でした。パケットリピータ stone の SSL 対応機能はクライアント認証をサポートしているため、ブラウザと VPN-Warp 中継サーバの間に stone のプロセスを介在させてやれば SSL まわりの処理一式を stone にまかせることができるのです。


stone に クライアント認証対応を含めて SSL 処理を代行させる

実機でこの操作を行った様子を以下に示します。
# 図中の stone コマンドラインは次の内容です

stone -d -q pfx="cert.pfx" -q passfile="pass.txt" \
      relay.klab.org:443/ssl localhost:8888

■ 実は最近の端末のブラウザもクライアント認証未対応?

さて、ブラウザがクライアント認証に対応していないという制約は筆者の持つ IS01 が旧い Android 1.6 ベースであることに起因するものであって Android 2.x 端末ではきっと解消されているのだろうと漠然と想像していたのですが、実際にはそうではないことを最近になって知りました。
この冬の新機種ブームも相まって社内の Android ユーザが増加する中、「外からイントラにアクセスできたよ!」という声が聞こえてこないのです。利用者に事情を尋ねるとやはりブラウザがクライアント認証をハンドルしない様子とのことでした。しばらく Android から離れていたため事情がわからず情報を探してみると、どうやら本件への対応は今後の課題という扱いのようです。

   Android プロジェクト公式フォーラムより
  ・Issue 11231: Provide support for managing CA and client certificates
  ・Issue 8196: Enhancement: Client Certificate Authentication in Browser

ご存知の方はとっくにご存知の話だと思いますがちょっと意外に感じました。世間一般に SSL クライアント認証を活用したサービスがあまり多くないことの影響なのかもしれません。

■ 同じ問題で困っている方へ

本件は Android の近い将来のバージョンでの解決が期待されますが、自分と同じく今現在の制約に困っている方の参考になればとこの記事を書くことにしました。Android 用 stone のダウンロードリンクと導入・実行方法を次の記事に掲載しています。

  ・DSAS開発者の部屋:パケットリピータ「stone」を Android へポーティング

今のところ単体の ELF 実行形式であるため CUI に慣れていないと多少扱いにくいという点と、Android では hosts ファイルの書き換えが一筋縄ではいかない事情もあり接続先によってはブラウザでの localhost 指定が通らない可能性がありますが、本記事を最後まで読み進められた知見と技術力で上手くご利用下さい。なお、stone の詳細については下記サイトの解説記事が参考になるでしょう。

stone 公式サイト:「Simple Repeater 'stone'」


(tanabe)
klab_gijutsu2 at 13:24|この記事のURLComments(8)TrackBack(0)
2010年12月13日

パケットリピータ「stone」を Android へポーティング

はてなブックマークに登録

オープンソースの多機能パケットリピータソフトウェア「stone」を Android 用にビルドしてみました。
当初は個人的に使用することが目的でしたが、原作者の仙石 浩明 (Hiroaki Sengoku) さんの了承のもと、ビルドずみバイナリを含むリソース一式を非正式版として公開します。他のプラットフォームで stone の便宜に馴染んでいる方はお試し下さい。なお、stone 本体の使用方法等についてはこの記事では触れません。公式サイトの解説記事を参照して下さい。 続きを読む
klab_gijutsu2 at 17:34|この記事のURLComments(4)TrackBack(0)
2010年12月02日

Android NDK でネイティブ CUI プログラムを書く!

はてなブックマークに登録

(2015年6月追記)
2014年10月に公開された Android 5.0 (Lollipop) 以降では PIE (Position Independent Executable) 以外のネイティブ実行形式がサポート外となったため注意が必要です。詳細は本ブログの次の記事を参照して下さい。

「Android で今後ネイティブ実行形式を扱う際に注意すべきこと」(2015年6月17日掲載)


今ならとても有利な条件で au の Android 端末を入手できることをネットで知り、先週の休みに地元の家電量販店へ足を運んでみました。 Android 1.6 搭載のこの「IS01」に今後 OS のバージョンアップサポートが適用されない旨の発表を聞いた時はお気の毒に・・と思っていましたが、極端に低いコストで実機を持てるのなら話は別です。とりあえずの実験用としていろいろ使えることでしょう。

そんなわけでこの何日間か Android SDK を勉強しながら Java のプログラムを書いたりしていたのですが、Android NDK を使えば ネイティブコードの JNI 用ライブラリだけではなく スタンドアロンの CUI プログラムも作成できることを知りました。

それはそれで面白いので Hello world! ではなくさっそく変なプログラムを C 言語で書いてみました。Android 端末上で次のように動作します。
・ 簡易 HTTP サーバとして振る舞い PC 上の web ブラウザと対話できる
・ フォームから指定された場所の地図を Android 端末上に表示する

# プログラム作成中の自問自答
Q:「もしかすると普通に PC で Google Maps を使う方が便利ではないか?」
A:「GPS の恩恵あり!入力の楽な PC キーボードを使えるのも良し!」
Q:「なら普通に SDK でそういうアプリを作ればよいのではないか?」
A:「Android の根っこは Linux!C でさくさく書くのが粋というものだ!」

粋かどうかはさておき興味のある方はご覧下さい。ソースコードも掲載します。 続きを読む

klab_gijutsu2 at 21:22|この記事のURLComments(6)TrackBack(0)
2008年01月07日

Ant とテキストエディタではじめる Android

はてなブックマークに登録

皆様、新年あけましておめでとうございます。今年もよろしくお願いいたします。

さて、The Open Handset Alliance が発表した Android SDK ですが、既に Eclipse + Eclipse Plugin を使用してお試しになられた方もいらっしゃるかと思 います。

私は通常の開発業務で Eclipse を使っているのですが、plugin を入れすぎたせ いか動作が重いので、もっと軽い環境で開発できたらと思ってました。という 訳で Emacsと か vi などのテキストエディタを使って Android のアプリケーション を開発できる方法をご紹介します。

続きを読む
klab_gijutsu2 at 11:38|この記事のURLComments(0)TrackBack(1)
Blog内検索
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント
最新トラックバック
Archives