ANDROID IDがどのように生成されているかざっくり調べた
悪名高い??ANDROID IDがどのように生成されているのか気になったのでざっくり調べた。
悪名高いと言われる闇の話は置いておいて....とりあえず生成方法だけざっくり調べた。
どのような手順で生成されるのか?という疑問はバージョンによって実装が異なるっぽかったので調べない。
ANDROID IDとは
ドキュメント見てもらえばわかるけどざっくり以下のようなもの。
- ランダムに生成される64ビットの数値を16進数文字列にしたもの
- 端末の最初の設定時に生成される。その後、ファクトリーリセットするまではその時に生成された値が常に取得できる
- ファクトリーリセットすることで値は再生成される
- 4.2以上でmultiple usersが導入され、ユーザごとにANDROID IDが生成される
ANDROID IDは以下のような感じで簡単に取得できる。
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
ANDROID IDを使用する際の気をつけていること
ざっくり以下かなー他にもなにかあれば教えてほしいかなー
- 「ランダムに生成される64ビットの数値を16進数文字列にしたもの」なので文字数は固定ではない
- どんなアプリでも同じ値がAPIを通して取得できる
- ファクトリーリセットすることで値が変わる
- 同じANDROID IDを持った端末が存在する可能性がある
- multiple users環境ではユーザごとにANDROID IDが生成されるため、ANDROID IDは端末に1つではない
ANDROID IDがどのように生成されているのか
とりあえずAndroid Nのコードから見てみる。
ANDROID IDを生成しているのはSettingsProvider.java
のensureSecureSettingAndroidIdSetLocked
メソッドだね。
全体の処理は以下のような感じ。詳しくは見ないけどドキュメントどおりmultiple usersの想定もされてる実装になってる。
private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) { Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID); if (!value.isNull()) { return; } final int userId = getUserIdFromKey(secureSettings.mKey); final UserInfo user; final long identity = Binder.clearCallingIdentity(); try { user = mUserManager.getUserInfo(userId); } finally { Binder.restoreCallingIdentity(identity); } if (user == null) { // Can happen due to races when deleting users - treat as benign. return; } String androidId = Long.toHexString(new SecureRandom().nextLong()); secureSettings.insertSettingLocked(Settings.Secure.ANDROID_ID, androidId, SettingsState.SYSTEM_PACKAGE_NAME); Slog.d(LOG_TAG, "Generated and saved new ANDROID_ID [" + androidId + "] for user " + userId); // Write a drop box entry if it's a restricted profile if (user.isRestricted()) { DropBoxManager dbm = (DropBoxManager) getContext().getSystemService( Context.DROPBOX_SERVICE); if (dbm != null && dbm.isTagEnabled(DROPBOX_TAG_USERLOG)) { dbm.addText(DROPBOX_TAG_USERLOG, System.currentTimeMillis() + "," + DROPBOX_TAG_USERLOG + "," + androidId + "\n"); } } }
ANDROID IDの生成処理は以下のような感じで実装されてる。
String androidId = Long.toHexString(new SecureRandom().nextLong());
SecureRandomでランダムなlong値を生成して、それをLong.toHexString
で16進数表現の文字列にする。
それがANDROID IDってことになるね。
ちなみにSecureRandom.nextLong
の実装はRandom.nextLong)なんだけど、ドキュメントと実装見ればわかるけど、long 値の一部しか返さないっぽいんだよね。
なのでlong値の幅 -9223372036854775808~9223372036854775807の中の一部ってことだね。うーん...思ってた以上に範囲が狭い気もする...
あと、SecureRandom.nextLong
で生成される値によって16進数文字列にした時の文字列の長さは変わる。
雑に以下のようなやつを実行してみればわかるけど、文字列の長さが16だったり、15だったり、14だったりする。
for (int i = 0; i < 100000; i++) { String androidId = Long.toHexString(new SecureRandom().nextLong()); System.out.println(androidId.length()); }
これが最初の気にしておかなければいけない点で書いた「ランダムに生成される64ビットの数値を16進数文字列にしたもの」なので文字数は固定ではないってこと。
他のAndroidのバージョンではどう実装されているか
全部のバージョン見るのは面倒なので、いくつかのバージョンだけ。
4.0.1ではensureAndroidIdIsSet
メソッドでANDROID IDが生成されてる。
もちろんSecureRandom.nextLong
+ Long.toHexString
でANDROID IDが生成されてる。
てかSlog.d
じゃなくて普通のLog.d
使ってる...まあいいや...
private boolean ensureAndroidIdIsSet() { final Cursor c = query(Settings.Secure.CONTENT_URI, new String[] { Settings.NameValueTable.VALUE }, Settings.NameValueTable.NAME + "=?", new String[] { Settings.Secure.ANDROID_ID }, null); try { final String value = c.moveToNext() ? c.getString(0) : null; if (value == null) { final SecureRandom random = new SecureRandom(); final String newAndroidIdValue = Long.toHexString(random.nextLong()); Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]"); 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); if (uri == null) { return false; } } return true; } finally { c.close(); } }
ちなみに、コードは割愛しますが2.3.7の実装はだいたい4.0.1と同じ。もちろんSecureRandom.nextLong
+ Long.toHexString
でANDROID IDが生成されてる。
その他のバージョンもざっくり見たけど、SecureRandom.nextLong
+ Long.toHexString
でANDROID IDが生成されていることは変わらない。当たり前か。
まとめ
Long.toHexString(new SecureRandom().nextLong())
で生成されるものがANDROID IDだねーということ
思ってたよりANDROID IDの生成処理があっさりしたもので驚きだったかなー
モヤモヤするのはドキュメントに書いてある「 ランダムに生成される64ビットの数値を16進数文字列にしたもの」というのは表面上はあっているが、内部処理の話をすると「えー」と言いたくなる気分...
気になったら調べてみるもんだなー