ANDROID IDがどのように生成されているかざっくり調べた

## 追記 2017/11/08

Android OからANDROID IDの仕様が変わってるので、この記事はO未満のバージョンに関しての記事になります。

どのように変わったのかは[Android O での端末識別子の変更について](https://developers-jp.googleblog.com/2017/04/changes-to-device-identifiers-in.html)とか読むとよいですよ。

ただざっと見た感じAndroid OでもANDROID IDを生成する実装は変わってなさそうかも。

仕様変更でANDROID IDの管理方法が変わった感じなので、生成処理には影響してないと思われ(ちゃんと読まんとわからんが....

http://tools.oesf.biz/android-8.0.0_r1.0/xref/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java#2725

後々Android Oでの動作も調べますわ。

悪名高い？？ANDROID IDがどのように生成されているのか気になったのでざっくり調べた。

悪名高いと言われる闇の話は置いておいて....とりあえず生成方法だけざっくり調べた。

どのような手順で生成されるのか？という疑問はバージョンによって実装が異なるっぽかったので調べない。

## ANDROID IDとは

ドキュメント見てもらえばわかるけどざっくり以下のようなもの。

* ランダムに生成される64ビットの数値を16進数文字列にしたもの
* 端末の最初の設定時に生成される。その後、ファクトリーリセットするまではその時に生成された値が常に取得できる
* ファクトリーリセットすることで値は再生成される
* 4.2以上でmultiple usersが導入され、ユーザごとにANDROID IDが生成される

https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID


ANDROID IDは以下のような感じで簡単に取得できる。

```java
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
```


## ANDROID IDを使用する際の気をつけていること

ざっくり以下かなー他にもなにかあれば教えてほしいかなー

* 「ランダムに生成される64ビットの数値を16進数文字列にしたもの」なので**文字数は固定ではない**
* **どんなアプリでも同じ値がAPIを通して取得**できる
* **ファクトリーリセットすることで値が変わる**
* **同じANDROID IDを持った端末が存在する可能性**がある
* multiple users環境ではユーザごとにANDROID IDが生成されるため、ANDROID IDは端末に１つではない

## ANDROID IDがどのように生成されているのか

とりあえずAndroid Nのコードから見てみる。

ANDROID IDを生成しているのは[`SettingsProvider.java`](http://tools.oesf.biz/android-7.0.0_r1.0/xref/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java)の[`ensureSecureSettingAndroidIdSetLocked`](http://tools.oesf.biz/android-7.0.0_r1.0/xref/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java#1915)メソッドだね。

全体の処理は以下のような感じ。詳しくは見ないけどドキュメントどおりmultiple usersの想定もされてる実装になってる。

```java
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");
        }
    }
}
```

http://tools.oesf.biz/android-7.0.0_r1.0/xref/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java#1915


ANDROID IDの生成処理は以下のような感じで実装されてる。

```java
String androidId = Long.toHexString(new SecureRandom().nextLong());
```

**SecureRandomでランダムなlong値を生成して、それを`Long.toHexString`で16進数表現の文字列**にする。

それがANDROID IDってことになるね。

ちなみに`SecureRandom.nextLong`の実装は[Random.nextLong](https://developer.android.com/reference/java/util/Random.html#nextLong())なんだけど、ドキュメントと実装見ればわかるけど、long 値の一部しか返さないっぽいんだよね。

なのでlong値の幅 -9223372036854775808～9223372036854775807の中の一部ってことだね。うーん...思ってた以上に範囲が狭い気もする...

あと、`SecureRandom.nextLong`で**生成される値によって16進数文字列にした時の文字列の長さは変わる**。

雑に以下のようなやつを実行してみればわかるけど、文字列の長さが16だったり、15だったり、14だったりする。

```java
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`使ってる...まあいいや...

```java
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();
    }
}
```

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java#316

ちなみに、コードは割愛しますが2.3.7の実装はだいたい4.0.1と同じ。もちろん`SecureRandom.nextLong` + ` Long.toHexString`でANDROID IDが生成されてる。

http://tools.oesf.biz/android-2.3.7_r1.0/xref/frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java#322

その他のバージョンもざっくり見たけど、`SecureRandom.nextLong` + ` Long.toHexString`でANDROID IDが生成されていることは変わらない。当たり前か。


## まとめ

`Long.toHexString(new SecureRandom().nextLong())`で生成されるものがANDROID IDだねーということ

思ってたよりANDROID IDの生成処理があっさりしたもので驚きだったかなー

モヤモヤするのはドキュメントに書いてある「 ランダムに生成される64ビットの数値を16進数文字列にしたもの」というのは表面上はあっているが、内部処理の話をすると「えー」と言いたくなる気分...

気になったら調べてみるもんだなー