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のバージョンが変わった際にまた条件が変わる可能性があるが、それでも必要の無いパーミションを要求してユーザの不審を買うよりは良いだろう。