Les appareils Android ont-ils un ID unique et, dans l'affirmative, quel est un moyen simple d'y accéder en utilisant Java?
Les appareils Android ont-ils un ID unique et, dans l'affirmative, quel est un moyen simple d'y accéder en utilisant Java?
Réponses:
Settings.Secure#ANDROID_ID
renvoie 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);
MISE À JOUR : Depuis les versions récentes d'Android, de nombreux problèmes ANDROID_ID
ont é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é):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(comme prévu)ANDROID_ID
ANDROID_ID
et TelephonyManager.getDeviceId()
- tant que qu'un compte Google a été ajouté lors de la configuration.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 TelephonyManager
proprié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;
<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.
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.
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).
Courriel de l'utilisateur - Logiciel
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
ou<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
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - Matériel (uniquement téléphones, besoins android.permission.READ_PHONE_STATE
)
<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é)
Adresse MAC WLAN - Matériel (besoins android.permission.ACCESS_WIFI_STATE
)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Adresse MAC Bluetooth - Matériel (appareils avec Bluetooth, besoins android.permission.BLUETOOTH
)
<uses-permission android:name="android.permission.BLUETOOTH "/>
ID pseudo-unique - Logiciel (pour tous les appareils Android)
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.
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!
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 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:
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();
}
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
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 ().
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!
Build
classe 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)
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é:
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;
}
}
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 :).
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
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
Il y a des informations plutôt utiles ici .
Il couvre cinq types d'ID différents:
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)Le blog officiel des développeurs Android a maintenant un article complet sur ce même sujet, Identifier les installations d'applications .
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.
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 .)
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) {
}
ro.serialno
est utilisé pour générer le Settings.Secure.ANDROID_ID
. Ce sont donc essentiellement des représentations différentes de la même valeur.
ANDROID_ID
dérive.
android.os.Build.SERIAL
android.os.Build.SERIAL
sera déconseillé dans Android O, voir android-developers.googleblog.com/2017/04/…
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, FirebaseInstanceId
ajoutez 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.
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);
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.
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.
9774d56d682e549c
?
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_ID
doivent ê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();
}
}
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" />
TelephonyManager
et 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 .
Il existe de nombreuses approches différentes pour contourner ces ANDROID_ID
problèmes ( null
parfois, ou les appareils d'un modèle spécifique retournent toujours le même ID) avec des avantages et des inconvénients:
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_ID
secours pour les problèmes mentionnés ci-dessus.
Que diriez-vous de l' IMEI . C'est unique pour Android ou d'autres appareils mobiles.
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;
}
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);
}
}
}
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 longId
chaî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 "";
}
longId
et le stocker dans un fichier, en fera l'identifiant le plus unique:String uuid = UUID.randomUUID().toString();
longId
. Modifiez cette ligne comme ceci: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
Cela garantit que l'ID généré sera unique.
Une autre façon est d'utiliser /sys/class/android_usb/android0/iSerial
dans 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:
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.
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;
}
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.
J'utilise le code suivant pour obtenir le IMEI
ou utiliser Secure. ANDROID_ID
comme 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);
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_ID
semble 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.
Pour comprendre les identifiants uniques disponibles sur les appareils Android. Utilisez ce guide officiel.
Meilleures pratiques pour les identifiants uniques:
IMEI, adresses Mac, identifiant d'instance, GUID, SSAID, identifiant publicitaire, API Safety Net pour vérifier les appareils.
https://developer.android.com/training/articles/user-data-ids
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).
ANDROID_ID
assurez-vous de lire cette réponse et ce bogue .