Existe-t-il un ID d'appareil Android unique?


Réponses:


2027

Settings.Secure#ANDROID_IDrenvoie l'ID Android comme unique pour chaque chaîne hexadécimale 64 bits d' utilisateur .

import android.provider.Settings.Secure;

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

477
Il est connu pour être parfois nul, il est documenté comme "peut changer lors de la réinitialisation d'usine". Utilisez-le à vos propres risques et il peut être facilement modifié sur un téléphone rooté.
Seva Alekseyev


18
Je pense que nous devons faire attention à utiliser ANDROID_ID dans le hachage dans la première réponse, car il peut ne pas être défini lors de la première exécution de l'application, peut être défini plus tard ou même changer en théorie, donc l'ID unique peut changer

46
Soyez conscient qu'il existe d'énormes limites avec cette solution: android-developers.blogspot.com/2011/03/…
emmby

35
ANDROID_ID n'identifie plus de façon unique un appareil (à partir de la version 4.2): stackoverflow.com/a/13465373/150016
Tom

1146

MISE À JOUR : Depuis les versions récentes d'Android, de nombreux problèmes ANDROID_IDont été résolus et je pense que cette approche n'est plus nécessaire. Veuillez jeter un œil à la réponse d'Anthony .

Divulgation complète: mon application a utilisé l'approche ci-dessous à l'origine mais n'utilise plus cette approche, et nous utilisons maintenant l'approche décrite dans l' entrée du blog des développeurs Android à laquelle la réponse d'Emby est liée (à savoir, générer et enregistrer un UUID#randomUUID()).


Il existe de nombreuses réponses à cette question, dont la plupart ne fonctionneront que "parfois", et malheureusement ce n'est pas suffisant.

Sur la base de mes tests d'appareils (tous les téléphones, dont au moins un n'est pas activé):

  1. Tous les appareils testés ont renvoyé une valeur pour TelephonyManager.getDeviceId()
  2. Tous les appareils GSM (tous testés avec une carte SIM) ont renvoyé une valeur pour TelephonyManager.getSimSerialNumber()
  3. Tous les appareils CDMA sont retournés nuls pour getSimSerialNumber() (comme prévu)
  4. Tous les appareils auxquels un compte Google a été ajouté ont renvoyé une valeur pour ANDROID_ID
  5. Tous les périphériques CDMA ont renvoyé la même valeur (ou dérivation de la même valeur) pour les deux ANDROID_IDet TelephonyManager.getDeviceId()- tant que qu'un compte Google a été ajouté lors de la configuration.
  6. Je n'ai pas encore eu la possibilité de tester des appareils GSM sans carte SIM, un appareil GSM sans compte Google ajouté ou l'un des appareils en mode avion.

Donc, si vous voulez quelque chose d'unique à l'appareil lui-même, cela TM.getDeviceId() devrait suffire. De toute évidence, certains utilisateurs sont plus paranoïaques que d'autres, il peut donc être utile de hacher 1 ou plusieurs de ces identifiants, afin que la chaîne soit toujours pratiquement unique à l'appareil, mais n'identifie pas explicitement l'appareil réel de l'utilisateur. Par exemple, en utilisant String.hashCode(), combiné avec 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();

pourrait entraîner quelque chose comme: 00000000-54b3-e7c7-0000-000046bffd97

Cela fonctionne assez bien pour moi.

Comme Richard le mentionne ci-dessous, n'oubliez pas que vous avez besoin d'une autorisation pour lire les TelephonyManagerpropriétés, alors ajoutez ceci à votre manifeste:

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

importer des bibliothèques

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

151
L'identification basée sur la téléphonie ne sera pas présente sur les tablettes, hein?
Seva Alekseyev

22
C'est pourquoi j'ai dit que la plupart ne fonctionneraient pas tout le temps :) Je n'ai pas encore vu de réponse à cette question qui soit fiable pour tous les appareils, tous les types d'appareils et toutes les configurations matérielles. C'est pourquoi cette question est là pour commencer. Il est assez clair qu'il n'y a pas de solution définitive à cela. Les fabricants d'appareils individuels peuvent avoir des numéros de série, mais ceux-ci ne sont pas exposés pour que nous les utilisions, et ce n'est pas une exigence. Il nous reste donc ce qui est à notre disposition.
Joe

31
L'exemple de code fonctionne très bien. N'oubliez pas d'ajouter <uses-permission android:name="android.permission.READ_PHONE_STATE" />au fichier manifeste. En cas de stockage dans une base de données, la chaîne renvoyée comporte 36 caractères.
Richard

10
Soyez conscient qu'il existe d'énormes limites avec cette solution: android-developers.blogspot.com/2011/03/…
emmby

18
@softarn: Je pense que ce à quoi vous faites référence est le blog des développeurs Android auquel un lien est déjà lié, qui explique ce que vous essayez de dire, alors peut-être auriez-vous simplement dû voter à la place. Quoi qu'il en soit, comme le mentionne emmby dans sa réponse, il y a toujours des problèmes, même avec les informations du blog. La question demande un identifiant unique DEVICE (pas un identifiant d'installation), donc je ne suis pas d'accord avec votre déclaration. Le blog fait l'hypothèse que ce que vous voulez n'est pas nécessairement de suivre l'appareil, alors que la question le demande. Sinon, je suis d'accord avec le blog.
Joe

439

Dernière mise à jour: 6/2/15


Après avoir lu chaque article Stack Overflow sur la création d'un ID unique, le blog des développeurs Google et la documentation Android, j'ai l'impression que le `` pseudo ID '' est la meilleure option possible.

Problème principal: matériel vs logiciel

Matériel

  • Les utilisateurs peuvent changer leur matériel, leur tablette ou leur téléphone Android, donc les identifiants uniques basés sur le matériel ne sont pas de bonnes idées pour suivre les utilisateurs
  • Pour le MATÉRIEL DE SUIVI , c'est une excellente idée

Logiciel

  • Les utilisateurs peuvent effacer / changer leur ROM s'ils sont enracinés
  • Vous pouvez suivre les utilisateurs sur toutes les plateformes (iOS, Android, Windows et Web)
  • Le meilleur moyen de SUIVRE UN UTILISATEUR INDIVIDUEL avec son consentement est de simplement lui demander de se connecter (rendre cela transparent à l'aide d'OAuth)

Répartition globale avec Android

- Garantir l'unicité (inclure les appareils enracinés) pour l'API> = 9/10 (99,5% des appareils Android)

- Pas d'autorisations supplémentaires

Code du pseudo:

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)

Merci à @stansult d'avoir publié toutes nos options (dans cette question Stack Overflow).

Liste des options - raisons pour lesquelles / pourquoi ne pas les utiliser:

  • Courriel de l'utilisateur - Logiciel

    • L'utilisateur pourrait changer d'e-mail - HAUTEMENT improbable
    • API 5+ <uses-permission android:name="android.permission.GET_ACCOUNTS" /> ou
    • API 14+ <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" />( Comment obtenir l'adresse e-mail principale de l'appareil Android )
  • Numéro de téléphone de l'utilisateur - Logiciel

    • Les utilisateurs peuvent changer de numéro de téléphone - très peu probable
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI - Matériel (uniquement téléphones, besoins android.permission.READ_PHONE_STATE)

    • La plupart des utilisateurs détestent le fait qu'il indique "Appels téléphoniques" dans l'autorisation. Certains utilisateurs donnent de mauvaises notes, car ils croient que vous volez simplement leurs informations personnelles alors que tout ce que vous voulez vraiment faire, c'est suivre les installations des appareils. Il est évident que vous collectez des données.
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • ID Android - Matériel (peut être nul, peut changer lors de la réinitialisation d'usine, peut être modifié sur un appareil rooté)

    • Puisqu'il peut être «nul», nous pouvons vérifier «nul» et changer sa valeur, mais cela signifie qu'il ne sera plus unique.
    • Si vous avez un utilisateur avec un périphérique de réinitialisation d'usine, la valeur peut avoir changé ou altéré sur le périphérique rooté, il peut donc y avoir des entrées en double si vous suivez les installations des utilisateurs.
  • Adresse MAC WLAN - Matériel (besoins android.permission.ACCESS_WIFI_STATE)

    • Cela pourrait être la deuxième meilleure option, mais vous collectez et stockez toujours un identifiant unique provenant directement d'un utilisateur. Il est évident que vous collectez des données.
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Adresse MAC Bluetooth - Matériel (appareils avec Bluetooth, besoins android.permission.BLUETOOTH)

    • La plupart des applications sur le marché n'utilisent pas Bluetooth, et donc si votre application n'utilise pas Bluetooth et que vous l'incluez, l'utilisateur peut devenir suspect.
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • ID pseudo-unique - Logiciel (pour tous les appareils Android)

    • Très possible, peut contenir des collisions - Voir ma méthode ci-dessous!
    • Cela vous permet d'avoir un ID «presque unique» de l'utilisateur sans prendre quoi que ce soit de privé. Vous pouvez créer votre propre ID anonyme à partir des informations sur l'appareil.

Je sais qu'il n'y a pas de moyen «parfait» d'obtenir un identifiant unique sans utiliser d'autorisations; cependant, il nous suffit parfois de suivre l'installation de l'appareil. Lorsqu'il s'agit de créer un identifiant unique, nous pouvons créer un 'pseudo identifiant unique' basé uniquement sur des informations que l'API Android nous donne sans utiliser d'autorisations supplémentaires. De cette façon, nous pouvons montrer le respect de l'utilisateur et essayer d'offrir une bonne expérience utilisateur également.

Avec un identifiant pseudo-unique, vous ne rencontrez vraiment que des doublons basés sur le fait qu'il existe des appareils similaires. Vous pouvez modifier la méthode combinée pour la rendre plus unique; cependant, certains développeurs ont besoin de suivre les installations d'appareils et cela fera l'affaire ou les performances basées sur des appareils similaires.

API> = 9:

Si leur appareil Android est API 9 ou supérieur, cela est garanti unique en raison du champ 'Build.SERIAL'.

N'OUBLIEZ PAS , vous ne manquez techniquement qu'environ 0,5% des utilisateurs qui ont une API <9 . Vous pouvez donc vous concentrer sur le reste: c'est 99,5% des utilisateurs!

API <9:

Si l'appareil Android de l'utilisateur est inférieur à l'API 9; nous espérons qu'ils n'ont pas effectué de réinitialisation d'usine et que leur 'Secure.ANDROID_ID' sera conservé ou non 'null'. (voir http://developer.android.com/about/dashboards/index.html )

Si tout le reste échoue:

Si tout le reste échoue, si l'utilisateur a une version inférieure à API 9 (inférieure à Gingerbread), a réinitialisé son appareil ou `` Secure.ANDROID_ID '' renvoie `` null '', alors simplement l'ID retourné sera uniquement basé sur les informations de son appareil Android. C'est là que les collisions peuvent se produire.

Changements:

  • Suppression de «Android.SECURE_ID» en raison de réinitialisations d'usine pouvant entraîner une modification de la valeur
  • Modification du code à modifier sur l'API
  • Changé le pseudo

Veuillez consulter la méthode ci-dessous:

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

Nouveau (pour les applications avec annonces ET services Google Play):

Depuis la console du développeur Google Play:

À compter du 1er août 2014, la politique du programme pour les développeurs de Google Play exige que tous les nouveaux téléchargements et mises à jour d'applications utilisent l'ID publicitaire au lieu de tout autre identifiant persistant à des fins publicitaires. Apprendre encore plus

Réalisation :

Autorisation:

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

Code:

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

Source / Documents:

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

Important:

Il est prévu que l'ID publicitaire remplace complètement l'utilisation existante d'autres identifiants à des fins publicitaires (telles que l'utilisation d'ANDROID_ID dans Settings.Secure) lorsque les services Google Play sont disponibles. Les cas où les services Google Play ne sont pas disponibles sont indiqués par une exception GooglePlayServicesNotAvailableException levée par getAdvertisingIdInfo ().

Attention, les utilisateurs peuvent réinitialiser:

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

J'ai essayé de référencer tous les liens dont j'ai tiré des informations. Si vous manquez et devez être inclus, veuillez commenter!

InstanceID des services Google Player

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


Mais la Buildclasse ne changerait-elle pas lors de la mise à jour du système d'exploitation? Surtout si l'API a été mise à jour? Si oui, comment garantissez-vous que ce soit unique? (En parlant de la méthode que vous avez écrite)
LuckyMe

2
j'ai utilisé votre méthode dans mon application pour envoyer des commentaires. J'ai de mauvaises nouvelles. malheureusement, PsuedoID n'est pas complètement unique. mon serveur a enregistré plus de 100 pour 5 ID et plus de 30 pour près de 30 ID. les identifiants les plus répétés sont «ffffffff-fc8f-6093-ffff-ffffd8» (159 enregistrements) et «ffffffff-fe99-b334-ffff-ffffef» (154 fois). également en fonction du temps et des commentaires, il est évident qu'il existe différents peuples. le nombre total d'enregistrements jusqu'à présent est de 10 000. s'il vous plaît laissez-moi savoir pourquoi cela s'est produit. réservoirs.
hojjat reyhane

1
J'ai écrit cela il y a plus de 1,5 ans. Je ne sais pas pourquoi ce n'est pas unique pour vous. Vous pouvez essayer l'ID publicitaire. Sinon, vous pouvez trouver votre propre solution.
Jared Burrows

2
sorta..J'apprécierais vraiment si vous passez par la question et donnez votre avis à ce sujet
Durai Amuthan.H

1
@ user1587329 Merci. J'essaie de garder cela à jour pour tout le monde. Cette question est délicate quand il s'agit de matériel vs logiciel et multiplateforme.
Jared Burrows

340

Comme le mentionne Dave Webb, le blog des développeurs Android contient un article qui couvre cela. Leur solution préférée est de suivre les installations d'applications plutôt que les appareils, et cela fonctionnera bien pour la plupart des cas d'utilisation. Le billet de blog vous montrera le code nécessaire pour que cela fonctionne, et je vous recommande de le vérifier.

Cependant, l'article de blog continue pour discuter des solutions si vous avez besoin d'un identifiant d'appareil plutôt que d'un identifiant d'installation d'application. J'ai parlé à quelqu'un chez Google pour obtenir des éclaircissements supplémentaires sur quelques éléments au cas où vous en auriez besoin. Voici ce que j'ai découvert à propos des identifiants d'appareil NON mentionnés dans le billet de blog susmentionné:

  • ANDROID_ID est l'identifiant d'appareil préféré. ANDROID_ID est parfaitement fiable sur les versions d'Android <= 2.1 ou> = 2.3. Seul 2.2 a les problèmes mentionnés dans le message.
  • Plusieurs appareils de plusieurs fabricants sont affectés par le bogue ANDROID_ID dans 2.2.
  • Pour autant que j'ai pu le déterminer, tous les appareils concernés ont le même ANDROID_ID , qui est 9774d56d682e549c . Qui est également le même identifiant de périphérique signalé par l'émulateur, btw.
  • Google estime que les OEM ont corrigé le problème pour la plupart ou la plupart de leurs appareils, mais j'ai pu vérifier qu'au début avril 2011, au moins, il est toujours assez facile de trouver des appareils qui ont le ANDROID_ID cassé.

Sur la base des recommandations de Google, j'ai implémenté une classe qui générera un UUID unique pour chaque appareil, en utilisant ANDROID_ID comme valeur de départ le cas échéant, en recourant à TelephonyManager.getDeviceId () si nécessaire, et si cela échoue, en recourant à un UUID unique généré de manière aléatoire qui persiste à travers les redémarrages des applications (mais pas les réinstallations d'applications).

Notez que pour les appareils qui doivent recourir à l'ID de l'appareil, l'ID unique SERA persistera dans les réinitialisations d'usine. C'est quelque chose dont il faut être conscient. Si vous devez vous assurer qu'une réinitialisation d'usine réinitialise votre ID unique, vous pouvez envisager de retomber directement sur l'UUID aléatoire au lieu de l'ID de l'appareil.

Encore une fois, ce code est pour un ID d'appareil, pas un ID d'installation d'application. Pour la plupart des situations, un ID d'installation d'application est probablement ce que vous recherchez. Mais si vous avez besoin d'un ID d'appareil, le code suivant fonctionnera probablement pour vous.

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
Ne devriez-vous pas hacher les différents identifiants pour qu'ils soient tous de la même taille? De plus, vous devez hacher l'ID de l'appareil afin de ne pas exposer accidentellement des informations privées.
Steve Pomeroy

2
De bons points, Steve. J'ai mis à jour le code pour toujours renvoyer un UUID. Cela garantit que a) les identifiants générés sont toujours de la même taille, et b) les identifiants Android et appareil sont hachés avant d'être retournés pour éviter d'exposer accidentellement des informations personnelles. J'ai également mis à jour la description pour noter que l'ID de l'appareil persistera lors des réinitialisations d'usine et que cela peut ne pas être souhaitable pour certains utilisateurs.
emmby

1
Je pense que vous vous trompez; la solution préférée consiste à suivre les installations et non les identifiants des appareils. Votre code est sensiblement plus long et plus complexe que celui du billet de blog et il n'est pas évident pour moi qu'il ajoute une valeur.
Tim Bray

7
Bon point, j'ai mis à jour le commentaire pour suggérer fortement aux utilisateurs d'utiliser des identifiants d'installation d'application plutôt que des identifiants d'appareil. Cependant, je pense que cette solution est toujours valable pour les personnes qui ont besoin d'un appareil plutôt que d'un ID d'installation.
emmby

8
ANDROID_ID peut changer lors de la réinitialisation d'usine, il ne peut donc pas identifier les périphériques également
Samuel

180

Voici le code que Reto Meier a utilisé dans la présentation Google I / O cette année pour obtenir un identifiant unique pour l'utilisateur:

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

Si vous associez cela à une stratégie de sauvegarde pour envoyer des préférences au cloud (également décrit dans l' exposé de Reto , vous devriez avoir un identifiant qui se lie à un utilisateur et reste après que l'appareil a été effacé, ou même remplacé. Je prévois d'utiliser cela dans l'analyse à l'avenir (en d'autres termes, je ne l'ai pas encore fait :).


J'utilisais la méthode de @Lenn Dolling avec l'heure actuelle ajoutée pour l'identifiant unique. Mais cela semble être un moyen plus simple et plus fiable. Merci Reto Meier et Antony Nolan
Gökhan Barış Aker

C'est super mais qu'en est-il des appareils rootés? Ils peuvent y accéder et changer facilement l'uid en un autre.
tasomane

3
Excellente option si vous n'avez pas besoin que l'identifiant unique persiste après une désinstallation et une réinstallation (par exemple, un événement / jeu promotionnel où vous avez trois chances de gagner, point final).
Kyle Clegg

2
La présentation Meier repose sur l'utilisation du gestionnaire de sauvegarde Android, qui à son tour dépend du choix de l'utilisateur d'activer cette fonctionnalité. C'est très bien pour les préférences utilisateur de l'application (utilisation de Meier), car si l'utilisateur n'a pas sélectionné cette option, il ne les récupérera tout simplement pas. Cependant, la question d'origine concerne la génération d'un ID unique pour l' appareil , et cet ID est généré par application, et même pas par installation, encore moins par appareil, et puisqu'il dépend de la sélection de l'option de sauvegarde par l'utilisateur, de ses utilisations au-delà de l'utilisateur les préférences (par exemple pour un essai limité dans le temps) sont limitées.
Carl

12
Cela ne fonctionnera pas sur une désinstallation ou un effacement des données.
John Shelley

106

Vous pouvez également considérer l'adresse MAC de l'adaptateur Wi-Fi. Récupéré comme ceci:

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

Nécessite une autorisation android.permission.ACCESS_WIFI_STATE dans le manifeste.

Signalé comme étant disponible même lorsque le Wi-Fi n'est pas connecté. Si Joe de la réponse ci-dessus essaye celui-ci sur ses nombreux appareils, ce serait bien.

Sur certains appareils, il n'est pas disponible lorsque le Wi-Fi est désactivé.

REMARQUE: à partir d'Android 6.x, il renvoie une fausse adresse mac cohérente:02:00:00:00:00:00


8
Cela a exigéandroid.permission.ACCESS_WIFI_STATE
ohhorob

5
Je pense que vous constaterez qu'il n'est pas disponible lorsque le WiFi est désactivé, sur à peu près tous les appareils Android. La désactivation du WiFi supprime l'appareil au niveau du noyau.
chrisdowney

12
@Sanandrea - avouons-le, sur un appareil rooté TOUT peut être usurpé.
ocodo

5
L'accès à l'adresse MAC WiFi a été bloqué sur Android M: stackoverflow.com/questions/31329733/…
breez

6
À partir d'Android 6.x, il renvoie une fausse adresse mac cohérente:02:00:00:00:00:00
Behrouz.M

87

Il y a des informations plutôt utiles ici .

Il couvre cinq types d'ID différents:

  1. IMEI (uniquement pour les appareils Android avec utilisation du téléphone; besoins android.permission.READ_PHONE_STATE)
  2. ID pseudo-unique (pour tous les appareils Android)
  3. ID Android (peut être nul, peut changer lors de la réinitialisation d'usine, peut être modifié sur un téléphone rooté)
  4. Chaîne d' adresse MAC WLAN (besoins android.permission.ACCESS_WIFI_STATE)
  5. Chaîne d' adresse MAC BT (appareils avec Bluetooth, besoins android.permission.BLUETOOTH)

2
Point important omis (ici et dans l'article): vous ne pouvez pas obtenir WLAN ou BT MAC à moins qu'ils ne soient allumés! Sinon, je pense que le WLAN MAC serait l'identifiant parfait. Vous n'avez aucune garantie que l'utilisateur allumera jamais son Wi-Fi, et je ne pense pas vraiment qu'il soit «approprié» de l'allumer vous-même.
Tom

1
@Tom tu as tort. Vous pouvez toujours lire WLAN ou BT MAC même lorsqu'ils sont éteints. Cependant, rien ne garantit que l'appareil dispose de modules WLAN ou BT.
2014

2
Plus particulièrement, les adresses MAC locales WiFi et Bluetooth ne sont plus disponibles.La méthode getMacAddress () d'un objet WifiInfo et la méthode BluetoothAdapter.getDefaultAdapter (). GetAddress () renverront désormais toutes les deux 02: 00: 00: 00: 00: 00: 00 à partir de maintenant
sarika kate

4
@sarikakate Ce n'est vrai que dans 6.0 Marshmallow et au-dessus ... Il fonctionne toujours comme prévu dans ci-dessous 6.0 Marshmallow.
Smeet

@Smeet Oui, vous avez raison, j'ai oublié de mentionner que son fonctionnement est inférieur à 6.0
sarika kate

51

Le blog officiel des développeurs Android a maintenant un article complet sur ce même sujet, Identifier les installations d'applications .


4
Et le point clé de cet argument est que si vous essayez d'obtenir un ID unique du matériel, vous faites probablement une erreur.
Tim Bray

3
Et si vous autorisez la réinitialisation du verrouillage de votre appareil par une réinitialisation d'usine, votre modèle d'essai est presque mort.
Seva Alekseyev

43

Chez Google I / O Reto Meier a publié une réponse solide sur la façon d'aborder ce problème, qui devrait répondre à la plupart des besoins des développeurs pour suivre les utilisateurs sur les installations. Anthony Nolan montre la direction dans sa réponse, mais je pensais que j'écrirais l'approche complète afin que les autres puissent facilement voir comment le faire (il m'a fallu un certain temps pour comprendre les détails).

Cette approche vous donnera un ID utilisateur anonyme et sécurisé qui sera persistant pour l'utilisateur sur différents appareils (en fonction du compte Google principal) et sur toutes les installations. L'approche de base consiste à générer un ID utilisateur aléatoire et à le stocker dans les préférences partagées des applications. Vous utilisez ensuite l'agent de sauvegarde de Google pour stocker les préférences partagées liées au compte Google dans le cloud.

Passons en revue l'approche complète. Tout d'abord, nous devons créer une sauvegarde pour nos SharedPreferences à l'aide du service de sauvegarde Android. Commencez par enregistrer votre application viahttp://developer.android.com/google/backup/signup.html .

Google vous fournira une clé de service de sauvegarde que vous devez ajouter au manifeste. Vous devez également indiquer à l'application d'utiliser le BackupAgent comme suit:

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

Ensuite, vous devez créer l'agent de sauvegarde et lui dire d'utiliser l'agent d'assistance pour les préférences partagées:

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

Pour terminer la sauvegarde, vous devez créer une instance de BackupManager dans votre activité principale:

BackupManager backupManager = new BackupManager(context);

Enfin, créez un ID utilisateur, s'il n'existe pas déjà, et stockez-le dans 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;
}

Cet ID_utilisateur sera désormais persistant dans toutes les installations, même si l'utilisateur déplace l'appareil.

Pour plus d'informations sur cette approche, voir le discours de Reto .

Et pour plus de détails sur la façon d'implémenter l'agent de sauvegarde, voir Sauvegarde des données . Je recommande particulièrement la section en bas sur les tests car la sauvegarde ne se fait pas instantanément et donc pour tester, vous devez forcer la sauvegarde.


5
Cela ne conduit-il pas à plusieurs appareils avec le même identifiant lorsqu'un utilisateur a plusieurs appareils? Une tablette et un téléphone par exemple.
Tosa

Un objectif minimum de 8 est requis pour cela.
halxinate le

Serait-ce le moyen préféré de créer une charge utile de vérification lors des achats intégrés? D'après l'exemple de code de facturation intégré à l'application: "Ainsi, une bonne charge utile de développeur présente les caractéristiques suivantes: 1. Si deux utilisateurs différents achètent un article, la charge utile est différente entre eux, de sorte que l'achat d'un utilisateur ne peut pas être rejoué à un autre utilisateur. 2. La charge utile doit être telle que vous puissiez la vérifier même lorsque l'application n'est pas celle qui a initié le flux d'achat (pour que les articles achetés par l'utilisateur sur un appareil fonctionnent sur d'autres appareils appartenant à l'utilisateur). "
TouchBoarder

@Tosa J'avais juste la même question. Mais ne pourrions-nous pas utiliser à nouveau cette même technique pour créer des identifiants de périphérique virtuel et ne pas simplement sauvegarder cela de la même manière? L'identifiant de l'appareil ne persisterait pas après un effacement ou une réinstallation, mais si nous avons un identifiant d'utilisateur persistant, nous n'aurons peut-être pas besoin de beaucoup à partir d'un identifiant de périphérique.
jwehrle

39

Je pense que c'est un moyen sûr de construire un squelette pour une identification unique ... vérifiez-le.

ID pseudo-unique, qui fonctionne sur tous les appareils Android Certains appareils n'ont pas de téléphone (par exemple, des tablettes) ou pour une raison quelconque, vous ne voulez pas inclure l'autorisation READ_PHONE_STATE. Vous pouvez toujours lire des détails tels que la version de la ROM, le nom du fabricant, le type de processeur et d'autres détails matériels, qui seront bien adaptés si vous souhaitez utiliser l'ID pour une vérification de clé de série ou à d'autres fins générales. L'ID ainsi calculé ne sera pas unique: il est possible de trouver deux appareils avec le même ID (basé sur le même matériel et la même image ROM) mais les changements dans les applications réelles sont négligeables. Pour cela, vous pouvez utiliser 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 plupart des membres de Build sont des chaînes, ce que nous faisons ici est de prendre leur longueur et de la transformer via modulo en un chiffre. Nous avons 13 de ces chiffres et nous en ajoutons deux de plus à l'avant (35) pour avoir le même ID de taille que l'IMEI (15 chiffres). Il existe d'autres possibilités ici sont bien, jetez un oeil à ces chaînes. Renvoie quelque chose comme 355715565309247. Aucune autorisation spéciale n'est requise, ce qui rend cette approche très pratique.


(Information supplémentaire: la technique indiquée ci-dessus a été copiée d'un article sur Pocket Magic .)


7
Solution intéressante. Il semble que ce soit une situation où vous devriez vraiment hacher toutes ces données concaténées au lieu d'essayer de créer votre propre fonction de «hachage». Il existe de nombreux cas où vous obtiendriez des collisions même s'il existe des données substantielles différentes pour chaque valeur. Ma recommandation: utilisez une fonction de hachage, puis transformez les résultats binaires en décimaux et tronquez-les si nécessaire. Pour le faire correctement, vous devez vraiment utiliser un UUID ou une chaîne de hachage complète.
Steve Pomeroy

21
Vous devriez donner du crédit à vos sources ... Cela a été retiré directement de l'article suivant: pocketmagic.net/?p=1662
Steve Haley

8
Cet ID est ouvert aux collisions comme si vous ne savez pas quoi. Il est pratiquement garanti d'être le même sur des appareils identiques provenant du même opérateur.
Seva Alekseyev

7
Cela peut également changer si l'appareil est mis à niveau.
David Given

8
Très, très mauvaise solution. Testé sur deux Nexus 5 ... Renvoyez les mêmes numéros.
Sinan Dizdarević

38

Le code suivant renvoie le numéro de série de l'appareil à l'aide d'une API Android masquée. Mais, ce code ne fonctionne pas sur Samsung Galaxy Tab car "ro.serialno" n'est pas défini sur cet appareil.

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

}

Je viens de lire sur le développeur xda que le ro.serialnoest utilisé pour générer le Settings.Secure.ANDROID_ID. Ce sont donc essentiellement des représentations différentes de la même valeur.
Martin

@Martin: mais le numéro de série ne change probablement pas lors de la réinitialisation de l'appareil. N'est-ce pas? Juste une nouvelle valeur pour en ANDROID_IDdérive.
Ronnie

En fait, sur tous les appareils que j'ai testés, ils étaient identiques. Ou au moins les valeurs de hachage où les mêmes (pour des raisons de confidentialité, je n'écris pas les vraies valeurs dans les fichiers journaux).
Martin

Cette valeur est la même queandroid.os.Build.SERIAL
eugeneek

android.os.Build.SERIALsera déconseillé dans Android O, voir android-developers.googleblog.com/2017/04/…
EpicPandaForce

32

C'est une question simple, sans réponse simple.

De plus, toutes les réponses existantes ici sont obsolètes ou peu fiables.

Donc, si vous cherchez une solution en 2020 .

Voici quelques éléments à prendre en compte:

Tous les identifiants basés sur le matériel (SSAID, IMEI, MAC, etc.) ne sont pas fiables pour les appareils autres que Google (tout sauf les pixels et les Nexus), qui représentent plus de 50% des appareils actifs dans le monde. Par conséquent, les meilleures pratiques des identifiants Android officiels indiquent clairement:

Évitez d'utiliser des identifiants matériels , tels que SSAID (ID Android), IMEI, adresse MAC, etc ...

Cela rend la plupart des réponses ci-dessus invalides. En raison également de différentes mises à jour de sécurité Android, certaines d'entre elles nécessitent des autorisations d'exécution plus récentes et plus strictes, qui peuvent être simplement refusées par l'utilisateur.

Comme exemple CVE-2018-9489 qui affecte toutes les techniques basées sur WIFI mentionnées ci-dessus.

Cela rend ces identifiants non seulement peu fiables, mais aussi inaccessibles dans de nombreux cas.

Donc, en termes plus simples: n'utilisez pas ces techniques .

De nombreuses autres réponses suggèrent d'utiliser le AdvertisingIdClient, qui est également incompatible, car sa conception devrait être utilisée uniquement pour le profilage des annonces. Il est également indiqué dans la référence officielle

Utilisez uniquement un identifiant publicitaire pour le profilage des utilisateurs ou les cas d'utilisation des annonces

Il est non seulement peu fiable pour l'identification de l'appareil, mais vous devez également respecter la confidentialité des utilisateurs concernant le suivi des annonces politique de , qui stipule clairement que l'utilisateur peut la réinitialiser ou la bloquer à tout moment.

Alors ne l'utilisez pas non plus .

Étant donné que vous ne pouvez pas disposer de l'identifiant d'appareil statique globalement unique et fiable souhaité. La référence officielle d'Android suggère:

Utilisez autant que possible un FirebaseInstanceId ou un GUID stocké en privé pour tous les autres cas d'utilisation, à l'exception de la prévention des fraudes de paiement et de la téléphonie.

C'est unique pour l'installation de l'application sur l'appareil, donc lorsque l'utilisateur désinstalle l'application - elle est effacée, donc ce n'est pas fiable à 100%, mais c'est la meilleure chose à faire.

Pour utiliser, FirebaseInstanceIdajoutez la dernière dépendance de messagerie Firebase dans votre gradle

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

Et utilisez le code ci-dessous dans un fil d'arrière-plan:

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

Si vous devez stocker l'identification de l'appareil sur votre serveur distant, ne la stockez pas telle quelle (texte brut), mais un hachage avec du sel .

Aujourd'hui, ce n'est pas seulement une meilleure pratique, vous devez en fait le faire conformément à la loi conformément au RGPD - identificateurs et réglementations similaires.


3
Pour l'instant, c'est la meilleure réponse, et la première phrase est le meilleur résumé: "C'est une question simple, sans réponse simple
adorez

Vous devez apprécier le commentaire «sauf pour la prévention des fraudes de paiement et la téléphonie» sans réponse sur la façon de traiter ce cas d'utilisation.
Eran Boudjnah

@EranBoudjnah Cette citation de la référence officielle, qui est liée dans la réponse. Je peux essayer de résoudre ce cas d'utilisation, mais comme ce n'est pas spécifique à la question OP, selon la politique de stackoverflow - cela devrait être fait dans une question dédiée distincte.
Nikita Kurtin

Je dis simplement que cette réponse est incomplète. Mais je suis conscient que ce n'est pas de votre faute car c'est une citation.
Eran Boudjnah

1
@ M.UsmanKhan, la réponse est écrite juste après cela: " Aujourd'hui, ce n'est pas seulement une meilleure pratique, vous devez en fait le faire conformément à la loi conformément au RGPD - identificateurs et réglementations similaires. "
Nikita Kurtin

27

En utilisant le code ci-dessous, vous pouvez obtenir l'ID d'appareil unique d'un appareil Android OS sous forme de chaîne.

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

21

Un champ Série a été ajouté auBuild classe dans l'API niveau 9 (Android 2.3 - Gingerbread). La documentation indique qu'il représente le numéro de série du matériel. Il doit donc être unique s'il existe sur l'appareil.

Je ne sais pas si elle est réellement prise en charge (= non nulle) par tous les appareils avec un niveau API> = 9.


2
Malheureusement, c'est "inconnu".
m0skit0

18

Une chose que j'ajouterai - j'ai une de ces situations uniques.

En utilisant:

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

Il s'avère que même si ma tablette Viewsonic G signale un DeviceID qui n'est pas nul, chaque tablette G rapporte le même nombre.

Il est intéressant de jouer à "Pocket Empires" qui vous donne un accès instantané au compte de quelqu'un en fonction du DeviceID "unique".

Mon appareil n'a pas de radio cellulaire.


Quel est l'identifiant? est-ce par hasard 9774d56d682e549c?
Mr_and_Mrs_D

Wow c'était il y a si longtemps que je lançais depuis longtemps cette tablette. Impossible de dire.
Tony Maro du

Travaux. En outre, est beaucoup plus compact que l'ID que j'obtiens de l'UUID aléatoire.
Treewallie

1
@Treewallie ça marche? Pouvez-vous obtenir le même ID d'appareil à partir de différentes applications?
Arnold Brown

@ArnoldBrown Oui. Assurez-vous de le tester à fond. Bonne journée: D
Treewallie

16

Pour obtenir des instructions détaillées sur la façon d'obtenir un identifiant unique pour chaque appareil Android à partir duquel votre application est installée, consultez la publication officielle du blog des développeurs Android Identification des installations d'applications .

Il semble que le meilleur moyen consiste à en générer un vous-même lors de l'installation et à le lire ensuite lorsque l'application est relancée.

Personnellement, je trouve cela acceptable mais pas idéal. Aucun identifiant fourni par Android ne fonctionne dans tous les cas, car la plupart dépendent des états de la radio du téléphone (Wi-Fi activé / désactivé, cellulaire activé / désactivé, Bluetooth activé / désactivé). Les autres, comme Settings.Secure.ANDROID_IDdoivent être mis en œuvre par le fabricant et ne sont pas garantis d'être uniques.

Voici un exemple d'écriture de données dans un fichier d' installation qui seraient stockées avec toutes les autres données que l'application enregistre localement.

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

Si vous souhaitez suivre les installations d'applications, c'est parfait. Le suivi des appareils est cependant beaucoup plus délicat, et il ne semble pas y avoir de solution complètement étanche.
Luca Spiller

Qu'en est-il des appareils rootés? Ils peuvent facilement changer cet identifiant d'installation, non?
tasomaniac

Absolument. Root peut changer l'ID d'installation. Vous pouvez vérifier la racine en utilisant ce bloc de code: stackoverflow.com/questions/1101380/…
Kevin Parker

si nous réinitialisons les paramètres d'usine, le fichier sera-t-il supprimé?
Jamshid

Si vous réinitialisez et supprimez ou formatez la partition / data, l'UUID est différent.
Kevin Parker

12

Ajouter le code ci-dessous dans le fichier de 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);

Ajoutez dans AndroidManifest.xml:

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

10

L'ID d'appareil unique d'un appareil Android OS en tant que chaîne, à l'aide de TelephonyManageret ANDROID_ID, est obtenu par:

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

Mais je recommande fortement une méthode suggérée par Google, voir Identification des installations d'applications .


9

Il existe de nombreuses approches différentes pour contourner ces ANDROID_IDproblèmes ( nullparfois, ou les appareils d'un modèle spécifique retournent toujours le même ID) avec des avantages et des inconvénients:

  • Implémentation d'un algorithme de génération d'ID personnalisé (basé sur des propriétés d'appareil qui sont censées être statiques et ne changeront pas -> qui sait)
  • Utilisation abusive d'autres identifiants comme IMEI , numéro de série, adresse Wi-Fi / Bluetooth-MAC (ils n'existeront pas sur tous les appareils ou des autorisations supplémentaires deviennent nécessaires)

Je préfère moi-même utiliser une implémentation OpenUDID existante (voir https://github.com/ylechelle/OpenUDID ) pour Android (voir https://github.com/vieux/OpenUDID ). Il est facile à intégrer et utilise les solutions de ANDROID_IDsecours pour les problèmes mentionnés ci-dessus.


8

Que diriez-vous de l' IMEI . C'est unique pour Android ou d'autres appareils mobiles.


9
Pas pour mes tablettes, qui n'ont pas d'IMEI car elles ne se connectent pas à mon opérateur mobile.
Brill Pappin

2
Sans parler des appareils CDMA qui ont un ESN au lieu d'un IMEI.
David Given

@David Compte tenu de l'existence de CDMA avec Android?
Elzo Valugi

1
Il suffit que ce soit un téléphone :) Une tablette peut ne pas.
Brill Pappin

3
@ElzoValugi C'est "de nos jours" déjà et toutes les tablettes n'ont pas encore de cartes SIM.
Matthew Quiros

8

Voici comment je génère l'identifiant unique:

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

si nous utilisons ReadPhoneState dans la version 6.0 pour demander l'autorisation d'exécution
Harsha

8

Mes deux cents - NB c'est pour un identifiant unique de périphérique (err) - pas celui d'installation comme discuté dans le blog des développeurs Android .

Il est à noter que la solution fournie par @emmby retombe dans un ID par application car les SharedPreferences ne sont pas synchronisées entre les processus (voir ici et ici ). J'ai donc complètement évité cela.

Au lieu de cela, j'ai encapsulé les différentes stratégies pour obtenir un ID (périphérique) dans une énumération - la modification de l'ordre des constantes d'énumération affecte la priorité des différentes façons d'obtenir l'ID. Le premier ID non nul est renvoyé ou une exception est levée (conformément aux bonnes pratiques Java de ne pas donner de sens à null). Par exemple, j'ai d'abord le TELEPHONY - mais un bon choix par défaut serait la bêta d' 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

Il y a plus de 30 réponses ici et certaines sont identiques et certaines sont uniques. Cette réponse est basée sur quelques-unes de ces réponses. L'un d'eux étant la réponse de @Lenn Dolling.

Il combine 3 ID et crée une chaîne hexadécimale à 32 chiffres. Cela a très bien fonctionné pour moi.

3 ID sont:
Pseudo-ID - Il est généré sur la base des spécifications de l'appareil physique
ANDROID_ID - Settings.Secure.ANDROID_ID
Adresse Bluetooth - Adresse de l' adaptateur Bluetooth

Il renverra quelque chose comme ceci: 551F27C060712A72730B0A0F734064B1

Remarque: vous pouvez toujours ajouter d'autres ID à la longIdchaîne. Par exemple, Serial #. adresse de l'adaptateur wifi. IMEI. De cette façon, vous le rendez plus unique par appareil.

@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
L'ajout de l' UUID au longIdet le stocker dans un fichier, en fera l'identifiant le plus unique:String uuid = UUID.randomUUID().toString();
Mousa Alfhaily

1
Si tout le reste échoue, si l'utilisateur a inférieur à API 9 (inférieur à Gingerbread), a réinitialisé son téléphone ou «Secure.ANDROID_ID». si retourne «null», alors simplement l'ID retourné sera uniquement basé sur les informations de leur appareil Android. C'est là que les collisions peuvent se produire. Essayez de ne pas utiliser DISPLAY, HOST ou ID - ces éléments peuvent changer. S'il y a des collisions, il y aura des données qui se chevauchent. La source: gist.github.com/pedja1/fe69e8a80ed505500caa
Mousa Alfhaily

Si nous essayons d'obtenir un numéro unique par cette ligne de code, pouvons-nous dire que c'est un identifiant unique et qu'il n'entrera jamais en conflit avec un autre appareil?
Ninja

1
@Ninja Étant donné que l'adresse mac BLE est unique, oui, l'ID généré sera toujours unique. Cependant, si vous voulez vraiment en être sûr, je vous suggère d'ajouter un UUID au longId. Modifiez cette ligne comme ceci: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();Cela garantit que l'ID généré sera unique.
ᴛʜᴇᴘᴀᴛᴇʟ

@ ᴛʜᴇᴘᴀᴛᴇʟ Merci beaucoup pour cette énorme aide. En fait, mon application a des données très sensibles, donc j'ai besoin de m'en assurer, c'est pourquoi je confirme simplement ces choses.
Ninja

7

Une autre façon est d'utiliser /sys/class/android_usb/android0/iSerialdans une application sans aucune autorisation.

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

Pour ce faire en Java, il suffit d'utiliser un FileInputStream pour ouvrir le fichier iSerial et lire les caractères. Assurez-vous simplement de l'envelopper dans un gestionnaire d'exceptions, car tous les périphériques n'ont pas ce fichier.

Au moins les périphériques suivants sont connus pour avoir ce fichier lisible dans le monde:

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

Vous pouvez également voir mon article de blog Fuite du numéro de série du matériel Android sur des applications non privilégiées où je discute des autres fichiers disponibles pour information.


Je viens de lire ton article de blog. Je crois que ce n'est pas unique: Build.SERIAL est également disponible sans aucune autorisation et est (en théorie) un numéro de série matériel unique.
Tom

1
Tu as raison. C'est juste une autre façon de suivre votre appareil, et comme vous l'avez dit, ces deux méthodes ne nécessitent aucune autorisation d'application.
insitusec

7

TelephonyManger.getDeviceId () Renvoie l'ID d'appareil unique, par exemple, l'IMEI pour GSM et le MEID ou ESN pour les téléphones CDMA.

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

Mais je recommande d'utiliser:

Settings.Secure.ANDROID_ID qui renvoie l'ID Android sous la forme d'une chaîne hexadécimale unique de 64 bits.

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

Parfois TelephonyManger.getDeviceId () retournera null, donc pour assurer un identifiant unique, vous utiliserez cette méthode:

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

J'ai récemment découvert qu'un appareil client de type SM-G928F / Galaxy S6 edge + ne fournit que 15 au lieu de 16 chiffres hexadécimaux pour l'ID Android.
Holger Jakobs

7

Pour la reconnaissance matérielle d'un appareil Android spécifique, vous pouvez vérifier les adresses MAC.

vous pouvez le faire de cette façon:

dans AndroidManifest.xml

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

maintenant dans votre code:

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

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

Dans chaque appareil Android, il s'agit au moins d'une interface "wlan0", la puce WI-FI. Ce code fonctionne même lorsque le WI-FI n'est pas activé.

PS Il y a un tas d'autres interfaces que vous obtiendrez dans la liste contenant MACS Mais cela peut changer entre les téléphones.


7

J'utilise le code suivant pour obtenir le IMEIou utiliser Secure. ANDROID_IDcomme alternative, lorsque l'appareil n'a pas de fonctionnalités téléphoniques:

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

Plus précisément, Settings.Secure.ANDROID_ID. Il s'agit d'une quantité 64 bits générée et stockée lors du premier démarrage de l'appareil. Il est réinitialisé lorsque l'appareil est essuyé.

ANDROID_IDsemble un bon choix pour un identifiant d'appareil unique. Il y a des inconvénients: tout d'abord, il n'est pas fiable à 100% sur les versions d'Android antérieures à la version 2.2.En (“Froyo”).outre, il y a eu au moins un bogue largement observé dans un combiné populaire d'un grand fabricant, où chaque instance a le même ANDROID_ID.


1
cette réponse est un copypaste de l'ancien blog google android-developers.googleblog.com/2011/03/… . Le bug est donc déjà résolu?
Sergii


6

ID d'instance Google

Sorti à I / O 2015; sur Android nécessite des services de jeu 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

Il semble que Google ait l'intention d'utiliser cet ID pour identifier les installations sur Android, Chrome et iOS.

Il identifie une installation plutôt qu'un périphérique, mais là encore, ANDROID_ID (qui est la réponse acceptée) n'identifie plus les périphériques non plus. Avec le runtime ARC, un nouvel ANDROID_ID est généré pour chaque installation ( détails ici ), tout comme ce nouvel ID d'instance. De plus, je pense que l'identification des installations (et non des appareils) est ce que la plupart d'entre nous recherchent réellement.

Les avantages de l'ID d'instance

Il me semble que Google a l'intention de l'utiliser à cette fin (identification de vos installations), il est multiplateforme et peut être utilisé à plusieurs autres fins (voir les liens ci-dessus).

Si vous utilisez GCM, vous devrez éventuellement utiliser cet ID d'instance car vous en aurez besoin pour obtenir le jeton GCM (qui remplace l'ancien ID d'enregistrement GCM).

Les inconvénients / problèmes

Dans l'implémentation actuelle (GPS 7.5), l'ID d'instance est récupéré d'un serveur lorsque votre application le demande. Cela signifie que l'appel ci-dessus est un appel bloquant - dans mes tests non scientifiques, cela prend 1 à 3 secondes si l'appareil est en ligne, et 0,5 à 1 seconde s'il est hors ligne (probablement le temps d'attente avant d'abandonner et de générer un ID aléatoire). Cela a été testé en Amérique du Nord sur Nexus 5 avec Android 5.1.1 et GPS 7.5.

Si vous utilisez l'ID aux fins prévues - par exemple. authentification de l'application, identification de l'application, GCM - Je pense que cette période de 1 à 3 secondes pourrait être gênante (selon votre application, bien sûr).


1
un autre inconvénient important de instanceID est qu'un nouvel instanceID sera généré pour vous si l'utilisateur efface les données de l'application.
idanakav

Intéressant, mais je ne pense pas que cela change vraiment les cas d'utilisation potentiels: l'ID d'instance, comme android_id, n'est pas adapté pour identifier un appareil. Ainsi, votre serveur verra les utilisateurs effacer les données comme si l'utilisateur désinstallait et réinstallait votre application - ce qui n'est pas déraisonnable.
Tom
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.