Android Zaurusの日記

ザウルスをアンドロイドにしてあげる
本館 Android Zaurus

2012-02-23 んふんふ。カンストしたのでアンインストール。

TF201でARM DS-5

17:25 |  TF201でARM DS-5 - Android Zaurusの日記 を含むブックマーク

2011/11末にARMのDeveloper Studio 5のCommunity Editionという無料版が公開された。TF201ではoprofileが有効化されているので、DS-5のStreamlineプロファイリングが出来るのではと淡く期待して作業してみた。


結論から言うと、出来なかった。Bootloaderのunlockソフトウェアも公開された*1ことだし、今後に期待してメモだけ残しておく。


まず、Ubuntuの32bit環境を用意する。64bit版では「filter is not applicable」というエラーが出てインストールが出来ない。Virtual BoxにUbuntuを普通にインストールして、Android SDKとNDKを普通にインストールして、DS-5を普通にインストールするだけ。


DS-5をインストールすると、Eclipseをインストールしたディレクトリの下に、

 $ ls plugins/com.arm.ds.collateral_5.8.0.20111129_133554/arm/gator/
 README_Streamline.txt  annotate/  daemon-src/  driver-src/  hrtimer_module/

というファイルがダウンロードされている。詳細な説明はREADMEに書かれているので一読する。


driver-src以下のソースコードをビルドして、カーネルモジュールを作る必要がある。従って、TF201のカーネルソースコードが必要になる。


ソースコードASUSのダウンロードサイト*2NVIDIAのgitからダウンロード出来る。NVIDIAのgitは

 $ git clone git://nv-tegra.nvidia.com/linux-2.6.git

今回はASUSのサイトからダウンロードしたカーネルソースコードを使ったが、そのままではビルド出来なかった。

 $ cd kernel-3b434ee
 $ export ARCH=arm
 $ export PATH=/opt/arm-cc/bin:$PATH
 $ export CROSS_COMPILE=arm-none-linux-gnueabi-
 $ make tegra_cardhu_android_defconfig
 $ make zImage -j16
 arch/arm/mach-tegra/tegra3_tsensor.c:24: fatal error:
 devices.h: No such file or directory

tegra_cardhu_android_defconfigが実機のconfig.gzと同等であることは、.configと保存してあるconfig.gzとのdiffで確認した。ヘッダが足りないらしいので、

$ find . -name "devices.h"
./arch/arm/mach-tegra/devices.h
./arch/arm/mach-mx3/devices.h
./arch/arm/mach-ks8695/include/mach/devices.h
./arch/arm/mach-mmp/include/mach/devices.h
./arch/arm/mach-mx25/devices.h
./arch/arm/mach-cns3xxx/devices.h
./arch/arm/mach-ux500/include/mach/devices.h
./arch/arm/mach-mx5/devices.h
./arch/arm/mach-pxa/devices.h
./arch/arm/mach-imx/devices.h
./arch/arm/mach-msm/devices.h
./arch/arm/mach-mxc91231/devices.h
./include/config/misc/devices.h
./include/config/usb/announce/new/devices.h

mach-tegraにそれらしいのがあるので、arm/includeにコピーしておく。

 $ cp arch/arm/mach-tegra/devices.h arch/arm/include/
 $ make zImage -j16

今度は正常にビルド出来るので、READMEにあるように、

 $ cd gator-driver
 $ make -C /path/to/kernel-3b434ee M=`pwd` modules

これでgator.koが出来る。なんとかしてTF201に持って行き、

 $ su
 # insmod ./gator.ko
 insmod: init_module './gator.ko' failed (No such file or directory)

 # busybox insmod ./gator.ko
 insmod: can't insert './gator.ko': unknown symbol in module or invalid parameter

というわけでインストール出来ない。実は試す前から多分ダメだろうなと察しがついていた。READMEに詳細なカーネルコンフィギュレーションが書かれている。

Required Kernel Changes (depending on the kernel version, the location of these 
configuration settings within menuconfig may be different)
 - General Setup
  - [*] Profiling Support
 - Kernel hacking
  - [*] Tracers
    - [*] Trace process context switches and events
 - Kernel Features
  - [*] High Resolution Timer Support
  - [*] Use local timer interrupts (only required for SMP)

Kernel hackingの「Trace process context switches and events」がtegra_cardhu_android_defconfigではオフになっている。これをオンにしないと、実際にtraceデータを吐くモジュールがリンクされない。オンにして、DS-5 Readyなdefconfigとして保存して終了。

 $ make menuconfig
 $ make savedefconfig
 $ mv defconfig arch/arm/configs/tegra_cardhu_android_ds-5_defconfig

というわけで、無駄骨の複雑骨折でした。

*1:ただしICS専用なので国内版はまだunlock出来ない

*2:サイトが落てるので後日追記

2012-01-11

Levels in Renderscript(超訳)

15:04 |  Levels in Renderscript(超訳) - Android Zaurusの日記 を含むブックマーク

Levels in RenderscriptというRenderscriptを解説した記事が本家に上がったので、久々に超訳。誤訳などあればご指摘を。


ICSではRenderscript(RS)がアップデートされた。いくつかの新しい機能が加えられ、アプリケーションで計算を簡単に高速化出来るようになっている。大量の処理が必要な大きなデータバッファがある場合、計算の高速化のためにRSは興味深い。この例ではレベル/サチュレーション処理をビットマップに施してみる。


この場合、サチュレーションはすべてのピクセルと色行列のかけ算として実装され、レベルはいくつかの演算で実装されるのが常套だ。


1. 入力レベルの調整

2. ガンマ補正

3. 出力レベルの調整

4. 有効値へのクランプ


単純な実装はこのようになる。


for (int i=0; i < mInPixels.length; i++) {
    float r = (float)(mInPixels[i] & 0xff);
    float g = (float)((mInPixels[i] >> 8) & 0xff);
    float b = (float)((mInPixels[i] >> 16) & 0xff);

    float tr = r * m[0] + g * m[3] + b * m[6];
    float tg = r * m[1] + g * m[4] + b * m[7];
    float tb = r * m[2] + g * m[5] + b * m[8];
    r = tr;
    g = tg;
    b = tb;

    if (r < 0.f) r = 0.f;
    if (r > 255.f) r = 255.f;
    if (g < 0.f) g = 0.f;
    if (g > 255.f) g = 255.f;
    if (b < 0.f) b = 0.f;
    if (b > 255.f) b = 255.f;

    r = (r - mInBlack) * mOverInWMinInB;
    g = (g - mInBlack) * mOverInWMinInB;
    b = (b - mInBlack) * mOverInWMinInB;

    if (mGamma != 1.0f) {
        r = (float)java.lang.Math.pow(r, mGamma);
        g = (float)java.lang.Math.pow(g, mGamma);
        b = (float)java.lang.Math.pow(b, mGamma);
    }

    r = (r * mOutWMinOutB) + mOutBlack;
    g = (g * mOutWMinOutB) + mOutBlack;
    b = (b * mOutWMinOutB) + mOutBlack;

    if (r < 0.f) r = 0.f;
    if (r > 255.f) r = 255.f;
    if (g < 0.f) g = 0.f;
    if (g > 255.f) g = 255.f;
    if (b < 0.f) b = 0.f;
    if (b > 255.f) b = 255.f;

    mOutPixels[i] = ((int)r) + (((int)g) << 8) + (((int)b) << 16)
                    + (mInPixels[i] & 0xff000000);
}

このコードはビットマップがすでにロードされていて、処理のために整数配列に移されていることを想定している。ビットマップはすでにロードされているので、これは簡単だ。


    mInPixels = new int[mBitmapIn.getHeight() * mBitmapIn.getWidth()];
    mOutPixels = new int[mBitmapOut.getHeight() * mBitmapOut.getWidth()];
    mBitmapIn.getPixels(mInPixels, 0, mBitmapIn.getWidth(), 0, 0,
    mBitmapIn.getWidth(), mBitmapIn.getHeight());

データ処理のループが終われば、描画のためにビットマップに戻すことも簡単だ。


    mBitmapOut.setPixels(mOutPixels, 0, mBitmapOut.getWidth(), 0, 0,
               mBitmapOut.getWidth(), mBitmapOut.getHeight());

フィルター本体の定数計算や、(ボタンなどの)コントロールの制御、画像表示などのコードを含めて、アプリケーション全体のコード量は232行程度になる。手元の実機では800x423の画像処理におおよそ140-180msecかかる。


もしこれで十分でなかったらどうする?


画像処理のコア部分をRSに移植するのはとても簡単だ。上記のピクセル処理本体をRSで実装しなおすとこうなる。コードはhttp://code.google.com/p/android-renderscript-samples/source/browse/Levelsにある。


void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
    float3 pixel = convert_float4(in[0]).rgb;     // 3要素ベクトル
    pixel = rsMatrixMultiply(&colorMat, pixel);   // ライブラリ関数
    pixel = clamp(pixel, 0.f, 255.f);             // ライブラリ関数
    pixel = (pixel - inBlack) * overInWMinInB;    // ベクトル演算
    if (gamma != 1.0f)
        pixel = pow(pixel, (float3)gamma);        // ライブラリ関数
    pixel = pixel * outWMinOutB + outBlack;       // ベクトル演算
    pixel = clamp(pixel, 0.f, 255.f);             // ライブラリ関数
    out->xyz = convert_uchar3(pixel);             // ライブラリ関数
}

コード行数が極端に少なくて済むのは、浮動小数点のベクトルや行列演算、フォーマット変換があらかじめ組み込まれているからだ。また、ループが存在しないことに着目して欲しい。


準備のためのコードは、スクリプトをロードする必要があるので、ほんの少し複雑になる。


    mRS = RenderScript.create(this);
    mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
                                 Allocation.MipmapControl.MIPMAP_NONE,
                                 Allocation.USAGE_SCRIPT);
    mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut,
                                 Allocation.MipmapControl.MIPMAP_NONE,
                                 Allocation.USAGE_SCRIPT);
    mScript = new ScriptC_levels(mRS, getResources(), R.raw.levels);

このコードはRSのコンテキストを生成している。続いて、2つのメモリアロケーションをこのコンテキストを使って生成し、ビットマップデータのRS用コピーを保持する。最後にデータ処理のためにスクリプトをロードする。


ソースコードには、他にもいくつか小さなコードの塊があり、定数に変更があった時に計算し直してスクリプトへコピーしている。グローバル変数はスクリプトからリフレクションされているので、簡単に行える。


    mScript.set_inBlack(mInBlack);
    mScript.set_outBlack(mOutBlack);
    mScript.set_inWMinInB(mInWMinInB);
    mScript.set_outWMinOutB(mOutWMinOutB);
    mScript.set_overInWMinInB(mOverInWMinInB);

先に述べたように、すべてのピクセルを処理するためのループがない。ビットマップデータを処理し、結果をコピーするRSコードはこうなる。


    mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
    mOutPixelsAllocation.copyTo(mBitmapOut);

最初の行はスクリプトと入力アロケーションを取り出して、結果を保持する出力アロケーションを設定している。たったこれだけで、ネイティブにコンパイルされたバージョンのスクリプトを、アロケーションに入っているすべてのピクセルに対して、1つづつ一度だけ呼び出す。しかし、Dalvikの実装とは異なり、プリミティブ(コンパイルされたスクリプト)は自動的に複数のスレッドを生成して処理を行う。ネイティブコードの効率と合わさることで、大きなパフォーマンス向上を生んでいる。ガンマ関数のありなしで計算コストが大きく異なるので、両方の結果を下に示す。


800x423の画像

DeviceDalvikRSGain
Xoom174ms39ms4.5x
Galaxy Nexus139ms30ms4.6x
Tegra 30 device136ms19ms7.2x

ガンマ補正を加えた場合の800x423の画像

DeviceDalvikRSGain
Xoom994ms259ms3.8x
Galaxy Nexus787ms213ms3.7x
Tegra 30 device783ms104ms7.5x

ゲインが大きいほど、簡単なコーディングで得られる見返りが大きいことを示す。


訳注: Xoom, GN, Tegra 3というのは理にかなった選択。

Xoom: 2コア、VFP3

GN: 2コア、NEON

Tegra 3: 4コア、NEON

Tegra 30というのは、単なる記憶違いもしくはTegra 3が正式名称になるまえに30と呼び習わしていたのかもしれない。

2011-06-22

Android SDKのQEMUでArmv7対応

17:29 |  Android SDKのQEMUでArmv7対応 - Android Zaurusの日記 を含むブックマーク

ちょっと調べたいことがあって、masterツリーでビルドしてみたら、

  • TARGET_ARCH_VARIANT=armv7-a

になってて驚いた。ソースコードレビューを見てみると、

ということらしい。QEMUのパッチは

で取り込まれていたので、ビルドしてみて遊んだ。

$ cd master
$ repo sync -j4
$ source build/envsetup.sh
$ lunch full-eng
$ time make -j8

蛇足だけど、repo syncも-jで並列できる。ビルドしたバイナリファイルのアーキテクチャを確認。

$ prebuilt/linux-x86/toolchain/arm-eabi-4.4.0/bin/arm-eabi-readelf \
  -A \
  out/target/product/generic/system/bin/dalvikvm
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "ARM v7"
  Tag_CPU_arch: v7
  Tag_CPU_arch_profile: Application
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-2
  Tag_VFP_arch: VFPv3
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align8_needed: Yes
  Tag_ABI_enum_size: int
  Tag_ABI_HardFP_use: SP and DP

ビルドしたエミュレータを起動。$ANDROID_PRODUCT_OUTがlunchで設定されているので、引数なしで起動できる。

$ out/host/linux-x86/bin/emulator -wipe-data &

起動したらadbで覗いてみる。

$ adb shell cat /proc/cpuinfo
Processor       : ARMv7 Processor rev 0 (v7l)
BogoMIPS        : 331.77
Features        : swp half thumb fastmult vfp edsp neon vfpv3 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc08
CPU revision    : 0

Hardware        : Goldfish
Revision        : 0000
Serial          : 0000000000000000

ところで、現在のmasterツリーでビルドしたemulatorのbrowserは起動できない。Building MLにパッチがあるが、レビュー中のパッチをcherry pickしてみる。

$ cd bionic
$ git fetch git://android.git.kernel.org/platform/bionic \
  refs/changes/94/23894/3 && git checkout FETCH_HEAD
$ cd ..
$ time make -j8

QEMUパッチのコメントによると、emulatorの起動時間が短くなったらしいので、計測してみた。emulatorコマンドをンッターーーンッ!してからホーム画面が表示されるまでの時間。

tree初回起動2度目の起動
master1:190:27
gingerbread1:540:39

多少、早くなることが期待できる。あと、/proc/cpuinfoにあるとおり、NEONのエミュレーションにも対応しているが、愚直にx86コードへトランスレートしているだけなので遅くなるらしい。試してみた。TARGET_ARCH_VARIANTをexportで変更してもlunchの方が優先されるらしく、BoardConfig.mkを直接変更した。

 --- a/target/board/generic/BoardConfig.mk
 +++ b/target/board/generic/BoardConfig.mk
 @@ -18,7 +18,7 @@ TARGET_NO_KERNEL := true
  # that are slower to emulate. On the other hand, it is possible to emulate
  # application code generated with the NDK that uses NEON in the emulator.
  #
 -TARGET_ARCH_VARIANT := armv7-a
 +TARGET_ARCH_VARIANT := armv7-a-neon
  TARGET_CPU_ABI := armeabi-v7a
  TARGET_CPU_ABI2 := armeabi

まずはバイナリのアーキテクチャをチェック。

$ prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-readelf \
  -A \
 out/target/product/generic/system/bin/dalvikvm 
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "ARM v7"
  Tag_CPU_arch: v7
  Tag_CPU_arch_profile: Application
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-2
  Tag_VFP_arch: VFPv3
  Tag_NEON_arch: NEONv1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align8_needed: Yes
  Tag_ABI_enum_size: int
  Tag_ABI_HardFP_use: SP and DP

$  prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-readelf \
   -A \
   out/target/product/generic/system/lib/libjpeg.so 
Attribute Section: aeabi
File Attributes
  Tag_CPU_name: "ARM v7"
  Tag_CPU_arch: v7
  Tag_CPU_arch_profile: Application
  Tag_ARM_ISA_use: Yes
  Tag_THUMB_ISA_use: Thumb-2
  Tag_VFP_arch: VFPv3
  Tag_NEON_arch: NEONv1
  Tag_ABI_PCS_wchar_t: 4
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align8_needed: Yes
  Tag_ABI_enum_size: int
  Tag_ABI_HardFP_use: SP and DP

そして、動かしてみた。

treeneonSprite TextNeocore
masteroff55fps4.1fps
masteron55fps4.3fps
gingerbreadn/a30fps3.2fps

あれ?速くなってるし。なぞゆえ。gingerbreadよりmasterの方が速いのはこのところ少しずつ追加されているOpenGLトランスコードの効果らしい。関係ないと思われるが起動時間も。

tree初回起動2度目の起動
master-neon1:170:30

QEMUのArmv7対応はもう1つOSSらしい点がある。パッチのコメントにあるのだが、

  • upstreamはMeeGo
  • NEON対応はLinaroの成果

を取り入れていて、今後も相乗効果が期待できる。

2011-05-09

ccacheを使ったリビルドの高速化と、android_disk_vdiのビルド

17:39 |  ccacheを使ったリビルドの高速化と、android_disk_vdiのビルド - Android Zaurusの日記 を含むブックマーク

先月のAndroid Builders Summit*1に参加した際に、USE_CCACHEを使ったビルドの高速化*2の話があって、そういえば使ってなかったなと思い出して、また、tetsu_kobaさんがエントリ*3書いて、Android Buildingに凸ってJBQさんからキャッシュ増やしてねというお返事が来てた*4ので、試してみた。


$ source build/envsetup.sh
$ lunch full-eng
$ export USE_CCACHE=1
$ prebuilt/linux-x86/ccache/ccache -M 10G
$ make clean
$ time make -j8
$ make clean
$ time make -j8
$ prebuilt/linux-x86/ccache/ccache -s

1回目のビルドは26分ほど。2度目は13分ほどに縮まった。ccache -sのサイズは1.1GBと、デフォルトの1Gをちょっとだけ越えていたので、キャッシュサイズを増やしておくのは多少有効。


別のソースツリー(バージョン違いとか)で、再びビルドをしてみると、同様に2度目のビルド時間が短縮され、キャッシュサイズが2.0GBに増えていた。JBQさんの100GBは大袈裟にしても、保守しているビルドツリーの個数GBに設定しておくのは有効そうだ。


蛇足だが、このキャッシュサイズがどこに保存されているかわからなかったので、ccacheのソースコードを読んでみたら、$CCACHE_DIR/statsにテキストで書かれている値の15番目にブロックサイズ16Kbyteの値のようだ。


ただビルドして時間を測るだけではつまらないので、ついでに気になっていたandroid_disk_vdiをビルドしてみた。これは、現在のmasterリポジトリに入っているVirtualBoxで起動できるx86イメージをビルドするオプション。


$ lunch vbox_86-eng
$ time make -j8 android_disk_vdi

ビルドしたandroid_disk.vdiがどうにも起動しないので、色々と調べて*5こうすれば動かせるというのが分かった。


  • VirtualBoxで新しい仮想マシンを作成。システムはLinux 2.6でメモリは512MB推奨。
  • 作成したandroid_disk.vdiを、IDEのプライマリとして登録。
  • GrubメニューのStartup from /dev/sdaで起動。
  • ESCキーがBack
  • F1キーがMenu
  • F4キーがEnd
  • ロック画面からの復帰はF1キー
  • 電源オフはF4キー長押しのダイアログで電源オフを選択した後、仮想マシンの電源オフ。

マウスもネットワークも使えないので、はっきり言ってまったく使い物にならない。ネットワーク経由でのadbが使えるとREADME.TXTに書かれているが、つなぎ方が分からない。現状ではandroid-x86.orgのISOイメージからVirtualBoxにインストールした方が幸せだろう。


make installer_vdiというビルドも出来るようだ。これは、空ディスクにAndroidをインストールするイメージが作成できる。使い方は、


  • VirtualBoxで新しい仮想マシンを作成。システムはLinux 2.6でメモリは512MB推奨。
  • 2GB以上の新規の仮想ディスクを作成し、IDEのプライマリとして登録。
  • 作成したinstaller.vdiを、IDEのセカンダリとして登録。
  • 仮想マシンを起動して、すかさずF12でブートオプション。
  • Secondry IDEから起動して、GrubメニューでInstall to /dev/sdaを選択。
  • /dev/sda5がマウント出来ないというエラーで止まるので、電源オフ。
  • 再度、仮想マシンを起動して、F12でSecondry IDから起動して再度Installを選択。
  • 今度はインストールが正常終了するので、rebootと入力して再起動
  • GrubメニューのStartup from /dev/sdaで起動。

embeddedembedded 2011/05/09 18:11 ccache確認乙です。

androidのmasterでビルドすると、emulator_x86というのができるので、次のSDKのリリースあたりでは普通にQEMUベースでx86版が動かせるようになるかもしれません。

androidzaurusandroidzaurus 2011/05/10 09:33 I/Oで発表されるかもしれないGoogle TV SDKの準備じゃないかなと妄想してます。

2011-03-31

インストーレーションを区別するアプリケーション

17:39 | インストーレーションを区別するアプリケーション - Android Zaurusの日記 を含むブックマーク

公式ブログのエントリ「Installations Identifying App」をはてブのtwitter連携使って投げたら、なんかすごい勢いでクリックされたみたいなので訳してみた。誤訳とかあったら教えて。

インストーレーションを区別するアプリケーション

Tim Brayが投稿


この投稿の内容は、本ブログの執筆者たちがいつも不思議に感じていることについて内部での議論から生まれました

Androidのグループでは、信頼でき安定した端末識別子を取得するのに問題があるという不満を開発者から時折聞く。これは私たちを少し不安にさせる。なぜなら、そのような識別子を追跡することは良いアイディアではないと考えるし、開発者の目的を達成するのにより良い方法があるからだ。

インストーレーションを追跡

開発者がアプリケーションのインストーレーションを個別に追跡したいと考えるのは一般的だし、完全に理にかなっている。TelephonyManager.getDeviceId() を呼び出して、その値を識別に使うのは、ぱっと見よさそうに思えるかも知れない。これには問題がある。

  • 挙動が信頼できない。(後述)
  • きちんと動作したとしても、端末の初期化(工場出荷時への初期化)しても残るので、悲惨な間違いを犯してしまう危険性がある。例えばユーザが端末を初期化して、他の人に渡した時。

インストーレーションを追跡するには、例えばUUIDを識別子として使うことが出来る。アプリケーションがインストール後に初めて起動したときに、新しいのを作るだけだ。以下はInstallation.id(Context context)という静的メソッドを持つ"installation"というクラスの雛形だ。インストールに関連したデータをINSTALLATIONファイルにもっと書き込むこのができるのは容易に想像がつくだろう。

Installationクラスのソースコードは元記事を参照

端末を識別

アプリケーションが端末を識別する必要があるとして、実際のハードウェアデバイスIDを使うとどうなるだろう。これはトリッキーな問題だ。


過去、すべてのAndroid端末が電話だった頃は、事は単純だった。ネットワークの種類にに応じて、電話機のIMEI、MEID、ESNをTelephonyManager.getDeviceId()が返すことが必須だった。そしてその値はその端末に一意に決まる。

  • 電話以外: 携帯電話のハードウェアを持っていないWiFiのみの端末や音楽再生機は、この種のユニークな識別子を持っていない。
  • 固定値: この識別子を持っている端末では、端末初期化や工場出荷時への初期化をしても値が変わらない。それでも、アプリケーションからみて同じ端末であると扱わなければいけないかどうかは不明だ。
  • 特権: READ_PHONE_STATEパーミッションが必要になり、携帯電話機能を使わないし必要もないなら、厄介だ。
  • バグ: 実装に問題がある製品がいつくかあることが分かっていて、0や*といったゴミデータを返す。

物理アドレス

端末のWiFiやBluetoothのハードウェアから物理アドレスを取得することも考えられるだろう。しかし、ユニークな識別子として利用するのは推奨しない。まず、すべての端末がWiFiを搭載しているわけではない。また、WiFiの電源が入っていない場合、ハードウェアが物理アドレスを報告しないかもしれない。

シリアル番号

Android 2.3 (Gingerbread)以降では、シリアル番号がandroid.os.Build.SERIALが使える。携帯電話機能のない端末ではユニークな端末IDを返す。つまり、いくつかの端末ではこの方法が使える。

ANDROID_ID

ANDROID_ID、より正確に言うとSettings.Secure.ANDROID_IDは、64bit幅で、端末が最初に起動したときに生成され、端末に保存される。端末が初期化されるとリセットされる。


ANDROID_IDはユニークな端末識別子として良い選択に思える。しかし、適切でない面もある。

  • Android 2.2 (Froyo)以前のリリースでは、100%信頼できるものではない
  • 主要なメーカーのある人気の端末で、少なくとも1つ広く知られたバグがあった。その端末はすべての製品が同じANDROID_IDを持っている。

まとめ

ごく一般的に言って、アプリケーションは物理的な端末を識別する必要はなく、インストールされたことを区別すればいい。運よくそれは単純だ。


特定の端末を識別するのを避けることには、良い理由が多くある。どうしてもというのであれば、ANDROID_IDを使用するのはベストプラクティスだ。ただし、最近の機種に限定され、過去の端末に対応するにはバッドノウハウが必要になる。