固有のAndroidデバイスIDはありますか?


回答:


2026

Settings.Secure#ANDROID_IDAndroid ID を各ユーザーの一意の 64ビット16進文字列として返します。

import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID); 

477
ヌルであることがわかっている場合があり、「出荷時設定にリセットすると変更できる」と記載されています。自己責任で使用してください。根ざした電話で簡単に変更できます。
Seva Alekseyev、2010年


18
ANDROID_IDをハッシュで最初の回答で使用する場合は注意が必要だと思います。これは、アプリの初回実行時に設定されなかったり、後で設定されたり、理論的には変更されたりする可能性があるため、一意のIDが変更される可能性があるためです

46
この溶液を用いた巨大な限界がある点に注意してください。android-developers.blogspot.com/2011/03/...
emmby

35
ANDROID_IDがデバイスを一意に識別しなくなりました(4.2以降):stackoverflow.com/a/13465373/150016
Tom

1146

更新:Androidの最近のバージョンでは、に関する多くの問題ANDROID_IDが解決されており、このアプローチはもはや必要ないと考えています。アンソニーの答えを見てください。

完全な開示:私のアプリはもともと以下のアプローチを使用していましたが、現在はこのアプローチを使用していません。現在は、emmbyの回答リンクへAndroidデベロッパーブログエントリで概説されているアプローチ(つまり、の生成と保存)を使用しています。UUID#randomUUID()


この質問には多くの答えがありますが、そのほとんどは「一部」の時間でしか機能しませんが、残念ながらそれでは不十分です。

デバイスのテストに基づいて(すべての電話、少なくとも1つはアクティブ化されていません):

  1. テストしたすべてのデバイスが次の値を返しました TelephonyManager.getDeviceId()
  2. すべてのGSMデバイス(すべてSIMでテスト済み)が次の値を返しました TelephonyManager.getSimSerialNumber()
  3. すべてのCDMAデバイスがgetSimSerialNumber()(予想どおり)に対してnullを返しました
  4. Googleアカウントが追加されたすべてのデバイスが次の値を返しました ANDROID_ID
  5. すべてのCDMAデバイスは、両方に同じ値(または同じ値の導出を)返されたANDROID_IDTelephonyManager.getDeviceId()- 限りのGoogleアカウントをセットアップ中に追加されました。
  6. SIMがないGSMデバイス、Googleアカウントが追加されていないGSMデバイス、または機内モードのデバイスをテストする機会はまだありませんでした。

あなたは、デバイス自体に固有の何かをしたいのであれば、TM.getDeviceId() 必要があります十分で。明らかに、一部のユーザーは他のユーザーより偏執的であるため、これらの識別子の1つ以上をハッシュすると、文字列がデバイスに対して実質的に一意でありながら、ユーザーの実際のデバイスを明示的に識別できない場合があるため便利です。たとえばString.hashCode()、UUIDと組み合わせて使用します。

final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE);

final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
tmSerial = "" + tm.getSimSerialNumber();
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);

UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();

次のような結果になる可能性があります: 00000000-54b3-e7c7-0000-000046bffd97

それは私には十分に機能します。

Richardが以下で言及するように、TelephonyManagerプロパティを読み取るためのアクセス許可が必要であることを忘れないでください。そのため、これをマニフェストに追加してください:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

インポートライブラリ

import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;

151
テレフォニーベースのIDはタブレットデバイスにはありませんね。
Seva Alekseyev、2010年

22
したがって、ほとんどの場合常に機能するとは限らないと言った理由:)この質問に対するすべてのデバイス、すべてのデバイスタイプ、およびすべてのハードウェア構成に対して信頼できる回答はまだ見ていません。それが、この質問がそもそもここにある理由です。これに対する万能の解決策がないことは明らかです。個々のデバイスの製造元にはデバイスのシリアル番号がある場合がありますが、それらは私たちが使用するために公開されておらず、必須ではありません。したがって、利用できるものは残ります。
Joe

31
コードサンプルはうまく機能します。<uses-permission android:name="android.permission.READ_PHONE_STATE" />マニフェストファイルに必ず追加してください。データベースに格納する場合、返される文字列は36文字です。
リチャード

10
この溶液を用いた巨大な限界がある点に注意してください。android-developers.blogspot.com/2011/03/...
emmby

18
@softarn:あなたが言及しているのは、emmbyがすでにリンクしているAndroidデベロッパーブログだと思います。これは、あなたが言おうとしていることを説明しているので、代わりに単に彼のコメントに賛成するべきでした。いずれにせよ、emmbyが彼の回答で言及しているように、ブログ情報にも問題が残っています。質問は一意のデバイス識別子(インストール識別子ではない)を要求するので、あなたの発言には同意しません。ブログは、あなたが望むのは必ずしもデバイスを追跡することではないことを前提にしていますが、質問はそれだけを求めています。それ以外の場合は、ブログに同意します。
Joe

439

最終更新日:2015年6月2日


一意のIDの作成に関するすべてのStack Overflowの投稿、Google開発者のブログ、Androidのドキュメントを読んだ後、「疑似ID」が最良のオプションであるかのように感じます。

主な問題:ハードウェアとソフトウェア

ハードウェア

  • ユーザーはハードウェア、Androidタブレット、または電話を変更できるため、ハードウェアに基づく一意のIDは、ユーザーの追跡には適していません。
  • 以下のためにトラッキング用ハードウェアは、これは素晴らしいアイディアです

ソフトウェア

  • ルート権限を取得している場合、ユーザーはROMをワイプまたは変更できます
  • プラットフォーム全体(iOS、Android、Windows、およびWeb)でユーザーを追跡できます
  • 同意を得て個々のユーザー追跡するのが最善の方法は、単にログインさせることです(OAuthを使用してこれをシームレスにします)。

Androidの全体的な内訳

-9/10以上のAPI(Androidデバイスの99.5%)の一意性を保証(ルート化されたデバイスを含む)

-追加の権限はありません

擬似コード:

if API >= 9/10: (99.5% of devices)

return unique ID containing serial id (rooted devices may be different)

else

return the unique ID of build information (may overlap data - API < 9)

すべてのオプションを投稿してくれた@stansultに感謝します(このStack Overflowの質問に)。

オプションのリスト-使用する理由/使用しない理由:

  • ユーザーのメール-ソフトウェア

  • ユーザーの電話番号-ソフトウェア

    • ユーザーが電話番号を変更する可能性-ほとんどない
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI-ハードウェア(電話のみ、必要android.permission.READ_PHONE_STATE

    • ほとんどのユーザーは、許可に「電話」と書かれていることを嫌っています。一部のユーザーは悪い評価を与えます。なぜなら、本当にしたいのはデバイスのインストールを追跡することだけであるのに、単に個人情報を盗んでいると信じているからです。データを収集していることは明らかです。
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • Android ID-ハードウェア(nullの可能性、出荷時設定へのリセット時に変更可能、ルート権限を取得されたデバイスで変更可能)

    • 「null」になる可能性があるため、「null」をチェックしてその値を変更できますが、これは一意ではなくなります。
    • 工場出荷時の状態にリセットされたデバイスを持つユーザーがいる場合、ルート化されたデバイスで値が変更または変更されている可能性があるため、ユーザーインストールを追跡している場合、重複したエントリが存在する可能性があります。
  • WLAN MACアドレス-ハードウェア(が必要android.permission.ACCESS_WIFI_STATE

    • これは2番目に良いオプションかもしれませんが、ユーザーから直接来る一意の識別子を収集して保存しています。これは、データを収集していることは明らかです。
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Bluetooth MACアドレス-ハードウェア(Bluetooth対応デバイス、ニーズandroid.permission.BLUETOOTH

    • 市場に出回っているほとんどのアプリケーションはBluetoothを使用していないため、アプリケーションがBluetoothを使用しておらず、これを組み込んでいる場合、ユーザーは疑わしいものになる可能性があります。
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • 疑似固有ID-ソフトウェア(すべてのAndroidデバイス用)

    • 非常に可能ですが、衝突が含まれている可能性があります-以下に掲載されている私の方法をご覧ください
    • これにより、プライベートなものを一切使用せずに、ユーザーから「ほぼ一意の」IDを取得できます。デバイス情報から独自の匿名IDを作成できます。

権限を使用せずに一意のIDを取得する「完全な」方法はないことを知っています。ただし、デバイスのインストールを追跡するだけで十分な場合もあります。一意のIDを作成する場合、追加の権限を使用せずに、Android APIから提供される情報のみに基づいて「疑似一意ID」を作成できます。このようにして、ユーザーの尊敬を示し、優れたユーザーエクスペリエンスを提供することもできます。

疑似一意IDを使用すると、類似のデバイスがあるという事実に基づいて重複が発生する可能性があるという事実に遭遇するだけです。組み合わせたメソッドを調整して、よりユニークにすることができます。ただし、一部の開発者はデバイスのインストールを追跡する必要があり、これは同様のデバイスに基づいたトリックまたはパフォーマンスを実行します。

API> = 9:

AndroidデバイスがAPI 9以上の場合、 'Build.SERIAL'フィールドにより、これは一意であることが保証されます。

覚えておいてください。あなたは技術的にAPIが9未満のユーザーの約0.5%だけを見逃しています。残りの部分に集中できます。これはユーザーの99.5%です。

API <9:

ユーザーのAndroidデバイスがAPI 9より低い場合。うまくいけば、彼らは出荷時設定へのリセットを行っておらず、「Secure.ANDROID_ID」は保持されるか「null」にならないでしょう。(http://developer.android.com/about/dashboards/index.htmlを参照してください)

他のすべてが失敗した場合:

他のすべてが失敗した場合、ユーザーがAPI 9よりも低い(Gingerbreadよりも低い)場合、デバイスをリセットするか、「Secure.ANDROID_ID」が「null」を返します。返されるIDは、Androidデバイスの情報のみに基づいています。これは、衝突が発生する可能性がある場所です。

変更:

  • 出荷時設定にリセットすると値が変更される可能性があるため、「Android.SECURE_ID」を削除しました
  • コードを編集してAPIを変更しました
  • 擬似を変更しました

以下の方法をご覧ください。

/**
 * Return pseudo unique ID
 * @return ID
 */
public static String getUniquePsuedoID() {
    // If all else fails, if the user does have lower than API 9 (lower
    // than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
    // returns 'null', then simply the ID returned will be solely based
    // off their Android device information. This is where the collisions
    // can happen.
    // Thanks http://www.pocketmagic.net/?p=1662!
    // Try not to use DISPLAY, HOST or ID - these items could change.
    // If there are collisions, there will be overlapping data
    String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);

    // Thanks to @Roman SL!
    // https://stackoverflow.com/a/4789483/950427
    // Only devices with API >= 9 have android.os.Build.SERIAL
    // http://developer.android.com/reference/android/os/Build.html#SERIAL
    // If a user upgrades software or roots their device, there will be a duplicate entry
    String serial = null;
    try {
        serial = android.os.Build.class.getField("SERIAL").get(null).toString();

        // Go ahead and return the serial for api => 9
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    } catch (Exception exception) {
        // String needs to be initialized
        serial = "serial"; // some value
    }

    // Thanks @Joe!
    // https://stackoverflow.com/a/2853253/950427
    // Finally, combine the values we have found by using the UUID class to create a unique identifier
    return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}

新機能(広告とGoogle Play開発者サービスのあるアプリの場合):

Google Playデベロッパーコンソールから:

2014年8月1日以降、Google Playデベロッパープログラムポリシーでは、すべての新しいアプリのアップロードと更新で、広告目的で他の永続的な識別子の代わりに広告IDを使用する必要があります。もっと詳しく知る

実装

許可:

<uses-permission android:name="android.permission.INTERNET" />

コード:

import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
import com.google.android.gms.common.GooglePlayServicesAvailabilityException;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import java.io.IOException;
...

// Do not call this function from the main thread. Otherwise, 
// an IllegalStateException will be thrown.
public void getIdThread() {

  Info adInfo = null;
  try {
    adInfo = AdvertisingIdClient.getAdvertisingIdInfo(mContext);

  } catch (IOException exception) {
    // Unrecoverable error connecting to Google Play services (e.g.,
    // the old version of the service doesn't support getting AdvertisingId).

  } catch (GooglePlayServicesAvailabilityException exception) {
    // Encountered a recoverable error connecting to Google Play services. 

  } catch (GooglePlayServicesNotAvailableException exception) {
    // Google Play services is not available entirely.
  }
  final String id = adInfo.getId();
  final boolean isLAT = adInfo.isLimitAdTrackingEnabled();
}

ソース/ドキュメント:

http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html

重要:

Google Play開発者サービスが利用可能な場合、広告IDは、他の識別子の既存の使用目的(Settings.SecureでのANDROID_IDの使用など)を完全に置き換えることを目的としています。Google Play開発者サービスが利用できない場合は、getAdvertisingIdInfo()によってスローされるGooglePlayServicesNotAvailableExceptionによって示されます。

警告、ユーザーはリセットできます:

http://en.kioskea.net/faq/34732-android-reset-your-advertising-id

私は私が情報を得たすべてのリンクを参照しようとしました。あなたが行方不明で含まれている必要がある場合は、コメントしてください!

GoogleプレーヤーサービスのインスタンスID

https://developers.google.com/instance-id/


しかしBuild、OSの更新時にクラスは変更されませんか?特にAPIが更新された場合はどうなりますか?もしそうなら、どのようにこれがユニークであることを保証しますか?(あなたが書いた方法について話す)
LuckyMe 2013

2
私は私のアプリであなたのメソッドを使用してコメントを送信しました。悪いニュースがある。残念ながら、PsuedoIDは完全に一意ではありません。私のサーバーは、5つのIDで100を超え、ほぼ30のIDで30を超えています。最も繰り返されるIDは、「ffffffff-fc8f-6093-ffff-ffffd8」(159レコード)と「ffffffff-fe99-b334-ffff-ffffef」(154時間)です。また、時間とコメントに基づいて、さまざまな人々がいることは明らかです。現在までの累計は10,000件。なぜこうなったのか教えてください。タンク。
hojjat reyhane 2015年

1
これは1.5年以上前に書いたものです。なぜそれがあなたにとってユニークではないのか分かりません。広告IDをお試しいただけます。そうでない場合は、独自のソリューションを考え出すことができます。
Jared Burrows

2
sorta ..質問に目を通し、これについてあなたの考えを述べていただければ幸いです
Durai Amuthan.H

1
@ user1587329ありがとうございます。私は皆のためにこれを最新に保つように努めています。この質問は、ハードウェアとソフトウェア、およびクロスプラットフォームに関しては注意が必要です。
Jared Burrows、

340

Dave Webbが言及しているように、Android開発者ブログにはこれをカバーする記事があります。彼らが好むソリューションは、デバイスではなくアプリのインストールを追跡することです。これは、ほとんどのユースケースでうまく機能します。ブログの投稿では、その機能を実現するために必要なコードを紹介しています。ぜひチェックしてみてください。

ただし、ブログの投稿では、アプリのインストールIDではなくデバイスIDが必要な場合の解決策について説明しています。Googleの担当者と話し合って、必要な場合に備えていくつかの項目についてさらに説明を求めました。前述のブログ投稿で言及されていないデバイス識別子について私が発見したものは次のとおりです。

  • ANDROID_IDは優先デバイス識別子です。ANDROID_IDは、Android <= 2.1または> = 2.3のバージョンで完全に信頼できます。投稿で言及されている問題があるのは2.2だけです。
  • いくつかの製造元によるいくつかのデバイスは、2.2のANDROID_IDバグの影響を受けます。
  • 私が判断できる限り、影響を受けるすべてのデバイスは同じANDROID_IDを持ってます。これは9774d56d682e549cです。これは、エミュレーターによって報告された同じデバイスIDでもあります。
  • GoogleはOEMがデバイスの多くまたはほとんどのデバイスにこの問題にパッチを適用していると考えていますが、2011年4月の初めの時点で、少なくともANDROID_IDが壊れているデバイスを見つけるのは非常に簡単であることを確認できました。

Googleの推奨事項に基づいて、必要に応じてANDROID_IDをシードとして使用し、必要に応じてTelephonyManager.getDeviceId()にフォールバックして、デバイスごとに一意のUUIDを生成するクラスを実装し、失敗した場合はランダムに生成された一意のUUIDを使用しましたアプリの再起動後も保持されます(アプリの再インストールは不可)。

デバイスIDにフォールバックする必要があるデバイスのために、ユニークなIDがいることを注意WILLファクトリーリセットしても保持されます。これは知っておくべきことです。出荷時設定にリセットして一意のIDを確実にリセットする必要がある場合は、デバイスIDではなくランダムなUUIDに直接フォールバックすることを検討してください。

繰り返しますが、このコードはアプリのインストールIDではなく、デバイスID用です。ほとんどの場合、アプリインストールIDがおそらくあなたが探しているものです。ただし、デバイスIDが必要な場合は、次のコードがおそらく機能します。

import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;

import java.io.UnsupportedEncodingException;
import java.util.UUID;

public class DeviceUuidFactory {

    protected static final String PREFS_FILE = "device_id.xml";
    protected static final String PREFS_DEVICE_ID = "device_id";
    protected volatile static UUID uuid;

    public DeviceUuidFactory(Context context) {
        if (uuid == null) {
            synchronized (DeviceUuidFactory.class) {
                if (uuid == null) {
                    final SharedPreferences prefs = context
                            .getSharedPreferences(PREFS_FILE, 0);
                    final String id = prefs.getString(PREFS_DEVICE_ID, null);
                    if (id != null) {
                        // Use the ids previously computed and stored in the
                        // prefs file
                        uuid = UUID.fromString(id);
                    } else {
                        final String androidId = Secure.getString(
                            context.getContentResolver(), Secure.ANDROID_ID);
                        // Use the Android ID unless it's broken, in which case
                        // fallback on deviceId,
                        // unless it's not available, then fallback on a random
                        // number which we store to a prefs file
                        try {
                            if (!"9774d56d682e549c".equals(androidId)) {
                                uuid = UUID.nameUUIDFromBytes(androidId
                                        .getBytes("utf8"));
                            } else {
                                final String deviceId = (
                                    (TelephonyManager) context
                                    .getSystemService(Context.TELEPHONY_SERVICE))
                                    .getDeviceId();
                                uuid = deviceId != null ? UUID
                                    .nameUUIDFromBytes(deviceId
                                            .getBytes("utf8")) : UUID
                                    .randomUUID();
                            }
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                        // Write the value out to the prefs file
                        prefs.edit()
                                .putString(PREFS_DEVICE_ID, uuid.toString())
                                .commit();
                    }
                }
            }
        }
    }

    /**
     * Returns a unique UUID for the current android device. As with all UUIDs,
     * this unique ID is "very highly likely" to be unique across all Android
     * devices. Much more so than ANDROID_ID is.
     * 
     * The UUID is generated by using ANDROID_ID as the base key if appropriate,
     * falling back on TelephonyManager.getDeviceID() if ANDROID_ID is known to
     * be incorrect, and finally falling back on a random UUID that's persisted
     * to SharedPreferences if getDeviceID() does not return a usable value.
     * 
     * In some rare circumstances, this ID may change. In particular, if the
     * device is factory reset a new device ID may be generated. In addition, if
     * a user upgrades their phone from certain buggy implementations of Android
     * 2.2 to a newer, non-buggy version of Android, the device ID may change.
     * Or, if a user uninstalls your app on a device that has neither a proper
     * Android ID nor a Device ID, this ID may change on reinstallation.
     * 
     * Note that if the code falls back on using TelephonyManager.getDeviceId(),
     * the resulting ID will NOT change after a factory reset. Something to be
     * aware of.
     * 
     * Works around a bug in Android 2.2 for many devices when using ANDROID_ID
     * directly.
     * 
     * @see http://code.google.com/p/android/issues/detail?id=10603
     * 
     * @return a UUID that may be used to uniquely identify your device for most
     *         purposes.
     */
    public UUID getDeviceUuid() {
        return uuid;
    }
}

6
さまざまなIDをハッシュして、すべて同じサイズにする必要はありませんか?また、誤って個人情報を公開しないように、デバイスIDをハッシュする必要があります。
Steve Pomeroy、2011

2
良い点、スティーブ。常にUUIDを返すようにコードを更新しました。これにより、a)生成されたIDが常に同じサイズであり、b)誤って個人情報が公開されないように、返される前にAndroid IDとデバイスIDがハッシュされます。また、説明を更新して、デバイスIDは出荷時設定にリセットしても保持され、一部のユーザーにとっては望ましくない可能性があることに注意してください。
emmby

1
私はあなたが間違っていると思います。推奨される解決策は、デバイス識別子ではなくインストールを追跡することです。あなたのコードはブログの投稿よりもはるかに長く複雑であり、それが何らかの価値を追加するかどうかは私には明らかではありません。
Tim Bray、

7
ユーザーがデバイスIDではなくアプリインストールIDを使用することを強く示唆するようにコメントを更新しました。ただし、このソリューションは、インストールIDではなくデバイスを必要とするユーザーにとって依然として価値があると思います。
emmby

8
ANDROID_IDは出荷時設定に変更される可能性があるため、デバイスを識別することもできません
Samuel

180

Reto Meier がユーザーの一意のIDを取得するために今年のGoogle I / Oプレゼンテーションで使用したコードは次のとおりです。

private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";

public synchronized static String id(Context context) {
    if (uniqueID == null) {
        SharedPreferences sharedPrefs = context.getSharedPreferences(
                PREF_UNIQUE_ID, Context.MODE_PRIVATE);
        uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
        if (uniqueID == null) {
            uniqueID = UUID.randomUUID().toString();
            Editor editor = sharedPrefs.edit();
            editor.putString(PREF_UNIQUE_ID, uniqueID);
            editor.commit();
        }
    }
    return uniqueID;
}

これをバックアップ戦略と組み合わせてプリファレンスをクラウドに送信する場合(Retoのトークでも説明されています)、ユーザーに関連付けられ、デバイスがワイプされた後、または交換された後も固執するIDが必要です。これを使用する予定です。今後の分析では(つまり、まだその段階では行っていません:)。


@Lenn Dollingのメソッドを使用して、一意のIDに現在時刻を追加しました。しかし、これはよりシンプルで信頼できる方法のようです。おかげレトマイヤーとアントニー・ノーラン
グーカン・バリスアーカー

それは素晴らしいですが、根ざしたデバイスはどうですか?彼らはこれにアクセスし、uidを別のものに簡単に変更できます。
tasomaniac

3
アンインストールおよび再インストール後に一意のIDを保持する必要がない場合に最適なオプション(例:勝つチャンスが3回あるプロモーションイベント/ゲーム、期間)。
カイルクレッグ

2
Meierプレゼンテーションは、Androidバックアップマネージャの使用に依存しています。これは、ユーザーがその機能をオンにすることを選択するかどうかに依存します。ユーザーがそのオプションを選択しなかった場合、ユーザーはそれらをバックアップしないため、アプリのユーザー設定(Meierの使用)にとってそれは問題ありません。ただし、元の質問はデバイスの一意のIDの生成に関するものであり、このIDはアプリごとに生成され、インストールごとに生成されるのではなく、デバイスごとに生成されます。また、ユーザーがバックアップオプションを選択することに依存しているため、ユーザー以外の用途好み(例えば、期間限定トライアル)は限られています。
カール

12
これは、データのアンインストールまたはクリアでは機能しません。
ジョンシェリー2014

106

また、Wi-FiアダプターのMACアドレスを検討することもできます。このようにして取得:

WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();

android.permission.ACCESS_WIFI_STATEマニフェストの許可が必要です。

Wi-Fiが接続されていなくても使用できると報告されています。上記の答えからジョーが彼の多くのデバイスでこれを試してみると、それは素晴らしいでしょう

一部のデバイスでは、Wi-Fiがオフの場合は使用できません。

注: Android 6.x以降、一貫した偽のMACアドレスが返されます。02:00:00:00:00:00


8
これは必須android.permission.ACCESS_WIFI_STATE
ohhorob、

5
ほぼすべてのAndroidデバイスで、WiFiがオフの場合は使用できないことがわかります。WiFiをオフにすると、デバイスはカーネルレベルで削除されます。
クリスダニー

12
@Sanandrea-それに直面しましょう、根ざしたデバイスでは、すべてがなりすまされる可能性があります。
ocodo 2013

5
:アクセスWiFiのMACアドレスは、Android Mにブロックされているstackoverflow.com/questions/31329733/...
ブリーズ

6
Android 6.x以降、一貫した偽の02:00:00:00:00:00
MAC

87

ここにはかなり役立つ情報があります

5つの異なるIDタイプをカバーしています。

  1. IMEI(電話を使用するAndroidデバイスのみ。ニーズありandroid.permission.READ_PHONE_STATE
  2. 疑似固有ID(すべてのAndroidデバイス用)
  3. Android ID(nullの可能性、出荷時設定へのリセット時に変更可能、根ざした電話で変更可能)
  4. WLAN MACアドレス文字列(が必要android.permission.ACCESS_WIFI_STATE
  5. BT MACアドレス文字列(Bluetooth対応デバイス、ニーズandroid.permission.BLUETOOTH

2
省略された重要なポイント(ここと記事で):WLANまたはBT MACがオンになっていないと、それらを取得できません!それ以外の場合は、WLAN MACが最適な識別子になると思います。ユーザーがWi-Fiをオンにする保証はありません。自分でオンにすることは「適切」ではないと思います。
トム

1
@トムあなたは間違っている。WLANまたはBT MACがオフになっている場合でも、それらを読み取ることができます。ただし、デバイスが利用可能なWLANまたはBTモジュールを備えている保証はありません。
Marqs 2014

2
特に、ローカルWiFiとBluetooth MACアドレスは使用できなくなりました。aWifiInfoオブジェクトのgetMacAddress()メソッドとBluetoothAdapter.getDefaultAdapter()。getAddress()メソッドはどちらも今後02:00:00:00:00:00を返します
sarika kate 2016

4
@sarikakate 6.0マシュマロ以上でのみ当てはまります... 6.0マシュマロ以下でも期待どおりに動作します。
Smeet

@Smeetええ、あなたは正しいです。私はそれが6.0以下で機能することを言及するのを忘れていました
sarika kate

51

Android Developersの公式ブログに、このテーマについての記事「Identifying App Installations」の完全な記事が掲載されました。


4
そして、その議論の要点は、ハードウェアから一意のIDを取得しようとすると、おそらく間違いを犯しているということです。
Tim Bray、

3
デバイスのロックを工場出荷時の状態にリセットすることを許可している場合、トライアルウェアモデルは完全に機能します。
セヴァアレクセイエフ

43

で、GoogleのI / Oレトマイヤーは、インストール全体でユーザーを追跡するために、ほとんどの開発者のニーズを満たすべきか、これをアプローチする方法に強固な答えをリリースしました。アンソニー・ノーランは彼の答えに方向性を示していますが、私は他の人が簡単にそれを行う方法を見ることができるように完全なアプローチを書きたいと思いました(詳細を理解するのにしばらく時間がかかりました)。

このアプローチにより、匿名の安全なユーザーIDが提供されます。このユーザーIDは、(プライマリGoogleアカウントに基づいて)異なるデバイス間およびインストール間でユーザーに対して永続的です。基本的なアプローチは、ランダムなユーザーIDを生成し、これをアプリの共有設定に保存することです。次に、Googleのバックアップエージェントを使用して、Googleアカウントにリンクされた共有設定をクラウドに保存します。

完全なアプローチを見てみましょう。まず、Androidバックアップサービスを使用してSharedPreferencesのバックアップを作成する必要があります。まず、からアプリを登録しますhttp://developer.android.com/google/backup/signup.html

Googleから、マニフェストに追加する必要があるバックアップサービスキーが提供されます。次のように、アプリケーションにBackupAgentを使用するように指示する必要もあります。

<application android:label="MyApplication"
         android:backupAgent="MyBackupAgent">
    ...
    <meta-data android:name="com.google.android.backup.api_key"
        android:value="your_backup_service_key" />
</application>

次に、バックアップエージェントを作成し、sharedpreferencesにヘルパーエージェントを使用するように指示する必要があります。

public class MyBackupAgent extends BackupAgentHelper {
    // The name of the SharedPreferences file
    static final String PREFS = "user_preferences";

    // A key to uniquely identify the set of backup data
    static final String PREFS_BACKUP_KEY = "prefs";

    // Allocate a helper and add it to the backup agent
    @Override
    public void onCreate() {
        SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this,          PREFS);
        addHelper(PREFS_BACKUP_KEY, helper);
    }
}

バックアップを完了するには、メインアクティビティでBackupManagerのインスタンスを作成する必要があります。

BackupManager backupManager = new BackupManager(context);

最後に、ユーザーIDがまだ存在しない場合は作成し、SharedPreferencesに保存します。

  public static String getUserID(Context context) {
            private static String uniqueID = null;
        private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
    if (uniqueID == null) {
        SharedPreferences sharedPrefs = context.getSharedPreferences(
                MyBackupAgent.PREFS, Context.MODE_PRIVATE);
        uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
        if (uniqueID == null) {
            uniqueID = UUID.randomUUID().toString();
            Editor editor = sharedPrefs.edit();
            editor.putString(PREF_UNIQUE_ID, uniqueID);
            editor.commit();

            //backup the changes
            BackupManager mBackupManager = new BackupManager(context);
            mBackupManager.dataChanged();
        }
    }

    return uniqueID;
}

このUser_IDは、ユーザーがデバイスを移動した場合でも、インストール全体で保持されます。

このアプローチの詳細については、Retoの講演を参照してください。

また、バックアップエージェントの実装方法の詳細については、データバックアップを参照してください。バックアップは瞬時に実行されないため、テストを実行するにはバックアップを強制する必要があるため、テストの下部にあるセクションを特にお勧めします。


5
ユーザーが複数のデバイスを持っている場合、これは同じIDを持つ複数のデバイスにつながるのではないですか?たとえばタブレットと電話。
土佐

これには最小ターゲット8が必要です。
ハルキシネート2013

これは、アプリ内購入を行うときに検証ペイロードを作成するための好ましい方法でしょうか?アプリ内課金のサンプルコードのコメントより:「優れたデベロッパーペイロードには次の特徴があります。1. 2人の異なるユーザーがアイテムを購入すると、ペイロードが異なるため、あるユーザーの購入を別のユーザーに再生することはできません。 2.ペイロードは、アプリが購入フローを開始したものではない場合でも確認できるようなものでなければなりません(そのため、あるデバイスでユーザーが購入したアイテムは、ユーザーが所有する他のデバイスで動作します)。
TouchBoarder 2013

@土佐ちょうど同じ質問でした。しかし、同じ方法を使用して仮想デバイスIDを作成し、同じ方法でバックアップしないのではないでしょうか。デバイスIDはワイプまたは再インストール後は保持されませんが、永続的なユーザーIDがある場合は、デバイスIDからはそれほど必要ない可能性があります。
jwehrle

39

これは一意のIDのスケルトンを構築するための確実な方法だと思います...確認してください。

すべてのAndroidデバイスで機能する疑似一意のID一部のデバイスには電話がありません(タブレットなど)。または、何らかの理由で、READ_PHONE_STATE権限を含めたくない場合があります。ROMバージョン、製造元名、CPUタイプ、その他のハードウェアの詳細などの詳細を読み取ることができます。シリアルキーチェックやその他の一般的な目的でIDを使用する場合に適しています。この方法で計算されたIDは一意ではありません。同じID(同じハードウェアとROMイメージに基づく)を持つ2つのデバイスを見つけることは可能ですが、実際のアプリケーションでの変更は無視できます。この目的のために、Buildクラスを使用できます。

String m_szDevIDShort = "35" + //we make this look like a valid IMEI
            Build.BOARD.length()%10+ Build.BRAND.length()%10 +
            Build.CPU_ABI.length()%10 + Build.DEVICE.length()%10 +
            Build.DISPLAY.length()%10 + Build.HOST.length()%10 +
            Build.ID.length()%10 + Build.MANUFACTURER.length()%10 +
            Build.MODEL.length()%10 + Build.PRODUCT.length()%10 +
            Build.TAGS.length()%10 + Build.TYPE.length()%10 +
            Build.USER.length()%10 ; //13 digits

Buildメンバーのほとんどは文字列です。ここでは、その長さを受け取り、モジュロを介して数字に変換しています。13桁の数字があり、IMEIと同じサイズのID(15桁)になるように前に2つ(35)を追加します。ここには他にも可能性があります。これらの文字列を見てください。のようなものを返します355715565309247。特別な許可は必要ないため、このアプローチは非常に便利です。


(補足:上記のテクニックはPocket Magicの記事からコピーされました。)


7
興味深いソリューション。これは、独自の「ハッシュ」関数を考え出そうとするのではなく、連結されたすべてのデータを実際にハッシュする必要がある状況のようです。値ごとに異なる実質的なデータがある場合でも、衝突が発生する場合が多くあります。私の推奨事項:ハッシュ関数を使用してから、バイナリの結果を10進数に変換し、必要に応じて切り捨てます。正しく実行するには、実際にはUUIDまたは完全なハッシュ文字列を使用する必要があります。
Steve Pomeroy

21
ソースにクレジットを付けてください...これは次の記事から直接削除されました:pocketmagic.net/
Steve Haley

8
このIDは、何がわからないかのように衝突する可能性があります。同じキャリアの同じデバイスでも同じであることが実際に保証されています。
セヴァアレクセイエフ

7
これは、デバイスがアップグレードされた場合にも変わる可能性があります。
Davidが

8
非常に悪い解決策です 2つのNexus 5でテスト済み...同じ数値を返します。
SinanDizdarević2015年

38

次のコードは、非表示のAndroid APIを使用してデバイスのシリアル番号を返します。ただし、このデバイスでは「ro.serialno」が設定されていないため、このコードはSamsung Galaxy Tabでは機能しません。

String serial = null;

try {
    Class<?> c = Class.forName("android.os.SystemProperties");
    Method get = c.getMethod("get", String.class);
    serial = (String) get.invoke(c, "ro.serialno");
}
catch (Exception ignored) {

}

ro.serialno生成するために使用されていることをxda開発者に読んだところSettings.Secure.ANDROID_IDです。したがって、それらは基本的に同じ値の異なる表現です。
マーティン

@Martin:しかし、おそらくシリアル番号はデバイスをリセットしても変わりません。だよね?ANDROID_IDから新しい値が導出されます。
ロニー

実際、すべてのデバイスで同じものをテストしました。または、少なくとも同じハッシュ値(プライバシー上の理由から、ログファイルに真の値を書き込みません)。
マーティン

この値はandroid.os.Build.SERIAL
eugeneek

android.os.Build.SERIALAndroid Oでは廃止予定です。android
developers.googleblog.com/

32

簡単な質問であり、簡単な答えはありません。

さらに、ここにある既存の答えはすべて、古くなっているか信頼できないかです。

だから、あなたは2020年に解決策を探している場合

覚えておくべきことがいくつかあります。

すべてのハードウェアベースの識別子(SSAID、IMEI、MACなど)は、世界中のアクティブなデバイスの50%を超えるGoogle以外のデバイス(ピクセルとNexusを除くすべて)では信頼できません。したがって、公式のAndroid識別子のベストプラクティスには、次のように明記されています。

SSAID(Android ID)、IMEI、MACアドレスなどのハードウェア識別子の使用は避けてください ...

それは上記の答えのほとんどを無効にします。また、Androidのセキュリティアップデートが異なるため、一部のアップデートにはより厳密なランタイム権限が必要ですが、これはユーザーが簡単に拒否できます。

上記のCVE-2018-9489すべてのWIFIベースのテクニックに影響を与える例として。

そのため、これらの識別子は信頼できないだけでなく、多くの場合アクセスできなくなります。

簡単に言うと、これらのテクニックは使用しないでください

他の多くの回答はAdvertisingIdClient、を使用することを提案しています。これは、互換性もありません。仕様上、広告プロファイリングにのみ使用する必要があるためです。公式リファレンスにも記載されています

ユーザーIDのプロファイリングまたは広告のユースケースにのみ広告IDを使用する

これはデバイスの識別に信頼できないだけでなく、ユーザーがいつでもリセットまたはブロックできることを明記している広告追跡ポリシーに関するユーザーのプライバシーに従う必要があります

したがって、どちらも使用しないでください

希望する静的でグローバルに一意で信頼性のあるデバイス識別子を持つことができないため。Androidの公式リファレンスは次のことを提案しています。

支払い詐欺防止とテレフォニーを除いて、他のすべてのユースケースで可能な限り、FirebaseInstanceIdまたはプライベートに格納されたGUIDを使用してください

これは、デバイスへのアプリケーションのインストールに固有なので、ユーザーがアプリをアンインストールすると、完全に消去されるため、100%の信頼性はありませんが、次善の策です。

使用FirebaseInstanceIdするには、最新のfirebase-messaging依存関係gradleに追加します

implementation 'com.google.firebase:firebase-messaging:20.2.0'

そして、バックグラウンドスレッドで以下のコードを使用します。

String reliableIdentifier = FirebaseInstanceId.getInstance().getId();

リモートサーバーにデバイスIDを保存する必要がある場合は、そのまま(プレーンテキスト)ではなく、saltを使用しハッシュで保存します。

今日では、これはベストプラクティスであるだけでなく、実際にはGDPR(識別子および類似の規制)に従って法律で行う必要があります。


3
今のところ、これが最良の答えで、最初の文は最高の要約です:「それは簡単な質問には、だ単純な答えなし-ちょうどそれを愛する。
b2mob

「支払い詐欺防止とテレフォニーを除いて」のコメントに感謝し、そのユースケースへの対処方法について回答を提供する必要はありません。
Eran Boudjnah

@EranBoudjnah回答にリンクされている公式リファレンスからの引用。私はそのユースケースに対処することを試みることができますが、それはOPの質問に固有ではないので、stackoverflowのポリシーによると-別の専用の質問で行う必要があります。
Nikita Kurtin

その答えは不完全だと言っているだけです。しかし、私はそれが引用であるのであなたのせいではないことを知っています。
Eran Boudjnah

1
@ M.UsmanKhan、答えはその直後に書かれています:「今日、これはベストプラクティスだけではなく、実際にはGDPR-識別子および類似の規則に従って法律で行う必要があります。
Nikita Kurtin

27

以下のコードを使用して、Android OSデバイスの一意のデバイスIDを文字列として取得できます。

deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 

21

シリアルフィールドがに追加されたBuildAPIレベル9( -ジンジャーブレッドアンドロイド2.3)でクラス。ドキュメントには、ハードウェアのシリアル番号を表すと記載されています。したがって、デバイス上に存在する場合、一意である必要があります。

ただし、APIレベルが9以上のすべてのデバイスで実際にサポートされている(= nullではない)かどうかはわかりません。


2
残念ながら、それは「不明」です。
m0skit0 2013

18

追加することの1つ-私には、これらのユニークな状況の1つがあります。

使用:

deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);

Viewsonic G TabletがNullではないDeviceIDを報告しても、すべてのG Tabletが同じ番号を報告することがわかりました。

「ユニークな」DeviceIDに基づいて誰かのアカウントに即座にアクセスできる「Pocket Empires」をプレイするのを面白くします。

私のデバイスには携帯ラジオがありません。


IDとは何ですか?それはperchance 9774d56d682e549cですか?
Mr_and_Mrs_D 2013

あのタブレットを投げてからずっと前のことです。言えなかった。
Tony Maro

動作します。また、ランダムなUUIDから取得したIDよりもはるかにコンパクトです。
Treewallie

1
@Treewallie機能しますか?異なるアプリから同じデバイスIDを取得できますか?
アーノルドブラウン

@ArnoldBrownはい。十分にテストしてください。良い一日をお過ごしください:D
Treewallie

16

アプリケーションのインストール元であるAndroidデバイスごとに一意の識別子を取得する方法の詳細については、Android開発者の公式ブログの投稿「アプリのインストールの識別」を参照してください。

インストール時に自分で生成し、アプリケーションが再起動されたときにそれを読み取るのが最善の方法のようです。

私は個人的にこれは受け入れられるが理想的ではないと思います。ほとんどの場合、Androidによって提供される識別子はどれも機能しません。ほとんどがスマートフォンの無線状態(Wi-Fiオン/オフ、セルラーオン/オフ、Bluetoothオン/オフ)に依存しているためです。その他はSettings.Secure.ANDROID_ID、製造元が実装する必要があり、一意であるとは限りません。

以下は、アプリケーションがローカルに保存する他のデータと一緒に保存されるインストールファイルにデータを書き込む例です。

public class Installation {
    private static String sID = null;
    private static final String INSTALLATION = "INSTALLATION";

    public synchronized static String id(Context context) {
        if (sID == null) {
            File installation = new File(context.getFilesDir(), INSTALLATION);
            try {
                if (!installation.exists())
                    writeInstallationFile(installation);
                sID = readInstallationFile(installation);
            } 
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return sID;
    }

    private static String readInstallationFile(File installation) throws IOException {
        RandomAccessFile f = new RandomAccessFile(installation, "r");
        byte[] bytes = new byte[(int) f.length()];
        f.readFully(bytes);
        f.close();
        return new String(bytes);
    }

    private static void writeInstallationFile(File installation) throws IOException {
        FileOutputStream out = new FileOutputStream(installation);
        String id = UUID.randomUUID().toString();
        out.write(id.getBytes());
        out.close();
    }
}

アプリのインストールを追跡する場合は、これが最適です。ただし、デバイスの追跡はかなりトリッキーであり、完全に気密なソリューションはないようです。
Luca Spiller、2011年

根ざしたデバイスはどうですか?彼らはこのインストールIDを簡単に変更できますよね?
tasomaniac

もちろんです。ルートはインストールIDを変更できます。あなたは、このコードブロックを使用して、ルートを確認することができます。stackoverflow.com/questions/1101380/...
ケビン・パーカー

工場をリセットすると、ファイルは削除されますか?
Jamshid 2015年

/ dataパーティションを出荷時設定にリセットして削除またはフォーマットした場合、UUIDは異なります。
Kevin Parker

12

クラスファイルに以下のコードを追加します。

final TelephonyManager tm = (TelephonyManager) getBaseContext()
            .getSystemService(SplashActivity.TELEPHONY_SERVICE);
    final String tmDevice, tmSerial, androidId;
    tmDevice = "" + tm.getDeviceId();
    Log.v("DeviceIMEI", "" + tmDevice);
    tmSerial = "" + tm.getSimSerialNumber();
    Log.v("GSM devices Serial Number[simcard] ", "" + tmSerial);
    androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(),
            android.provider.Settings.Secure.ANDROID_ID);
    Log.v("androidId CDMA devices", "" + androidId);
    UUID deviceUuid = new UUID(androidId.hashCode(),
            ((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
    String deviceId = deviceUuid.toString();
    Log.v("deviceIdUUID universally unique identifier", "" + deviceId);
    String deviceModelName = android.os.Build.MODEL;
    Log.v("Model Name", "" + deviceModelName);
    String deviceUSER = android.os.Build.USER;
    Log.v("Name USER", "" + deviceUSER);
    String devicePRODUCT = android.os.Build.PRODUCT;
    Log.v("PRODUCT", "" + devicePRODUCT);
    String deviceHARDWARE = android.os.Build.HARDWARE;
    Log.v("HARDWARE", "" + deviceHARDWARE);
    String deviceBRAND = android.os.Build.BRAND;
    Log.v("BRAND", "" + deviceBRAND);
    String myVersion = android.os.Build.VERSION.RELEASE;
    Log.v("VERSION.RELEASE", "" + myVersion);
    int sdkVersion = android.os.Build.VERSION.SDK_INT;
    Log.v("VERSION.SDK_INT", "" + sdkVersion);

AndroidManifest.xmlに追加:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

10

TelephonyManagerおよびを使用してANDROID_ID、文字列としてのAndroid OSデバイスの一意のデバイスIDは、

String deviceId;
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (mTelephony.getDeviceId() != null) {
    deviceId = mTelephony.getDeviceId();
}
else {
    deviceId = Secure.getString(
                   getApplicationContext().getContentResolver(),
                   Secure.ANDROID_ID);
}

ただし、Googleが提案する方法を強くお勧めします。アプリインストールの識別をご覧ください。


9

これらのANDROID_ID問題を回避するには、多くの異なるアプローチがあります(場合によってnullは、特定のモデルのデバイスが常に同じIDを返すこともあります)。

  • カスタムID生成アルゴリズムの実装(静的であり、変更されないことになっているデバイスプロパティに基づく->知っている人)
  • IMEI、シリアル番号、Wi-Fi / Bluetooth-MACアドレスなどの他のIDの悪用(すべてのデバイスに存在するわけではないか、追加の権限が必要になります)

私自身は、Android(https://github.com/vieux/OpenUDIDを参照)の既存のOpenUDID実装(https://github.com/ylechelle/OpenUDIDを参照)を使用することを好みます。統合は簡単で、ANDROID_ID上記の問題のフォールバックを使用できます。


8

IMEIはどうですか。これは、Androidや他のモバイルデバイスに固有のものです。


9
モバイルキャリアに接続しないため、IMEIがないタブレットには対応していません。
Brill Pappin、2012年

2
IMEIの代わりにESNを持つCDMAデバイスは言うまでもありません。
Davidが

@David Givenは、AndroidのCDMAはありますか?
Elzo Valugi 2012年

1
それはそれ電話であるということだけをします:)タブレットはそうではないかもしれません。
Brill Pappin

3
@ElzoValugiもう「今」ですが、すべてのタブレットにSIMカードが搭載されているわけではありません。
Matthew Quiros 2012

8

ここに私がユニークなIDを生成する方法があります:

public static String getDeviceId(Context ctx)
{
    TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);

    String tmDevice = tm.getDeviceId();
    String androidId = Secure.getString(ctx.getContentResolver(), Secure.ANDROID_ID);
    String serial = null;
    if(Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO) serial = Build.SERIAL;

    if(tmDevice != null) return "01" + tmDevice;
    if(androidId != null) return "02" + androidId;
    if(serial != null) return "03" + serial;
    // other alternatives (i.e. Wi-Fi MAC, Bluetooth MAC, etc.)

    return null;
}

6.0バージョンでReadPhoneStateを使用してランタイムの許可を求めている場合
Harsha

8

私の2セント-これはデバイス(err)の一意のID であり、Android開発者のブログで説明されているインストール用ではありません。

SharedPreferencesはプロセス間で同期されないため、@ emmbyによって提供されるソリューションはアプリケーションIDごとにフォールバックすることに注意してください(ここここを参照)。だから私はこれを完全に避けました。

代わりに、(デバイス)IDを取得するためのさまざまな戦略を列挙型にカプセル化しました。列挙定数の順序を変更すると、IDを取得するさまざまな方法の優先順位に影響します。最初のnull以外のIDが返されるか、例外がスローされます(nullに意味を与えないというJavaの慣習に従って)。たとえば、最初にTELEPHONYを最初に持っていますが、デフォルトの適切な選択はANDROID_ID ベータ版です。

import android.Manifest.permission;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.wifi.WifiManager;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;

// TODO : hash
public final class DeviceIdentifier {

    private DeviceIdentifier() {}

    /** @see http://code.google.com/p/android/issues/detail?id=10603 */
    private static final String ANDROID_ID_BUG_MSG = "The device suffers from "
        + "the Android ID bug - its ID is the emulator ID : "
        + IDs.BUGGY_ANDROID_ID;
    private static volatile String uuid; // volatile needed - see EJ item 71
    // need lazy initialization to get a context

    /**
     * Returns a unique identifier for this device. The first (in the order the
     * enums constants as defined in the IDs enum) non null identifier is
     * returned or a DeviceIDException is thrown. A DeviceIDException is also
     * thrown if ignoreBuggyAndroidID is false and the device has the Android ID
     * bug
     *
     * @param ctx
     *            an Android constant (to retrieve system services)
     * @param ignoreBuggyAndroidID
     *            if false, on a device with the android ID bug, the buggy
     *            android ID is not returned instead a DeviceIDException is
     *            thrown
     * @return a *device* ID - null is never returned, instead a
     *         DeviceIDException is thrown
     * @throws DeviceIDException
     *             if none of the enum methods manages to return a device ID
     */
    public static String getDeviceIdentifier(Context ctx,
            boolean ignoreBuggyAndroidID) throws DeviceIDException {
        String result = uuid;
        if (result == null) {
            synchronized (DeviceIdentifier.class) {
                result = uuid;
                if (result == null) {
                    for (IDs id : IDs.values()) {
                        try {
                            result = uuid = id.getId(ctx);
                        } catch (DeviceIDNotUniqueException e) {
                            if (!ignoreBuggyAndroidID)
                                throw new DeviceIDException(e);
                        }
                        if (result != null) return result;
                    }
                    throw new DeviceIDException();
                }
            }
        }
        return result;
    }

    private static enum IDs {
        TELEPHONY_ID {

            @Override
            String getId(Context ctx) {
                // TODO : add a SIM based mechanism ? tm.getSimSerialNumber();
                final TelephonyManager tm = (TelephonyManager) ctx
                        .getSystemService(Context.TELEPHONY_SERVICE);
                if (tm == null) {
                    w("Telephony Manager not available");
                    return null;
                }
                assertPermission(ctx, permission.READ_PHONE_STATE);
                return tm.getDeviceId();
            }
        },
        ANDROID_ID {

            @Override
            String getId(Context ctx) throws DeviceIDException {
                // no permission needed !
                final String andoidId = Secure.getString(
                    ctx.getContentResolver(),
                    android.provider.Settings.Secure.ANDROID_ID);
                if (BUGGY_ANDROID_ID.equals(andoidId)) {
                    e(ANDROID_ID_BUG_MSG);
                    throw new DeviceIDNotUniqueException();
                }
                return andoidId;
            }
        },
        WIFI_MAC {

            @Override
            String getId(Context ctx) {
                WifiManager wm = (WifiManager) ctx
                        .getSystemService(Context.WIFI_SERVICE);
                if (wm == null) {
                    w("Wifi Manager not available");
                    return null;
                }
                assertPermission(ctx, permission.ACCESS_WIFI_STATE); // I guess
                // getMacAddress() has no java doc !!!
                return wm.getConnectionInfo().getMacAddress();
            }
        },
        BLUETOOTH_MAC {

            @Override
            String getId(Context ctx) {
                BluetoothAdapter ba = BluetoothAdapter.getDefaultAdapter();
                if (ba == null) {
                    w("Bluetooth Adapter not available");
                    return null;
                }
                assertPermission(ctx, permission.BLUETOOTH);
                return ba.getAddress();
            }
        }
        // TODO PSEUDO_ID
        // http://www.pocketmagic.net/2011/02/android-unique-device-id/
        ;

        static final String BUGGY_ANDROID_ID = "9774d56d682e549c";
        private final static String TAG = IDs.class.getSimpleName();

        abstract String getId(Context ctx) throws DeviceIDException;

        private static void w(String msg) {
            Log.w(TAG, msg);
        }

        private static void e(String msg) {
            Log.e(TAG, msg);
        }
    }

    private static void assertPermission(Context ctx, String perm) {
        final int checkPermission = ctx.getPackageManager().checkPermission(
            perm, ctx.getPackageName());
        if (checkPermission != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Permission " + perm + " is required");
        }
    }

    // =========================================================================
    // Exceptions
    // =========================================================================
    public static class DeviceIDException extends Exception {

        private static final long serialVersionUID = -8083699995384519417L;
        private static final String NO_ANDROID_ID = "Could not retrieve a "
            + "device ID";

        public DeviceIDException(Throwable throwable) {
            super(NO_ANDROID_ID, throwable);
        }

        public DeviceIDException(String detailMessage) {
            super(detailMessage);
        }

        public DeviceIDException() {
            super(NO_ANDROID_ID);
        }
    }

    public static final class DeviceIDNotUniqueException extends
            DeviceIDException {

        private static final long serialVersionUID = -8940090896069484955L;

        public DeviceIDNotUniqueException() {
            super(ANDROID_ID_BUG_MSG);
        }
    }
}

8

ここには30以上の回答があり、いくつかは同じで、いくつかはユニークです。この回答は、それらの回答のいくつかに基づいています。それらの1つは、@ Lenn Dollingの回答です。

3つのIDを組み合わせて、32桁の16進数文字列を作成します。それは私にとって非常にうまくいきました。

3つのIDは次のとおりです。
疑似
ID-物理デバイスの仕様に基づいて生成されますANDROID_ID - Settings.Secure.ANDROID_ID
Bluetoothアドレス-Bluetoothアダプターアドレス

次のようなものが返されます: 551F27C060712A72730B0A0F734064B1

注:longId文字列にはいつでもIDを追加できます。たとえば、シリアル番号。wifiアダプターのアドレス。IMEI。このようにして、デバイスごとに固有のものにします。

@SuppressWarnings("deprecation")
@SuppressLint("HardwareIds")
public static String generateDeviceIdentifier(Context context) {

        String pseudoId = "35" +
                Build.BOARD.length() % 10 +
                Build.BRAND.length() % 10 +
                Build.CPU_ABI.length() % 10 +
                Build.DEVICE.length() % 10 +
                Build.DISPLAY.length() % 10 +
                Build.HOST.length() % 10 +
                Build.ID.length() % 10 +
                Build.MANUFACTURER.length() % 10 +
                Build.MODEL.length() % 10 +
                Build.PRODUCT.length() % 10 +
                Build.TAGS.length() % 10 +
                Build.TYPE.length() % 10 +
                Build.USER.length() % 10;

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

        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        String btId = "";

        if (bluetoothAdapter != null) {
            btId = bluetoothAdapter.getAddress();
        }

        String longId = pseudoId + androidId + btId;

        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(longId.getBytes(), 0, longId.length());

            // get md5 bytes
            byte md5Bytes[] = messageDigest.digest();

            // creating a hex string
            String identifier = "";

            for (byte md5Byte : md5Bytes) {
                int b = (0xFF & md5Byte);

                // if it is a single digit, make sure it have 0 in front (proper padding)
                if (b <= 0xF) {
                    identifier += "0";
                }

                // add number to string
                identifier += Integer.toHexString(b);
            }

            // hex string to uppercase
            identifier = identifier.toUpperCase();
            return identifier;
        } catch (Exception e) {
            Log.e("TAG", e.toString());
        }
        return "";
}

1
追加UUIDをするlongIdと、ファイルに保存し、それ最もユニークな識別子になります:String uuid = UUID.randomUUID().toString();
Mousa Alfhaily

1
他のすべてが失敗した場合、ユーザーがAPI 9より低い(Gingerbreadより低い)場合は、電話または「Secure.ANDROID_ID」をリセットしました。が「null」を返す場合、返されるIDは、Androidデバイス情報のみに基づいています。これは、衝突が発生する可能性がある場所です。DISPLAY、HOST、IDは使用しないでください。これらの項目は変更される可能性があります。衝突がある場合、データが重複します。ソース:gist.github.com/pedja1/fe69e8a80ed505500caa
Mousa Alfhaily

このコード行で一意の番号を取得しようとすると、それは一意のIDであり、他のデバイスと競合することはありませんか?
忍者

1
@Ninja BLE MACアドレスは一意であるため、生成されるIDは常に一意になります。ただし、本当に確認したい場合は、UUIDをに追加することをお勧めしlongIdます。この1行を次のように変更します。String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();これにより、生成されたIDが一意になることが保証されます。
ᴛʜᴇᴘᴀᴛᴇʟ

@ᴛʜᴇᴘᴀᴛᴇʟこの大きな助けに感謝します。実際、私のアプリは非常に機密性の高いデータなので、確認する必要があるので、このことを確認しています。
忍者

7

もう1つの方法は/sys/class/android_usb/android0/iSerial、権限なしにアプリで使用することです。

user@creep:~$ adb shell ls -l /sys/class/android_usb/android0/iSerial
-rw-r--r-- root     root         4096 2013-01-10 21:08 iSerial
user@creep:~$ adb shell cat /sys/class/android_usb/android0/iSerial
0A3CXXXXXXXXXX5

Javaでこれを行うには、FileInputStreamを使用してiSerialファイルを開き、文字を読み取ります。すべてのデバイスがこのファイルを持っているわけではないので、例外ハンドラで必ずラップしてください。

少なくとも次のデバイスでは、このファイルが誰でも読み取り可能であることがわかっています。

  • Galaxy Nexus
  • Nexus S
  • Motorola Xoom 3G
  • 東芝AT300
  • HTC One V
  • ミニMK802
  • Samsung Galaxy S II

また、私のブログの記事「Androidハードウェアのシリアル番号が権限のないアプリ漏れている」を参照して、他に入手可能なファイルについて説明してください。


私はあなたのブログ投稿を読んだだけです。これは一意ではないと思います。Build.SERIALは、アクセス許可なしでも使用でき、(理論的には)一意のハードウェアシリアル番号です。
トム

1
あなたが正しい。これは、デバイスを追跡できるもう1つの方法であり、あなたが言ったように、これらの方法はどちらもアプリの権限を必要としません。
insitusec 2014年

7

TelephonyManger.getDeviceId()一意のデバイスID(GSMの場合はIMEI、CDMA電話の場合はMEIDまたはESNなど)を返します。

final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);            
String myAndroidDeviceId = mTelephony.getDeviceId(); 

しかし、私は使用することをお勧めします:

Android IDを一意の64ビット16進文字列として返すSettings.Secure.ANDROID_ID

    String   myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 

ときどきTelephonyManger.getDeviceId()ので、あなたは、このメソッドを使用する一意のIDを保証するために、ヌルを返します。

public String getUniqueID(){    
    String myAndroidDeviceId = "";
    TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    if (mTelephony.getDeviceId() != null){
        myAndroidDeviceId = mTelephony.getDeviceId(); 
    }else{
         myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
    }
    return myAndroidDeviceId;
}

私は最近、タイプSM-G928F / Galaxy S6 edge +のクライアントのデバイスが、Android IDに対して16桁ではなく15桁の16進数を提供することを発見しました。
Holger Jakobs 2016年

7

特定のAndroidデバイスのハードウェア認識については、MACアドレスを確認できます。

あなたはそれをそのようにすることができます:

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

今あなたのコードで:

List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());

for (NetworkInterface interface : interfacesList) {
   // This will give you the interface MAC ADDRESS
   interface.getHardwareAddress();
}

すべてのAndroidデバイスで、それらは少なくとも「wlan0」です。インターフェースの魔女はWI-FIチップです。このコードは、WI-FIがオンになっていない場合でも機能します。

PSそれらはMACSを含むリストから取得する他のインターフェースの集まりですが、これは電話によって異なる場合があります。


7

IMEISecure を取得または使用するには、次のコードを使用します。ANDROID_ID代わりに、デバイスに電話機能がない場合:

String identifier = null;
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
if (tm != null)
      identifier = tm.getDeviceId();
if (identifier == null || identifier .length() == 0)
      identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);

7

すなわち、 Settings.Secure.ANDROID_ID。これは、デバイスが最初に起動したときに生成および保存される64ビットの量です。デバイスがワイプされるとリセットされます。

ANDROID_ID一意のデバイス識別子に適しています。欠点もあります。まず、2.2より前のAndroidのリリースでは100%の信頼性がありません。(“Froyo”).また、大手メーカーの人気のある携帯電話には、すべてのインスタンスが同じANDROID_IDであるという広く観察されているバグが1つ以上あります。


1
この回答は、以前のGoogleブログandroid-developers.googleblog.com/2011/03/…からのコピーペーストです。だからバグはすでに解決されていますか?
セルギ2017年


6

GoogleインスタンスID

I / O 2015にリリース。Androidでは、再生サービス7.5が必要です。

https://developers.google.com/instance-id/
https://developers.google.com/instance-id/guides/android-implementation

InstanceID iid = InstanceID.getInstance( context );   // Google docs are wrong - this requires context
String id = iid.getId();  // blocking call

Googleは、このIDがAndroid、Chrome、iOS全体のインストールを識別するために使用されることを意図しているようです。

これは、デバイスではなくインストールを識別しますが、ANDROID_ID(これは受け入れられた回答です)もデバイスを識別しなくなりました。ARCランタイムでは、この新しいインスタンスIDと同様に、インストールごとに新しいANDROID_IDが生成されます(詳細はこちら)。また、(デバイスではなく)設備を特定することは、私たちの多くが実際に求めていることだと思います。

インスタンスIDの利点

Googleはこの目的(インストールの識別)に使用することを意図しており、クロスプラットフォームであり、他の多くの目的に使用できるようです(上記のリンクを参照)。

GCMを使用する場合、GCMトークン(古いGCM登録IDを置き換える)を取得するために必要になるため、最終的にはこのインスタンスIDを使用する必要があります。

欠点/問題

現在の実装(GPS 7.5)では、アプリがリクエストしたときにインスタンスIDがサーバーから取得されます。これは、上記の呼び出しがブロッキング呼び出しであることを意味します-私の非科学的なテストでは、デバイスがオンラインの場合は1〜3秒、オフラインの場合は0.5〜1.0秒かかります(おそらくこれはあきらめて生成する前に待機する時間の長さです)ランダムID)。これは、Android 5.1.1およびGPS 7.5を搭載したNexus 5で北米でテストされています。

あなたが彼らが意図する目的のためにIDを使用する場合-例えば。アプリの認証、アプリの識別、GCM-この1〜3秒は厄介だと思います(もちろん、アプリによって異なります)。


1
instanceIDのもう1つの重要な欠点は、ユーザーがアプリのデータを消去すると、新しいinstanceIDが生成されることです。
idanakav

興味深いですが、それによって潜在的なユースケースが実際に変わるとは思いません。android_idのようなインスタンスIDは、デバイスの識別には適していません。したがって、サーバーは、ユーザーがアプリをアンインストールして再インストールしたようなユーザーのクリアデータを表示します。これは不合理ではありません。
トム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.