Androidデバイスには一意のIDがありますか?ある場合、Javaを使用してそれにアクセスする簡単な方法は何ですか?
Androidデバイスには一意のIDがありますか?ある場合、Javaを使用してそれにアクセスする簡単な方法は何ですか?
回答:
Settings.Secure#ANDROID_ID
Android ID を各ユーザーの一意の 64ビット16進文字列として返します。
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
更新:Androidの最近のバージョンでは、に関する多くの問題ANDROID_ID
が解決されており、このアプローチはもはや必要ないと考えています。アンソニーの答えを見てください。
完全な開示:私のアプリはもともと以下のアプローチを使用していましたが、現在はこのアプローチを使用していません。現在は、emmbyの回答リンクへのAndroidデベロッパーブログエントリで概説されているアプローチ(つまり、の生成と保存)を使用しています。UUID#randomUUID()
この質問には多くの答えがありますが、そのほとんどは「一部」の時間でしか機能しませんが、残念ながらそれでは不十分です。
デバイスのテストに基づいて(すべての電話、少なくとも1つはアクティブ化されていません):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(予想どおり)に対してnullを返しましたANDROID_ID
ANDROID_ID
とTelephonyManager.getDeviceId()
- 限りのGoogleアカウントをセットアップ中に追加されました。あなたは、デバイス自体に固有の何かをしたいのであれば、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;
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
マニフェストファイルに必ず追加してください。データベースに格納する場合、返される文字列は36文字です。
一意のIDの作成に関するすべてのStack Overflowの投稿、Google開発者のブログ、Androidのドキュメントを読んだ後、「疑似ID」が最良のオプションであるかのように感じます。
擬似コード:
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.GET_ACCOUNTS" />
または<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
(Androidデバイスのメインのメールアドレスを取得する方法)ユーザーの電話番号-ソフトウェア
<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の可能性、出荷時設定へのリセット時に変更可能、ルート権限を取得されたデバイスで変更可能)
WLAN MACアドレス-ハードウェア(が必要android.permission.ACCESS_WIFI_STATE
)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Bluetooth MACアドレス-ハードウェア(Bluetooth対応デバイス、ニーズandroid.permission.BLUETOOTH
)
<uses-permission android:name="android.permission.BLUETOOTH "/>
疑似固有ID-ソフトウェア(すべてのAndroidデバイス用)
権限を使用せずに一意のIDを取得する「完全な」方法はないことを知っています。ただし、デバイスのインストールを追跡するだけで十分な場合もあります。一意のIDを作成する場合、追加の権限を使用せずに、Android APIから提供される情報のみに基づいて「疑似一意ID」を作成できます。このようにして、ユーザーの尊敬を示し、優れたユーザーエクスペリエンスを提供することもできます。
疑似一意IDを使用すると、類似のデバイスがあるという事実に基づいて重複が発生する可能性があるという事実に遭遇するだけです。組み合わせたメソッドを調整して、よりユニークにすることができます。ただし、一部の開発者はデバイスのインストールを追跡する必要があり、これは同様のデバイスに基づいたトリックまたはパフォーマンスを実行します。
AndroidデバイスがAPI 9以上の場合、 'Build.SERIAL'フィールドにより、これは一意であることが保証されます。
覚えておいてください。あなたは技術的にAPIが9未満のユーザーの約0.5%だけを見逃しています。残りの部分に集中できます。これはユーザーの99.5%です。
ユーザーのAndroidデバイスがAPI 9より低い場合。うまくいけば、彼らは出荷時設定へのリセットを行っておらず、「Secure.ANDROID_ID」は保持されるか「null」にならないでしょう。(http://developer.android.com/about/dashboards/index.htmlを参照してください)
他のすべてが失敗した場合、ユーザーがAPI 9よりも低い(Gingerbreadよりも低い)場合、デバイスをリセットするか、「Secure.ANDROID_ID」が「null」を返します。返されるIDは、Androidデバイスの情報のみに基づいています。これは、衝突が発生する可能性がある場所です。
変更:
以下の方法をご覧ください。
/**
* 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デベロッパーコンソールから:
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
私は私が情報を得たすべてのリンクを参照しようとしました。あなたが行方不明で含まれている必要がある場合は、コメントしてください!
Build
、OSの更新時にクラスは変更されませんか?特にAPIが更新された場合はどうなりますか?もしそうなら、どのようにこれがユニークであることを保証しますか?(あなたが書いた方法について話す)
Dave Webbが言及しているように、Android開発者ブログにはこれをカバーする記事があります。彼らが好むソリューションは、デバイスではなくアプリのインストールを追跡することです。これは、ほとんどのユースケースでうまく機能します。ブログの投稿では、その機能を実現するために必要なコードを紹介しています。ぜひチェックしてみてください。
ただし、ブログの投稿では、アプリのインストールIDではなくデバイスIDが必要な場合の解決策について説明しています。Googleの担当者と話し合って、必要な場合に備えていくつかの項目についてさらに説明を求めました。前述のブログ投稿で言及されていないデバイス識別子について私が発見したものは次のとおりです。
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;
}
}
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が必要です。これを使用する予定です。今後の分析では(つまり、まだその段階では行っていません:)。
また、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
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
5つの異なるIDタイプをカバーしています。
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)Android Developersの公式ブログに、このテーマについての記事「Identifying App Installations」の完全な記事が掲載されました。
で、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の講演を参照してください。
また、バックアップエージェントの実装方法の詳細については、データバックアップを参照してください。バックアップは瞬時に実行されないため、テストを実行するにはバックアップを強制する必要があるため、テストの下部にあるセクションを特にお勧めします。
これは一意の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の記事からコピーされました。)
次のコードは、非表示の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
です。したがって、それらは基本的に同じ値の異なる表現です。
ANDROID_ID
から新しい値が導出されます。
android.os.Build.SERIAL
android.os.Build.SERIAL
Android Oでは廃止予定です。android
簡単な質問であり、簡単な答えはありません。
さらに、ここにある既存の答えはすべて、古くなっているか信頼できないかです。
だから、あなたは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(識別子および類似の規制)に従って法律で行う必要があります。
シリアルフィールドがに追加されたBuild
APIレベル9( -ジンジャーブレッドアンドロイド2.3)でクラス。ドキュメントには、ハードウェアのシリアル番号を表すと記載されています。したがって、デバイス上に存在する場合、一意である必要があります。
ただし、APIレベルが9以上のすべてのデバイスで実際にサポートされている(= nullではない)かどうかはわかりません。
追加することの1つ-私には、これらのユニークな状況の1つがあります。
使用:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Viewsonic G TabletがNullではないDeviceIDを報告しても、すべてのG Tabletが同じ番号を報告することがわかりました。
「ユニークな」DeviceIDに基づいて誰かのアカウントに即座にアクセスできる「Pocket Empires」をプレイするのを面白くします。
私のデバイスには携帯ラジオがありません。
9774d56d682e549c
ですか?
アプリケーションのインストール元である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();
}
}
クラスファイルに以下のコードを追加します。
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" />
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が提案する方法を強くお勧めします。アプリインストールの識別をご覧ください。
これらのANDROID_ID
問題を回避するには、多くの異なるアプローチがあります(場合によってnull
は、特定のモデルのデバイスが常に同じIDを返すこともあります)。
私自身は、Android(https://github.com/vieux/OpenUDIDを参照)の既存のOpenUDID実装(https://github.com/ylechelle/OpenUDIDを参照)を使用することを好みます。統合は簡単で、ANDROID_ID
上記の問題のフォールバックを使用できます。
IMEIはどうですか。これは、Androidや他のモバイルデバイスに固有のものです。
ここに私がユニークな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;
}
私の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);
}
}
}
ここには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 "";
}
longId
ます。この1行を次のように変更します。String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
これにより、生成されたIDが一意になることが保証されます。
もう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ファイルを開き、文字を読み取ります。すべてのデバイスがこのファイルを持っているわけではないので、例外ハンドラで必ずラップしてください。
少なくとも次のデバイスでは、このファイルが誰でも読み取り可能であることがわかっています。
また、私のブログの記事「Androidハードウェアのシリアル番号が権限のないアプリに漏れている」を参照して、他に入手可能なファイルについて説明してください。
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;
}
特定の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を含むリストから取得する他のインターフェースの集まりですが、これは電話によって異なる場合があります。
IMEI
Secure を取得または使用するには、次のコードを使用します。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);
すなわち、 Settings.Secure.ANDROID_ID
。これは、デバイスが最初に起動したときに生成および保存される64ビットの量です。デバイスがワイプされるとリセットされます。
ANDROID_ID
一意のデバイス識別子に適しています。欠点もあります。まず、2.2より前のAndroidのリリースでは100%の信頼性がありません。(“Froyo”).
また、大手メーカーの人気のある携帯電話には、すべてのインスタンスが同じANDROID_IDであるという広く観察されているバグが1つ以上あります。
Androidデバイスで使用可能な一意のIDを理解する。この公式ガイドを使用してください。
一意の識別子のベストプラクティス:
IMEI、Macアドレス、インスタンスID、GUID、SSAID、広告ID、Safety Net APIでデバイスを確認します。
https://developer.android.com/training/articles/user-data-ids
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秒は厄介だと思います(もちろん、アプリによって異なります)。
ANDROID_ID
いる場合は、この回答とこのバグを必ずお読みください。