追記 2017/11/08
Android OからANDROID IDの仕様が変わってるので、この記事はO未満のバージョンに関しての記事になります。
どのように変わったのかはAndroid O での端末識別子の変更についてとか読むとよいですよ。
ただざっと見た感じAndroid OでもANDROID IDを生成する実装は変わってなさそうかも。
仕様変更でANDROID IDの管理方法が変わった感じなので、生成処理には影響してないと思われ(ちゃんと読まんとわからんが....
後々Android Oでの動作も調べますわ。
悪名高い??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進数文字列にしたもの」というのは表面上はあっているが、内部処理の話をすると「えー」と言いたくなる気分...
気になったら調べてみるもんだなー