I dispositivi Android hanno un ID univoco e, in tal caso, qual è un modo semplice per accedervi utilizzando Java?
I dispositivi Android hanno un ID univoco e, in tal caso, qual è un modo semplice per accedervi utilizzando Java?
Risposte:
Settings.Secure#ANDROID_ID
restituisce l'ID Android come univoco per ogni stringa esadecimale a 64 bit.
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
AGGIORNAMENTO : A partire dalle recenti versioni di Android, molti dei problemi ANDROID_ID
sono stati risolti e credo che questo approccio non sia più necessario. Dai un'occhiata alla risposta di Anthony .
Informativa completa: la mia app ha utilizzato inizialmente l'approccio seguente ma non utilizza più questo approccio, e ora utilizziamo l'approccio descritto nella voce Blog degli sviluppatori Android a cui si collega la risposta di emmby (vale a dire, generare e salvare a UUID#randomUUID()
).
Ci sono molte risposte a questa domanda, la maggior parte delle quali funzionerà solo "alcune" del tempo, e sfortunatamente non è abbastanza buono.
Sulla base dei miei test sui dispositivi (tutti i telefoni, almeno uno dei quali non è attivato):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(come previsto)ANDROID_ID
ANDROID_ID
e TelephonyManager.getDeviceId()
- purché durante l'installazione sia stato aggiunto un account Google.Quindi, se vuoi qualcosa di unico per il dispositivo stesso, TM.getDeviceId()
dovrebbe essere sufficiente. Ovviamente alcuni utenti sono più paranoici di altri, quindi potrebbe essere utile eseguire l'hash 1 o più di questi identificatori, in modo che la stringa sia ancora praticamente unica per il dispositivo, ma non identifichi esplicitamente il dispositivo effettivo dell'utente. Ad esempio, usando String.hashCode()
, combinato con un 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();
potrebbe risultare in qualcosa del tipo: 00000000-54b3-e7c7-0000-000046bffd97
Funziona abbastanza bene per me.
Come Richard menziona di seguito, non dimenticare che hai bisogno dell'autorizzazione per leggere le TelephonyManager
proprietà, quindi aggiungi questo al tuo manifest:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
import libs
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
al file manifest. Se la memorizzazione in un database, la stringa restituita è lunga 36 caratteri.
Dopo aver letto tutti i post di Stack Overflow sulla creazione di un ID univoco, il blog degli sviluppatori di Google e la documentazione di Android, mi sento come se "Pseudo ID" fosse l'opzione migliore possibile.
Codice psuedo:
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)
Grazie a @stansult per aver pubblicato tutte le nostre opzioni (in questa domanda Stack Overflow).
Email utente - Software
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
o<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
( Come ottenere l'indirizzo e-mail principale del dispositivo Android )Numero di telefono dell'utente - Software
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - Hardware (solo telefoni, esigenze android.permission.READ_PHONE_STATE
)
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
ID Android - Hardware (può essere nullo, può cambiare al ripristino delle impostazioni di fabbrica, può essere modificato su un dispositivo rootato)
Indirizzo MAC WLAN - Hardware (esigenze android.permission.ACCESS_WIFI_STATE
)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Indirizzo MAC Bluetooth - Hardware (dispositivi con Bluetooth, esigenze android.permission.BLUETOOTH
)
<uses-permission android:name="android.permission.BLUETOOTH "/>
ID pseudo-univoco - Software (per tutti i dispositivi Android)
So che non esiste un modo "perfetto" per ottenere un ID univoco senza utilizzare le autorizzazioni; tuttavia, a volte è sufficiente monitorare l'installazione del dispositivo. Quando si tratta di creare un ID univoco, possiamo creare un "ID univoco pseudo" basato esclusivamente sulle informazioni fornite dall'API Android senza utilizzare autorizzazioni aggiuntive. In questo modo, possiamo mostrare rispetto per l'utente e provare a offrire anche una buona esperienza utente.
Con un ID pseudo-unico, ti imbatti solo nel fatto che potrebbero esserci duplicati in base al fatto che ci sono dispositivi simili. È possibile modificare il metodo combinato per renderlo più unico; tuttavia, alcuni sviluppatori devono tenere traccia delle installazioni dei dispositivi e questo farà il trucco o le prestazioni in base a dispositivi simili.
Se il loro dispositivo Android è API 9 o superiore, questo è garantito per essere unico a causa del campo "Build.SERIAL".
RICORDA , tecnicamente stai perdendo solo circa lo 0,5% degli utenti con API <9 . Quindi puoi concentrarti sul resto: questo è il 99,5% degli utenti!
Se il dispositivo Android dell'utente è inferiore all'API 9; si spera che non abbiano eseguito un ripristino delle impostazioni di fabbrica e il loro "Secure.ANDROID_ID" verrà conservato o meno "null". (vedi http://developer.android.com/about/dashboards/index.html )
Se tutto il resto fallisce, se l'utente ha un valore inferiore a API 9 (inferiore a Gingerbread), ha ripristinato il proprio dispositivo o 'Secure.ANDROID_ID' restituisce 'null', quindi semplicemente l'ID restituito si baserà esclusivamente sulle informazioni del proprio dispositivo Android. Questo è dove possono verificarsi le collisioni.
I cambiamenti:
Dai un'occhiata al metodo seguente:
/**
* 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();
}
Dalla console per gli sviluppatori di Google Play:
A partire dal 1 ° agosto 2014, le Norme del programma per gli sviluppatori di Google Play richiedono che tutti i caricamenti e gli aggiornamenti di nuove app utilizzino l'ID pubblicità al posto di qualsiasi altro identificatore persistente per scopi pubblicitari. Per saperne di più
Implementazione :
Autorizzazione:
<uses-permission android:name="android.permission.INTERNET" />
Codice:
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();
}
Fonte / Documenti:
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
È previsto che l'ID pubblicità sostituisca completamente l'utilizzo esistente di altri identificativi a scopi pubblicitari (come l'uso di ANDROID_ID in Settings.Secure) quando Google Play Services è disponibile. I casi in cui Google Play Services non è disponibile sono indicati da GooglePlayServicesNotAvailableException generato da getAdvertisingIdInfo ().
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
Ho provato a fare riferimento a ogni link da cui ho preso informazioni. Se ti manca e devi essere incluso, commenta!
Build
classe non cambierebbe con l'aggiornamento del sistema operativo? Soprattutto se l'API è stata aggiornata? In tal caso, come garantite che questo sia unico? (Parlando del metodo che hai scritto)
Come menziona Dave Webb, il Blog per sviluppatori Android ha un articolo che tratta questo. La loro soluzione preferita è tenere traccia delle installazioni di app piuttosto che dei dispositivi e funzionerà bene per la maggior parte dei casi d'uso. Il post sul blog ti mostrerà il codice necessario per farlo funzionare, e ti consiglio di dare un'occhiata.
Tuttavia, il post sul blog continua a discutere delle soluzioni se è necessario un identificatore del dispositivo anziché un identificatore di installazione dell'app. Ho parlato con qualcuno di Google per ottenere ulteriori chiarimenti su alcuni elementi nel caso in cui tu debba farlo. Ecco cosa ho scoperto sugli identificatori di dispositivo che NON è menzionato nel suddetto post sul blog:
Sulla base dei consigli di Google, ho implementato una classe che genererà un UUID univoco per ciascun dispositivo, utilizzando ANDROID_ID come seme, se del caso, ricadendo su TelephonyManager.getDeviceId (), se necessario, e in caso contrario, ricorrendo a un UUID univoco generato casualmente persistente durante i riavvii delle app (ma non le reinstallazione di app).
Si noti che per i dispositivi che dispongono di ripiego sul ID del dispositivo, l'ID univoco SARA persistere attraverso reset di fabbrica. Questo è qualcosa di cui essere consapevoli. Se devi assicurarti che un ripristino delle impostazioni di fabbrica ripristini il tuo ID univoco, ti consigliamo di ricorrere direttamente all'UUID casuale anziché all'ID dispositivo.
Ancora una volta, questo codice è per un ID dispositivo, non un ID installazione app. Per la maggior parte delle situazioni, un ID di installazione dell'app è probabilmente quello che stai cercando. Ma se hai bisogno di un ID dispositivo, probabilmente il seguente codice funzionerà per te.
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;
}
}
Ecco il codice che Reto Meier ha usato quest'anno nella presentazione I / O di Google per ottenere un ID univoco per l'utente:
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;
}
Se si accoppia questo con una strategia di backup per inviare le preferenze al cloud (descritto anche nel discorso di Reto , dovresti avere un ID che si lega a un utente e si attacca dopo che il dispositivo è stato cancellato o addirittura sostituito. Ho intenzione di utilizzare questo in analisi andando avanti (in altre parole, non l'ho ancora fatto un po ':).
Inoltre, potresti considerare l'indirizzo MAC dell'adattatore Wi-Fi. Estratto in questo modo:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
Richiede l'autorizzazione android.permission.ACCESS_WIFI_STATE
nel manifest.
Riferito per essere disponibile anche quando il Wi-Fi non è connesso. Se Joe dalla risposta sopra provasse questo sui suoi numerosi dispositivi, sarebbe bello.
Su alcuni dispositivi, non è disponibile quando il Wi-Fi è disattivato.
NOTA: da Android 6.x, restituisce un indirizzo MAC falso coerente:02:00:00:00:00:00
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
Ci sono informazioni piuttosto utili qui .
Copre cinque diversi tipi di ID:
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)Il blog ufficiale per sviluppatori Android ora contiene un articolo completo proprio su questo argomento, Identificazione delle installazioni di app .
Al Google I / O Reto Meier ha rilasciato una risposta robusta per come affrontare questo che dovrebbe soddisfare la maggior parte delle esigenze di sviluppatori per tracciare gli utenti attraverso installazioni. Anthony Nolan mostra la direzione nella sua risposta, ma ho pensato di scrivere l'approccio completo in modo che gli altri possano vedere facilmente come farlo (mi ci è voluto un po 'di tempo per capire i dettagli).
Questo approccio ti fornirà un ID utente anonimo e sicuro che sarà persistente per l'utente su diversi dispositivi (basato sull'account Google primario) e su diverse installazioni. L'approccio di base è generare un ID utente casuale e memorizzarlo nelle preferenze condivise delle app. Quindi utilizzare l'agente di backup di Google per archiviare le preferenze condivise collegate all'account Google nel cloud.
Passiamo attraverso l'approccio completo. Innanzitutto, dobbiamo creare un backup per le nostre condivisioni preferite utilizzando il servizio di backup di Android. Inizia registrando la tua app tramite http://developer.android.com/google/backup/signup.html
.
Google ti fornirà una chiave del servizio di backup che devi aggiungere al manifest. È inoltre necessario indicare all'applicazione di utilizzare BackupAgent come segue:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="your_backup_service_key" />
</application>
Quindi è necessario creare l'agente di backup e dirgli di utilizzare l'agente di supporto per le preferenze condivise:
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);
}
}
Per completare il backup è necessario creare un'istanza di BackupManager nella tua attività principale:
BackupManager backupManager = new BackupManager(context);
Infine, crea un ID utente, se non esiste già, e memorizzalo in 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;
}
Questo User_ID sarà ora persistente su tutte le installazioni, anche se l'utente sposta il dispositivo.
Per ulteriori informazioni su questo approccio, consultare il discorso di Reto .
E per i dettagli completi su come implementare l'agente di backup, consultare Backup dei dati . Consiglio in particolare la sezione in basso sui test poiché il backup non avviene istantaneamente e quindi per testare devi forzare il backup.
Penso che questo sia un modo sicuro per costruire uno scheletro per un ID univoco ... dai un'occhiata.
ID pseudo-univoco, che funziona su tutti i dispositivi Android Alcuni dispositivi non dispongono di un telefono (ad es. Tablet) o, per qualche motivo, non desideri includere l'autorizzazione READ_PHONE_STATE. Puoi ancora leggere dettagli come la versione ROM, il nome del produttore, il tipo di CPU e altri dettagli hardware, che saranno adatti se desideri utilizzare l'ID per un controllo della chiave seriale o altri scopi generali. L'ID calcolato in questo modo non sarà univoco: è possibile trovare due dispositivi con lo stesso ID (basato sulla stessa immagine hardware e ROM) ma i cambiamenti nelle applicazioni del mondo reale sono trascurabili. A questo scopo puoi usare la classe 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
La maggior parte dei membri di Build sono stringhe, quello che stiamo facendo qui è prendere la loro lunghezza e trasformarla tramite modulo in una cifra. Abbiamo 13 cifre di questo tipo e ne stiamo aggiungendo altre due davanti (35) per avere lo stesso ID dimensione dell'IMEI (15 cifre). Ci sono altre possibilità qui sono bene, basta dare un'occhiata a queste stringhe. Restituisce qualcosa di simile 355715565309247
. Non è richiesta alcuna autorizzazione speciale, rendendo questo approccio molto conveniente.
(Ulteriori informazioni: la tecnica sopra indicata è stata copiata da un articolo su Pocket Magic .)
Il codice seguente restituisce il numero di serie del dispositivo utilizzando un'API Android nascosta. Ma questo codice non funziona su Samsung Galaxy Tab perché "ro.serialno" non è impostato su questo dispositivo.
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
viene utilizzato per generare il Settings.Secure.ANDROID_ID
. Quindi sono sostanzialmente rappresentazioni diverse dello stesso valore.
ANDROID_ID
deriva solo un nuovo valore .
android.os.Build.SERIAL
android.os.Build.SERIAL
sarà deprecato in Android O, vedi android-developers.googleblog.com/2017/04/…
È una domanda semplice, senza una risposta semplice.
Inoltre, tutte le risposte esistenti qui sono obsolete o inaffidabili.
Quindi, se stai cercando una soluzione nel 2020 .
Ecco alcune cose da tenere a mente:
Tutti gli identificatori basati su hardware (SSAID, IMEI, MAC, ecc.) Non sono affidabili per i dispositivi non Google (Tutto tranne Pixel e Nexuse), che rappresentano oltre il 50% dei dispositivi attivi in tutto il mondo. Pertanto, le migliori pratiche degli identificatori Android ufficiali indicano chiaramente:
Evitare l'uso di identificatori hardware , come SSAID (Android ID), IMEI, indirizzo MAC, ecc ...
Ciò rende la maggior parte delle risposte sopra non valide. Anche a causa di diversi aggiornamenti di sicurezza Android, alcuni richiedono autorizzazioni di runtime più recenti e più rigide, che possono essere semplicemente negate dall'utente.
Ad esempio, CVE-2018-9489
che riguarda tutte le tecniche basate su WIFI menzionate sopra.
Ciò rende questi identificatori non solo inaffidabili, ma anche inaccessibili in molti casi.
Quindi, in parole più semplici: non usare queste tecniche .
Molte altre risposte qui stanno suggerendo di utilizzare il AdvertisingIdClient
, che è anche incompatibile, dal momento che la sua progettazione dovrebbe essere utilizzata solo per la profilazione degli annunci. È anche indicato nel riferimento ufficiale
Utilizzare solo un ID pubblicità per la profilazione dell'utente o casi di utilizzo di annunci
Non è solo inaffidabile per l'identificazione del dispositivo, ma è anche necessario seguire la privacy dell'utente in merito alla politica di tracciamento degli annunci , in cui si afferma chiaramente che l'utente può ripristinarlo o bloccarlo in qualsiasi momento.
Quindi non usarlo neanche .
Dal momento che non è possibile avere l'identificatore del dispositivo univoco e affidabile globalmente statico desiderato. Il riferimento ufficiale di Android suggerisce:
Utilizzare un FirebaseInstanceId o un GUID memorizzato privatamente quando possibile per tutti gli altri casi d'uso, ad eccezione della prevenzione delle frodi di pagamento e della telefonia.
È unico per l'installazione dell'applicazione sul dispositivo, quindi quando l'utente disinstalla l'app, viene cancellata, quindi non è affidabile al 100%, ma è la cosa migliore successiva.
Per usare FirebaseInstanceId
aggiungi l' ultima dipendenza di messaggistica firebase nel tuo livello
implementation 'com.google.firebase:firebase-messaging:20.2.0'
E usa il codice qui sotto in un thread in background:
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
Se è necessario memorizzare l'identificazione del dispositivo sul server remoto, non memorizzarla così com'è (testo normale), ma un hash con salt .
Oggi non è solo una buona pratica, in realtà è necessario farlo per legge secondo il GDPR - identificatori e regolamenti simili.
Utilizzando il codice seguente, è possibile ottenere l'ID univoco del dispositivo di un dispositivo con sistema operativo Android come stringa.
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Un campo seriale è stato aggiunto alla Build
classe nel livello API 9 (Android 2.3 - Gingerbread). La documentazione afferma che rappresenta il numero seriale dell'hardware. Quindi dovrebbe essere unico, se esiste sul dispositivo.
Non so se sia effettivamente supportato (= non nullo) da tutti i dispositivi con livello API> = 9.
Una cosa che aggiungerò: ho una di quelle situazioni uniche.
usando:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Si scopre che anche se il mio Tablet G Viewsonic riporta un DeviceID che non è Null, ogni singolo Tablet G riporta lo stesso numero.
Lo rende interessante giocare a "Pocket Empires" che ti dà accesso istantaneo all'account di qualcuno in base al "unico" DeviceID.
Il mio dispositivo non ha una radio cellulare.
9774d56d682e549c
?
Per istruzioni dettagliate su come ottenere un identificatore univoco per ogni dispositivo Android da cui è installata l'applicazione, vedere il blog ufficiale degli sviluppatori Android che pubblica le installazioni di identificazione delle app .
Sembra che il modo migliore sia quello di generarne uno tu stesso durante l'installazione e successivamente leggerlo al riavvio dell'applicazione.
Personalmente lo trovo accettabile ma non ideale. Nessun identificatore fornito da Android funziona in tutti i casi in quanto la maggior parte dipende dagli stati radio del telefono (Wi-Fi acceso / spento, cellulare acceso / spento, Bluetooth acceso / spento). Gli altri, come Settings.Secure.ANDROID_ID
devono essere implementati dal produttore e non sono garantiti per essere unici.
Di seguito è riportato un esempio di scrittura di dati in un file di installazione che verrebbe archiviato insieme a qualsiasi altro dato che l'applicazione salva localmente.
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();
}
}
Aggiungi sotto il codice nel file di classe:
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);
Aggiungi in AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
TelephonyManager
e ANDROID_ID
, è ottenuto da: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);
}
Ma consiglio vivamente un metodo suggerito da Google, vedi Identificazione delle installazioni di app .
Esistono molti approcci diversi per aggirare quei ANDROID_ID
problemi (a null
volte possono essere o i dispositivi di un modello specifico restituiscono sempre lo stesso ID) con pro e contro:
Io stesso preferisco usare un'implementazione OpenUDID esistente (vedi https://github.com/ylechelle/OpenUDID ) per Android (vedi https://github.com/vieux/OpenUDID ). È facile da integrare e utilizza i ANDROID_ID
fallback per i problemi sopra menzionati.
Che ne dici dell'IMEI . Questo è unico per Android o altri dispositivi mobili.
Ecco come sto generando l'ID univoco:
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;
}
I miei due centesimi - NB questo è per un ID univoco del dispositivo (err) - non quello di installazione come discusso nel blog degli sviluppatori Android .
Da notare che la soluzione fornita da @emmby ricade in un ID per applicazione poiché le SharedPreferences non sono sincronizzate tra i processi (vedere qui e qui ). Quindi l'ho evitato del tutto.
Invece, ho incapsulato le varie strategie per ottenere un ID (dispositivo) in un enum - la modifica dell'ordine delle costanti dell'enum influisce sulla priorità dei vari modi di ottenere l'ID. Viene restituito il primo ID non nullo o viene generata un'eccezione (secondo le buone pratiche Java di non dare un significato a null). Ad esempio, per prima cosa ho il TELEPHONY, ma una buona scelta di default sarebbe la beta 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);
}
}
}
Ci sono più di 30 risposte qui e alcune sono uguali e alcune sono uniche. Questa risposta si basa su alcune di quelle risposte. Uno di questi è la risposta di @Lenn Dolling.
Combina 3 ID e crea una stringa esadecimale a 32 cifre. Ha funzionato molto bene per me.
3 ID sono:
Pseudo-ID - Viene generato in base alle specifiche del dispositivo fisico
ANDROID_ID - Settings.Secure.ANDROID_ID
Indirizzo Bluetooth - Indirizzo adattatore Bluetooth
Restituirà qualcosa del genere: 551F27C060712A72730B0A0F734064B1
Nota: è sempre possibile aggiungere più ID alla longId
stringa. Ad esempio, Numero di serie. indirizzo adattatore wifi. IMEI. In questo modo lo rendi più unico per dispositivo.
@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
e memorizzandolo in un file, lo renderà l'identificatore più univoco:String uuid = UUID.randomUUID().toString();
longId
. Cambia quella riga in questo modo: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
questo garantisce che l'ID generato sarà univoco.
Un altro modo è quello di utilizzare /sys/class/android_usb/android0/iSerial
un'app senza alcuna autorizzazione.
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
Per fare ciò in Java, basta usare FileInputStream per aprire il file iSerial e leggere i caratteri. Assicurati solo di racchiuderlo in un gestore eccezioni, perché non tutti i dispositivi hanno questo file.
Almeno i seguenti dispositivi sono noti per avere questo file leggibile in tutto il mondo:
Puoi anche vedere il mio post sul blog che perde il numero di serie dell'hardware Android su app non privilegiate dove discuto quali altri file sono disponibili per informazioni.
TelephonyManger.getDeviceId () Restituisce l'ID dispositivo univoco, ad esempio l'IMEI per GSM e il MEID o ESN per i telefoni CDMA.
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
Ma raccomando di usare:
Settings.Secure.ANDROID_ID che restituisce l'ID Android come una stringa esadecimale univoca a 64 bit.
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
A volte TelephonyManger.getDeviceId () restituirà null, quindi per assicurare un ID univoco utilizzerai questo metodo:
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;
}
Per il riconoscimento hardware di un dispositivo Android specifico è possibile controllare gli indirizzi MAC.
puoi farlo in questo modo:
in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
ora nel tuo codice:
List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface interface : interfacesList) {
// This will give you the interface MAC ADDRESS
interface.getHardwareAddress();
}
In ogni dispositivo Android la loro è almeno un'interfaccia "wlan0" che è il chip WI-FI. Questo codice funziona anche quando il WI-FI non è attivato.
PS Loro sono un sacco di altre interfacce che otterrai dall'elenco contenente MACS ma questo può cambiare tra i telefoni.
Uso il seguente codice per ottenere IMEI
o utilizzare Secure. ANDROID_ID
in alternativa, quando il dispositivo non ha funzionalità telefoniche:
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);
Più specificamente Settings.Secure.ANDROID_ID
,. Questa è una quantità a 64 bit che viene generata e memorizzata al primo avvio del dispositivo. Viene ripristinato quando il dispositivo viene cancellato.
ANDROID_ID
sembra una buona scelta per un identificatore univoco del dispositivo. Ci sono degli aspetti negativi: in primo luogo, non è affidabile al 100% nelle versioni di Android precedenti alla 2.2. (“Froyo”).
Inoltre, c'è stato almeno un bug ampiamente osservato in un telefono popolare di un produttore importante, in cui ogni istanza ha lo stesso ANDROID_ID.
Per comprendere gli ID univoci disponibili nei dispositivi Android. Usa questa guida ufficiale.
Best practice per identificatori univoci:
IMEI, indirizzi Mac, ID istanza, GUID, SSAID, ID pubblicità, API Net di sicurezza per verificare i dispositivi.
https://developer.android.com/training/articles/user-data-ids
ID istanza di Google
Rilasciato all'I / O 2015; su Android richiede servizi di riproduzione 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
Sembra che Google intenda utilizzare questo ID per identificare le installazioni su Android, Chrome e iOS.
Identifica un'installazione piuttosto che un dispositivo, ma poi di nuovo, ANDROID_ID (che è la risposta accettata) ora non identifica più nemmeno i dispositivi. Con il runtime ARC viene generato un nuovo ANDROID_ID per ogni installazione ( dettagli qui ), proprio come questo nuovo ID istanza. Inoltre, penso che identificare le installazioni (non i dispositivi) sia ciò che la maggior parte di noi sta effettivamente cercando.
I vantaggi dell'ID istanza
Mi sembra che Google intenda che venga utilizzato per questo scopo (identificando le tue installazioni), è multipiattaforma e può essere utilizzato per una serie di altri scopi (vedere i link sopra).
Se usi GCM, alla fine dovrai utilizzare questo ID istanza perché ne hai bisogno per ottenere il token GCM (che sostituisce il vecchio ID registrazione GCM).
Gli svantaggi / problemi
Nell'attuale implementazione (GPS 7.5) l'ID istanza viene recuperato da un server quando l'app lo richiede. Ciò significa che la chiamata sopra è una chiamata bloccante - nel mio test non scientifico ci vogliono 1-3 secondi se il dispositivo è online e 0,5 - 1,0 secondi se offline (presumibilmente questo è il tempo che attende prima di rinunciare e generare un ID casuale). Questo è stato testato in Nord America su Nexus 5 con Android 5.1.1 e GPS 7.5.
Se si utilizza l'ID per gli scopi che intendono - ad es. autenticazione delle app, identificazione delle app, GCM - Penso che questi 1-3 secondi potrebbero essere un fastidio (a seconda della tua app, ovviamente).
ANDROID_ID
assicurati di leggere questa risposta e questo errore .