Esiste un ID dispositivo Android univoco?


Risposte:


2026

Settings.Secure#ANDROID_IDrestituisce 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); 

477
A volte è noto che è nullo, è documentato come "può cambiare al ripristino delle impostazioni di fabbrica". Utilizzare a proprio rischio e può essere facilmente modificato su un telefono rooted.
Seva Alekseyev,


18
Penso che dobbiamo stare attenti all'utilizzo di ANDROID_ID nell'hash nella prima risposta perché potrebbe non essere impostato quando l'app viene eseguita per la prima volta, può essere impostato in seguito o potrebbe anche cambiare in teoria, quindi l'ID univoco può cambiare

46
Attenzione, ci sono enormi limiti con questa soluzione: android-developers.blogspot.com/2011/03/…
emmby

35
ANDROID_ID non identifica più in modo univoco un dispositivo (a partire dal 4.2): stackoverflow.com/a/13465373/150016
Tom

1146

AGGIORNAMENTO : A partire dalle recenti versioni di Android, molti dei problemi ANDROID_IDsono 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):

  1. Tutti i dispositivi testati hanno restituito un valore per TelephonyManager.getDeviceId()
  2. Tutti i dispositivi GSM (tutti testati con una SIM) hanno restituito un valore per TelephonyManager.getSimSerialNumber()
  3. Tutti i dispositivi CDMA hanno restituito null per getSimSerialNumber()(come previsto)
  4. Tutti i dispositivi con un account Google aggiunto hanno restituito un valore per ANDROID_ID
  5. Tutti i dispositivi CDMA hanno restituito lo stesso valore (o derivazione dello stesso valore) per entrambi ANDROID_IDe TelephonyManager.getDeviceId()- purché durante l'installazione sia stato aggiunto un account Google.
  6. Non ho ancora avuto la possibilità di testare dispositivi GSM senza SIM, un dispositivo GSM senza account Google aggiunto o nessuno dei dispositivi in ​​modalità aereo.

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 TelephonyManagerproprietà, 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;

151
L'ID basato sulla telefonia non sarà presente sui dispositivi tablet, eh?
Seva Alekseyev,

22
Ecco perché ho detto che la maggior parte non funzionerà sempre :) Devo ancora vedere una risposta a questa domanda che sia affidabile per tutti i dispositivi, tutti i tipi di dispositivi e tutte le configurazioni hardware. Ecco perché questa domanda è qui per cominciare. È abbastanza chiaro che non esiste una soluzione per tutti. I produttori dei singoli dispositivi possono avere numeri di serie dei dispositivi, ma questi non sono esposti per noi e non è un requisito. Quindi ci resta ciò che è disponibile per noi.
Joe,

31
L'esempio di codice funziona alla grande. Ricorda di aggiungere <uses-permission android:name="android.permission.READ_PHONE_STATE" />al file manifest. Se la memorizzazione in un database, la stringa restituita è lunga 36 caratteri.
Richard,


18
@softarn: credo che ciò a cui ti riferisci sia il Blog per sviluppatori Android a cui è già collegato Emmby, il che spiega cosa stai cercando di dire, quindi forse avresti semplicemente dovuto annullare l'upgrade del suo commento. Ad ogni modo, come menziona emmby nella sua risposta, ci sono ancora problemi anche con le informazioni sul blog. La domanda richiede un identificatore DEVICE univoco (non identificativo di installazione), quindi non sono d'accordo con la tua affermazione. Il blog ipotizza che ciò che si desidera non sia necessariamente tracciare il dispositivo, mentre la domanda richiede proprio questo. Sono d'accordo con il blog altrimenti.
Joe,

439

Ultimo aggiornamento: 6/06/15


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.

Problema principale: hardware e software

Hardware

  • Gli utenti possono cambiare hardware, tablet o telefono Android, quindi gli ID univoci basati sull'hardware non sono una buona idea per gli UTENTI DI TRACCIAMENTO
  • Per TRACKING HARDWARE , questa è un'ottima idea

Software

  • Gli utenti possono cancellare / modificare la propria ROM se sono rootati
  • Puoi tenere traccia degli utenti su più piattaforme (iOS, Android, Windows e Web)
  • La cosa migliore da tenere traccia di UN UTENTE INDIVIDUALE con il loro consenso è semplicemente fare in modo che effettuino il login (rendilo perfetto usando OAuth)

Ripartizione generale con Android

- Garantire l'unicità (include i dispositivi rooted) per API> = 9/10 (99,5% dei dispositivi Android)

- Nessuna autorizzazione aggiuntiva

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).

Elenco di opzioni - motivi per cui / perché non usarle:

  • Email utente - Software

    • L'utente potrebbe cambiare la posta elettronica - ALTAMENTE improbabile
    • API 5+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />o
    • API 14+ <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

    • Gli utenti possono cambiare i numeri di telefono - ALTAMENTE improbabile
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI - Hardware (solo telefoni, esigenze android.permission.READ_PHONE_STATE)

    • La maggior parte degli utenti odia il fatto che si dice "Telefonate" nell'autorizzazione. Alcuni utenti danno valutazioni negative, perché credono che tu stia semplicemente rubando le loro informazioni personali quando tutto ciò che vuoi davvero fare è tenere traccia delle installazioni del dispositivo. È ovvio che stai raccogliendo dati.
    • <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)

    • Dal momento che può essere "null", possiamo controllare "null" e modificarne il valore, ma ciò significa che non sarà più univoco.
    • Se si dispone di un utente con un dispositivo ripristinato alle impostazioni di fabbrica, il valore potrebbe essere stato modificato o alterato sul dispositivo rooted, pertanto potrebbero esserci voci duplicate se si stanno monitorando le installazioni dell'utente.
  • Indirizzo MAC WLAN - Hardware (esigenze android.permission.ACCESS_WIFI_STATE)

    • Questa potrebbe essere la seconda opzione migliore, ma stai ancora raccogliendo e memorizzando un identificatore univoco che proviene direttamente da un utente. Questo è ovvio che stai raccogliendo dati.
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Indirizzo MAC Bluetooth - Hardware (dispositivi con Bluetooth, esigenze android.permission.BLUETOOTH)

    • La maggior parte delle applicazioni sul mercato non usa il Bluetooth, quindi se la tua applicazione non usa il Bluetooth e lo stai includendo, l'utente potrebbe diventare sospettoso.
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • ID pseudo-univoco - Software (per tutti i dispositivi Android)

    • Molto possibile, può contenere collisioni - Vedi il mio metodo pubblicato di seguito!
    • Ciò ti consente di avere un ID "univoco" dall'utente senza prendere nulla di privato. È possibile creare il proprio ID anonimo dalle informazioni sul dispositivo.

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.

API> = 9:

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!

API <9:

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 tutti gli altri falliscono:

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:

  • La rimozione di "Android.SECURE_ID" a causa di ripristini di fabbrica potrebbe causare la modifica del valore
  • Modificato il codice per cambiare sull'API
  • Modificato lo Pseudo

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

Novità (per app con pubblicità E Google Play Services):

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

Importante:

È 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 ().

Attenzione, gli utenti possono ripristinare:

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!

InstanceID di Google Player Services

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


Ma la Buildclasse 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)
LuckyMe

2
ho usato il tuo metodo nella mia app per inviare commenti. ho brutte notizie. purtroppo PsuedoID non è completamente unico. il mio server ha registrato più di 100 per 5 ID e più di 30 per quasi 30 ID. gli ID più ripetuti sono 'ffffffff-fc8f-6093-ffff-ffffd8' (159 record) e 'ffffffff-fe99-b334-ffff-ffffef' (154 volte). anche in base al tempo e ai commenti è ovvio che ci sono persone diverse. il totale dei record fino ad ora è 10.000. per favore fatemi sapere perché è successo. carri armati.
Hojjat Reygan

1
L'ho scritto 1.5+ anni fa. Non sono sicuro del perché non sia unico per te. Puoi provare l'ID pubblicità. In caso contrario, puoi trovare la tua soluzione.
Jared Burrows,

2
sorta..Apprezzerei molto se passassi attraverso la domanda e riflettessi su questo
Durai Amuthan.H

1
@ user1587329 Grazie. Sto cercando di tenerlo aggiornato per tutti. Questa domanda è difficile quando si tratta di hardware vs software e multipiattaforma.
Jared Burrows,

340

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:

  • ANDROID_ID è l'identificatore del dispositivo preferito. ANDROID_ID è perfettamente affidabile nelle versioni di Android <= 2.1 o> = 2.3. Solo 2.2 ha i problemi menzionati nel post.
  • Diversi dispositivi di diversi produttori sono interessati dal bug ANDROID_ID in 2.2.
  • Per quanto ho potuto determinare, tutti i dispositivi interessati hanno lo stesso ANDROID_ID , che è 9774d56d682e549c . Che è anche lo stesso ID dispositivo riportato dall'emulatore, tra l'altro.
  • Google ritiene che gli OEM abbiano risolto il problema per molti o la maggior parte dei loro dispositivi, ma sono stato in grado di verificare che a partire dall'inizio di aprile 2011, almeno, è ancora abbastanza facile trovare dispositivi con ANDROID_ID rotto.

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;
    }
}

6
Non dovresti eseguire l'hashing dei vari ID in modo che abbiano tutti le stesse dimensioni? Inoltre, dovresti eseguire l'hashing dell'ID dispositivo per non esporre accidentalmente informazioni private.
Steve Pomeroy,

2
Buoni punti, Steve. Ho aggiornato il codice per restituire sempre un UUID. Ciò garantisce che a) gli ID generati abbiano sempre le stesse dimensioni e b) gli ID android e device siano sottoposti a hash prima di essere restituiti per evitare di esporre accidentalmente informazioni personali. Ho anche aggiornato la descrizione per notare che l'ID del dispositivo persisterà tra i ripristini di fabbrica e che questo potrebbe non essere auspicabile per alcuni utenti.
emmby,

1
Credo che tu sia errato; la soluzione preferita è tenere traccia delle installazioni, non degli identificatori dei dispositivi. Il tuo codice è sostanzialmente più lungo e più complesso di quello nel post del blog e non è ovvio per me che aggiunge alcun valore.
Tim Bray,

7
Bene, ho aggiornato il commento per suggerire fortemente agli utenti di utilizzare gli ID di installazione delle app anziché gli ID dei dispositivi. Tuttavia, penso che questa soluzione sia ancora preziosa per le persone che hanno bisogno di un dispositivo anziché dell'ID di installazione.
emmby,

8
ANDROID_ID può cambiare al ripristino delle impostazioni di fabbrica, quindi non può identificare anche i dispositivi
Samuel

180

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 ':).


Stavo usando il metodo di @Lenn Dolling con l'ora corrente aggiunta per un ID unico. Ma questo sembra un modo più semplice e affidabile. Grazie Reto Meier e Antony Nolan
Gökhan Barış Aker il

È fantastico, ma per quanto riguarda i dispositivi rooted? Possono accedervi e cambiarti facilmente in un altro.
tasomaniaco

3
Ottima opzione se non hai bisogno dell'ID univoco per persistere dopo una disinstallazione e reinstallazione (ad es. Evento / gioco promozionale in cui hai tre possibilità di vincere, punto).
Kyle Clegg,

2
La presentazione di Meier si basa sull'utilizzo di Android Backup Manager, che a sua volta dipende dalla scelta dell'utente di attivare quella funzione. Questo va bene per le preferenze dell'utente dell'app (uso di Meier), perché se l'utente non ha selezionato quell'opzione, non ne farà il backup. Tuttavia, la domanda originale riguarda la generazione di un ID univoco per il dispositivo e questo ID viene generato per app e nemmeno per installazione, figuriamoci per dispositivo e poiché si basa sulla selezione dell'opzione di backup da parte dell'utente, i suoi usi oltre l'utente le preferenze (ad es. per una prova a tempo limitato) sono limitate.
Carl

12
Questo non funzionerà su una disinstallazione o cancellazione dei dati.
John Shelley,

106

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_STATEnel 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


8
Ciò ha richiestoandroid.permission.ACCESS_WIFI_STATE
ohhorob,

5
Penso che troverai che non è disponibile quando il WiFi è spento, praticamente su tutti i dispositivi Android. La disattivazione del WiFi rimuove il dispositivo a livello di kernel.
chrisdowney,

12
@Sanandrea - ammettiamolo, su un dispositivo rooted TUTTO può essere falsificato.
ocodo,

5
L'accesso all'indirizzo MAC WiFi è stato bloccato su Android M: stackoverflow.com/questions/31329733/…
breez

6
Da Android 6.x restituisce un indirizzo Mac falso coerente:02:00:00:00:00:00
Behrouz.M

87

Ci sono informazioni piuttosto utili qui .

Copre cinque diversi tipi di ID:

  1. IMEI (solo per dispositivi Android con utilizzo del telefono; necessità android.permission.READ_PHONE_STATE)
  2. ID pseudo-univoco (per tutti i dispositivi Android)
  3. ID Android (può essere nullo, può cambiare al ripristino delle impostazioni di fabbrica, può essere modificato sul telefono rootato)
  4. Stringa di indirizzo MAC WLAN (esigenze android.permission.ACCESS_WIFI_STATE)
  5. BT MAC Address string (dispositivi con Bluetooth, esigenze android.permission.BLUETOOTH)

2
Punto importante escluso (qui e nell'articolo): non è possibile ottenere WLAN o BT MAC se non sono accesi! Altrimenti penso che il MAC MAC WLAN sarebbe l'identificatore perfetto. Non hai alcuna garanzia che l'utente possa mai attivare il proprio Wi-Fi e non credo davvero che sia "appropriato" attivarlo.
Tom,

1
@Tom sbagli. Puoi ancora leggere WLAN o BT MAC anche quando sono spenti. Tuttavia, non esiste alcuna garanzia che il dispositivo disponga di moduli WLAN o BT disponibili.
Marq

2
In particolare, gli indirizzi MAC locali WiFi e Bluetooth non sono più disponibili. Il metodo getMacAddress () dell'oggetto aWifiInfo e il metodo BluetoothAdapter.getDefaultAdapter (). GetAddress () restituiranno entrambi02: 00: 00: 00: 00: 00 da ora in poi
Sarika Kate,

4
@sarikakate È vero solo nella 6.0 Marshmallow e oltre ... Funziona ancora come previsto nella 6.0 Marshmallow inferiore.
Smeet,

@Smeet Sì, hai ragione. Ho dimenticato di dire che funziona sotto 6.0
sarika kate,

51

Il blog ufficiale per sviluppatori Android ora contiene un articolo completo proprio su questo argomento, Identificazione delle installazioni di app .


4
E il punto chiave di tale argomento è che se stai cercando di ottenere un ID univoco dall'hardware, probabilmente stai commettendo un errore.
Tim Bray,

3
E se stai consentendo il ripristino del blocco del dispositivo tramite un ripristino delle impostazioni di fabbrica, il tuo modello di prova è buono come morto.
Seva Alekseyev,

43

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.


5
Questo non porta a più dispositivi con lo stesso ID quando un utente ha più dispositivi? Un tablet e un telefono per esempio.
Tosa,

Per questo è richiesto l'obiettivo minimo 8.
Halxinate

Questo sarebbe il modo preferito per creare un payload di verifica quando si effettuano acquisti in-app? Dal commento del codice dell'esempio di fatturazione in-app: "Quindi un buon payload per sviluppatori ha queste caratteristiche: 1. Se due utenti diversi acquistano un articolo, il payload è diverso tra loro, in modo che l'acquisto di un utente non possa essere riprodotto su un altro utente. 2. Il payload deve essere tale da poterlo verificare anche quando l'app non era quella che ha avviato il flusso di acquisto (in modo che gli articoli acquistati dall'utente su un dispositivo funzionino su altri dispositivi di proprietà dell'utente). "
TouchBoarder

@Tosa Avevo la stessa domanda. Ma non potremmo usare di nuovo questa stessa tecnica per creare ID di dispositivi virtuali e non eseguire il backup allo stesso modo? L'ID del dispositivo non persisterebbe oltre la cancellazione o la reinstallazione, ma se disponiamo di un ID utente persistente potremmo non aver bisogno di così tanto da un ID dispositivo.
jwehrle,

39

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 .)


7
Soluzione interessante. Sembra che questa sia una situazione in cui dovresti davvero eseguire l'hashing di tutti quei dati concatenati invece di cercare di creare la tua funzione "hash". Ci sono molti casi in cui potresti ottenere collisioni anche se ci sono dati sostanziali che sono diversi per ciascun valore. Il mio consiglio: usa una funzione hash e poi trasforma i risultati binari in decimali e troncali come necessario. Per farlo bene, anche se dovresti davvero usare un UUID o una stringa di hash completa.
Steve Pomeroy,

21
Dovresti dare credito alle tue fonti ... Questo è stato sollevato dal seguente articolo: pocketmagic.net/?p=1662
Steve Haley,

8
Questo ID è aperto a collisioni come se non sapessi cosa. È praticamente garantito che sia lo stesso su dispositivi identici dello stesso vettore.
Seva Alekseyev,

7
Questo può cambiare anche se il dispositivo viene aggiornato.
David, dato il

8
Molto, pessima soluzione. Testato su due Nexus 5 ... Restituisce gli stessi numeri.
Sinan Dizdarević,

38

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) {

}

Ho appena letto sullo sviluppatore xda che ro.serialnoviene utilizzato per generare il Settings.Secure.ANDROID_ID. Quindi sono sostanzialmente rappresentazioni diverse dello stesso valore.
Martin,

@Martin: ma probabilmente il numero di serie non cambia al ripristino del dispositivo. No? Ne ANDROID_IDderiva solo un nuovo valore .
Ronnie,

In realtà su tutti i dispositivi che ho testato sono identici. O almeno i valori di hash dove lo stesso (per motivi di privacy non scrivo i valori veri nei file di registro).
Martin,

Questo valore è uguale aandroid.os.Build.SERIAL
eugeneek l'

android.os.Build.SERIALsarà deprecato in Android O, vedi android-developers.googleblog.com/2017/04/…
EpicPandaForce

32

È 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-9489che 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 FirebaseInstanceIdaggiungi 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.


3
Per ora, questa è la risposta migliore, e la prima frase è il miglior riassunto: "È una domanda semplice, senza una risposta semplice
adorala

Devi apprezzare il commento "ad eccezione della prevenzione delle frodi di pagamento e della telefonia" senza risposta fornita su come affrontare tale caso d'uso.
Eran Boudjnah,

@EranBoudjnah Quella citazione dal riferimento ufficiale, che è collegato nella risposta. Posso tentare di affrontare quel caso d'uso, ma poiché non è specifico alla domanda OP, secondo la politica di stackoverflow - dovrebbe essere fatto in una domanda dedicata separata.
Nikita Kurtin,

Sto solo dicendo che la risposta è incompleta. Ma sono consapevole che non è colpa tua in quanto è una citazione.
Eran Boudjnah,

1
@ M.UsmanKhan, la risposta è scritta subito dopo: " Oggi non è solo una buona pratica, in realtà devi farlo per legge secondo il GDPR - identificatori e regolamenti simili. "
Nikita Kurtin

27

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); 

21

Un campo seriale è stato aggiunto alla Buildclasse 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.


2
Sfortunatamente, è "sconosciuto".
m0skit0,

18

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.


Qual è l'id? è forse 9774d56d682e549c?
Mr_and_Mrs_D,

Wow, è stato tanto tempo fa che ho lanciato quel tablet. Non saprei dire.
Tony Maro,

Lavori. Inoltre, è molto più compatto dell'ID che ottengo dall'UUID casuale.
Treewallie

1
@Treewallie funziona? Puoi ottenere lo stesso ID dispositivo da app diverse?
Arnold Brown,

@ArnoldBrown Sì. Assicurati di provarlo a fondo. Buona giornata: D
Treewallie,

16

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

Se vuoi tenere traccia delle installazioni di app, questo è perfetto. Tracciare i dispositivi è molto più complicato e non sembra esserci una soluzione completamente a tenuta d'aria.
Luca Spiller,

Che dire dei dispositivi rooted? Possono cambiare facilmente questo ID di installazione, giusto?
tasomaniaco

Assolutamente. La radice può cambiare l'ID di installazione. È possibile verificare la presenza di root utilizzando questo blocco di codice: stackoverflow.com/questions/1101380/…
Kevin Parker,

se ripristiniamo la fabbrica, il file verrà eliminato?
Jamshid,

Se ripristini le impostazioni di fabbrica ed elimini o formatti la partizione / data, l'UUID è diverso.
Kevin Parker,

12

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" />

10

L'ID univoco del dispositivo di un dispositivo con sistema operativo Android come String, usando TelephonyManagere 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 .


9

Esistono molti approcci diversi per aggirare quei ANDROID_IDproblemi (a nullvolte possono essere o i dispositivi di un modello specifico restituiscono sempre lo stesso ID) con pro e contro:

  • Implementazione di un algoritmo di generazione di ID personalizzato (basato su proprietà del dispositivo che dovrebbero essere statiche e che non cambieranno -> chissà)
  • Abuso di altri ID come IMEI , numero di serie, indirizzo Wi-Fi / Bluetooth-MAC (non esistono su tutti i dispositivi o diventano necessarie autorizzazioni aggiuntive)

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_IDfallback per i problemi sopra menzionati.


8

Che ne dici dell'IMEI . Questo è unico per Android o altri dispositivi mobili.


9
Non per i miei tablet, che non hanno un IMEI poiché non si collegano al mio gestore di telefonia mobile.
Brill Pappin,

2
Per non parlare dei dispositivi CDMA che hanno un ESN anziché un IMEI.
David, dato il

@David Dato c'è qualche CDMA con Android?
Elzo Valugi,

1
Lo farà solo se è un telefono :) Un tablet potrebbe non esserlo .
Brill Pappin,

3
@ElzoValugi È già "oggi" e non tutti i tablet dispongono di schede SIM.
Matthew Quiros,

8

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;
}

se utilizziamo ReadPhoneState nella versione 6.0 chiedendo l'autorizzazione di runtime
Harsha,

8

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);
        }
    }
}

8

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 longIdstringa. 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 "";
}

1
Aggiungendo l' UUID a longIde memorizzandolo in un file, lo renderà l'identificatore più univoco:String uuid = UUID.randomUUID().toString();
Mousa Alfhaily,

1
Se tutto il resto fallisce, se l'utente ha un valore inferiore a API 9 (inferiore a Gingerbread), ha ripristinato il proprio telefono o "Secure.ANDROID_ID". se restituisce "null", semplicemente l'ID restituito sarà basato esclusivamente sulle informazioni del proprio dispositivo Android. Questo è dove possono verificarsi le collisioni. Cerca di non utilizzare DISPLAY, HOST o ID: questi elementi potrebbero cambiare. Se ci sono collisioni, ci saranno dati sovrapposti. La fonte: gist.github.com/pedja1/fe69e8a80ed505500caa
Mousa Alfhaily

Se proviamo a ottenere un numero univoco con questa riga di codice, possiamo dire che è un ID univoco e non entrerà mai in conflitto con nessun altro dispositivo?
Ninja,

1
@Ninja Poiché l'indirizzo mac di BLE è univoco, sì, l'ID generato sarà sempre univoco. Tuttavia, se vuoi davvero esserne sicuro, suggerirei di aggiungere un UUID a longId. Cambia quella riga in questo modo: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();questo garantisce che l'ID generato sarà univoco.
ᴛʜᴇᴘᴀᴛᴇʟ

@ ᴛʜᴇᴘᴀᴛᴇʟ Grazie mille per questo enorme aiuto. In realtà i dati della mia app sono molto sensibili, quindi devo esserne sicuro, per questo sto solo confermando queste cose.
Ninja

7

Un altro modo è quello di utilizzare /sys/class/android_usb/android0/iSerialun'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:

  • Galaxy Nexus
  • Nexus S
  • Motorola Xoom 3G
  • Toshiba AT300
  • HTC One V
  • Mini MK802
  • Samsung Galaxy S II

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.


Ho appena letto il tuo post sul blog. Credo che questo non sia unico: Build.SERIAL è anche disponibile senza autorizzazioni ed è (in teoria) un numero seriale hardware univoco.
Tom,

1
Hai ragione. È solo un altro modo in cui il tuo dispositivo può essere monitorato e, come hai detto, entrambi i modi non richiedono autorizzazioni per l'app.
insitusec,

7

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;
}

Di recente ho scoperto che il dispositivo client di tipo SM-G928F / Galaxy S6 edge + fornisce solo 15 cifre esadecimali invece di 16 per l'ID Android.
Holger Jakobs,

7

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.


7

Uso il seguente codice per ottenere IMEIo utilizzare Secure. ANDROID_IDin 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);

7

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_IDsembra 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.


1
questa risposta è un copypaste dal vecchio blog google android-developers.googleblog.com/2011/03/… . Quindi il bug è già stato risolto?
Sergii,


6

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).


1
un altro aspetto negativo significativo di instanceID è che verrà generato per te un nuovo instanceID se l'utente cancella i dati dell'app.
idanakav,

Interessante, ma non penso che cambi davvero i potenziali casi d'uso: l'ID istanza, come android_id, non è adatto all'identificazione di un dispositivo. Quindi il tuo server vedrà i dati di cancellazione dell'utente come la disinstallazione e la reinstallazione della tua app da parte dell'utente, il che non è irragionevole.
Tom,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.