LoginSignup
65
50

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-09-22

追記 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.javaensureSecureSettingAndroidIdSetLockedメソッドだね。

全体の処理は以下のような感じ。詳しくは見ないけどドキュメントどおり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進数文字列にしたもの」というのは表面上はあっているが、内部処理の話をすると「えー」と言いたくなる気分...

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

65
50
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
65
50