膠着状態

一体何時になったらNTLMがサポートされてこのスレッドが終了するのだろう。
Issue 4962 - android - NTLM authentication in browser - Project Hosting on Google Code

前にも書いたが企業ネットワークではNTLM認証が使われていることが多く、サポートしていないAndroidではネットワークに接続することができない。

オープンソースとはいえGoogleは意地になってスルーしてないか? と思わせる放置っぷりだ。

一筋の光明

「Android auで本気度を見せる」――KDDIがスマートフォン新機種「IS03」発表
さすが国産! “気配りのスマートフォン”au「IS03」の全貌
CEATEC JAPAN 2010 - シャープ、IS03やGALAPAGOSを展示

探せばリンクはいくらでもあるが、とにかくSHARPである。発表前から写真もリークされていたし噂は入ってきていたのだが、実際に出してくれたそのやる気と勇気に感謝したい。

日記で何度も書いているが、世界的に盛上がっているAndroid界にあって隆盛を極めているメーカは同じアジアでも台湾と韓国であり日本の現状はお寒いものであった。「あった」と過去形にしたのは、ここにきてようやく日本のメーカがきちんとした訴求力のあるAndroid端末を出してきているからだ。まだ日本は大丈夫だと思わせるニュースだった。

使わない機能があるという指摘もあるだろうが(少なくとも私はワンセグやらキャリアのメールやらは不要)iPhone4に負けない液晶とFeliCaAndroidにパッケージしたことだけで価値があると思う。

ファミリー割引の主契約者である事情もあり、キャリアをdocomo以外に変えられない私はIS03は購入できないが、IS01に対してLYNXが出たようにdocomoから同様の端末が発売されたならば、まだ一年以上残っているHT-03Aの違約金を払ってでも買い換えようと思っている。

謎すぎるInstrumentationのActivityThread

ActivityInstrumentationTestCase2(長いな)を使ってActivityをテストしていたのだが、独自に拡張したApplicationクラスのonCreateメソッドが呼ばれていない(ブレークポイントに引っかからない)

ならばApplicationのインスタンスはどこで生成されているのだろうと追ってみたのだが、

  • ActivityThread.java#makeApplication
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }
    Application app = null;

    String appClass = mApplicationInfo.className;
    if (forceDefaultAppClass || (appClass == null)) {
        appClass = "android.app.Application";
    }

    try {
        java.lang.ClassLoader cl = getClassLoader();
        ContextImpl appContext = new ContextImpl();
        appContext.init(this, null, mActivityThread);
        app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
        appContext.setOuterContext(app);

〜以下略〜
 
    return app;
}

間違い無くこのメソッドだが、実際にデバッグしてみると初回起動時はnullのはずのmApplicationには既にApplicationクラスのインスタンスがセットされていたりするのだ。

//何故か最初にブレークした段階でmApplicationは非null
if (mApplication != null) {
    return mApplication;
}

これはまだ推測だが、instrumentationを使う場合、デバッガがアタッチする前にフレームワーク側でアプリケーションを生成しちまうんだろうと思われるが、ということは同クラスではデバッグでブレークすることは不可能ということになる。
古のしかし確実なログ出力で実際には全てのメソッドが実行されていることは確認できたが、一日無駄にしたこともあり納得がいかない。

Froyo(Android 2.2)でもANDROID_IDでエミュレータを判定する

先日のコード

String android_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
if (android_id == null) {
    //エミュレータ
} else {
    //実機
}

Froyoのエミュレータはnullを返さなくなったのでエミュレータの判定には使えないと書いたが、何時どの環境から実行しても同じ値を返すのであれば、単にその値をエミュレータであることの証にすれば良いだけなのではないか。

final String ANDROID_ID_EMULATOR = "9774d56d682e549c";

boolean isInEmulator;
String android_id = Secure.getString(this.getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
if ( VERSION.SDK_INT < 8 ) {
    isInEmulator = (android_id == null); 
} else {
    isInEmulator = (android_id.equals(ANDROID_ID_EMULATOR)); 
}
//isInEmulatorが真ならばエミュレータ

このANDROID_IDだが、実際にどこで設定されているのかを調べてみた。

    • com.android.providers.settings.SettingsProvider#ensureAndroidIdIsSet
〜
final SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
String serial = SystemProperties.get("ro.serialno");
if (serial != null) {
    try {
        random.setSeed(serial.getBytes("UTF-8"));
    } catch (UnsupportedEncodingException ignore) {
        // stick with default seed
    }
}
final String newAndroidIdValue = Long.toHexString(random.nextLong());
Log.d(TAG, "Generated and saved new ANDROID_ID");
final ContentValues values = new ContentValues();
values.put(Settings.NameValueTable.NAME, Settings.Secure.ANDROID_ID);
values.put(Settings.NameValueTable.VALUE, newAndroidIdValue);
final Uri uri = insert(Settings.Secure.CONTENT_URI, values);
〜

システムプロパティro.serialnoの値をシードにして、セキュアランダムから疑似乱数を生成している訳で、シードになっているro.serialno(製品のシリアルNo.か?)が同じならば結果も同じになるので間違いなさそうだ。

アドホックだしAndroidのバージョンが変わった際にまた条件が変わる可能性があるが、それでも必要の無いパーミションを要求してユーザの不審を買うよりは良いだろう。

ANDROID_IDでエミュレータを判定する方法はFroyoからは使えない

Androidアプリケーションにおいてエミュレータで実行しているか否かを検出するために以下のコードがよく使われる。

String android_id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
if (android_id == null) {
    //エミュレータ
} else {
    //実機
}

Secure.ANDROID_IDはリファレンスには以下のように記述されている。

public static final String ANDROID_ID 

Since: API Level 3 A 64-bit number (as a hex string) that is randomly generated on the device's first boot and should remain constant for the lifetime of the device. (The value may change if a factory reset is performed on the device.) 

ファクトリリセットされた後の初回起動時に乱数生成された64bitの16進数がセットされるとある。ネットではAndroid Marketにアクセスされた際に付与されるという記述も散見されるが、リファレンスが正しいとすれば「生成された数値がAndroid Marketでも使われる」ではないだろうか。

コードに話を戻すと機器毎に乱数値が戻るはずの所エミュレータでは何度起動しても取得される値はnullであり、今まではそれを利用してエミュレータか否かを判断していた訳だ。

ところが最新のAnrdoid 2.2(Froyo)で実行するとエミュレータでは固定で"9774D56D682E549C"※という値を返すように変わっており今まで通りの判定ができない。つまりこの方法は使えないということになる。

代替えとしては、

    • Activity内にて
boolean isEmulator = false; 
TelephonyManager tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
if ( tm != null ) { 
    String devId = tm.getDeviceSoftwareVersion(); 
    isEmulator = ( devId == null ); 
} 
//isEmulatorが真ならばエミュレータ

という手も使えるらしいのだが、マニフェストにPHONE_STATEパーミションの許可が必要になってしまうのが嫌だ。
さてどうしたものか。

※私の環境ではという話。ひょっとしたら他の環境、OSでは違うのかもしれない。

こいつのせいか〜

Android 推奨 ADT 0.9.9へのアップデート - タオソフトウェアブログ

ADT Plugin for Eclipse 0.9.9
General notes:
•Fixes a problem in project import, in which source files were deleted in some cases.
•Includes all other ADT 0.9.8 features (see below).

最近、あるはずのソースコードの読込に失敗したり、コンパイルが上手くいかないことがよくあった(Eclipseの再起動で治ったりする)が、知らない内にバグFix版が出ていたとは。

>きっと私と同じようにアップデート忘れている方がいると思ってのブログ記事でした。
全く以てその通りでした。

追記:
元記事にトラックバックしたいんだけど、403になってしまう。無効にしているのかな。
http://www.taosoftware.co.jp/mt/mt-tb.cgi/767 へのトラックバックに失敗しました。 (403 Throttled )