Os dispositivos Android têm um ID exclusivo? Em caso afirmativo, qual é uma maneira simples de acessá-lo usando Java?
Os dispositivos Android têm um ID exclusivo? Em caso afirmativo, qual é uma maneira simples de acessá-lo usando Java?
Respostas:
Settings.Secure#ANDROID_ID
retorna o ID do Android como exclusivo para cada sequência hexadecimal de 64 bits do usuário .
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
ATUALIZAÇÃO : Nas versões recentes do Android, muitos dos problemas ANDROID_ID
foram resolvidos e acredito que essa abordagem não é mais necessária. Por favor, dê uma olhada na resposta de Anthony .
Divulgação completa: meu aplicativo usou a abordagem abaixo originalmente, mas não a usa mais, e agora usamos a abordagem descrita na entrada do Blog do desenvolvedor do Android à qual a resposta do empacotamento está vinculada (a saber, gerar e salvar a UUID#randomUUID()
).
Existem muitas respostas para essa pergunta, a maioria das quais só funciona "algumas" vezes e, infelizmente, isso não é bom o suficiente.
Com base nos meus testes de dispositivos (todos os telefones, pelo menos um dos quais não está ativado):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(conforme o esperado)ANDROID_ID
ANDROID_ID
e TelephonyManager.getDeviceId()
- desde que uma conta do Google tenha sido adicionada durante a instalação.Portanto, se você deseja algo único para o próprio dispositivo, TM.getDeviceId()
deve ser suficiente. Obviamente, alguns usuários são mais paranóicos que outros, portanto, pode ser útil usar o hash 1 ou mais desses identificadores, para que a string ainda seja praticamente exclusiva do dispositivo, mas não identifique explicitamente o dispositivo real do usuário. Por exemplo, usando String.hashCode()
, combinado com um 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();
pode resultar em algo como: 00000000-54b3-e7c7-0000-000046bffd97
Funciona bem o suficiente para mim.
Como Richard menciona abaixo, não esqueça que você precisa de permissão para ler as TelephonyManager
propriedades, então adicione isso ao seu manifesto:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
libs de importação
import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
ao arquivo de manifesto. Se estiver armazenando em um banco de dados, a sequência retornada terá 36 caracteres.
Depois de ler todas as postagens do Stack Overflow sobre a criação de um ID exclusivo, o blog do desenvolvedor do Google e a documentação do Android, sinto como se o 'Pseudo ID' fosse a melhor opção possível.
Código Psuedo:
if API >= 9/10: (99.5% of devices)
return unique ID containing serial id (rooted devices may be different)
else
return the unique ID of build information (may overlap data - API < 9)
Agradecemos a @stansult por postar todas as nossas opções (nesta questão do Stack Overflow).
E-mail do usuário - Software
<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" />
( Como obter o endereço de e-mail principal do dispositivo Android )Número de telefone do usuário - Software
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
IMEI - Hardware (apenas telefones, necessidades android.permission.READ_PHONE_STATE
)
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
ID do Android - Hardware (pode ser nulo, pode ser alterado após a redefinição de fábrica, pode ser alterado em um dispositivo raiz)
Endereço MAC da WLAN - Hardware (necessário android.permission.ACCESS_WIFI_STATE
)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Endereço MAC Bluetooth - Hardware (dispositivos com Bluetooth, necessidades android.permission.BLUETOOTH
)
<uses-permission android:name="android.permission.BLUETOOTH "/>
ID pseudo-exclusivo - software (para todos os dispositivos Android)
Sei que não existe uma maneira 'perfeita' de obter um ID exclusivo sem usar permissões; no entanto, às vezes precisamos apenas rastrear a instalação do dispositivo. Quando se trata de criar um ID exclusivo, podemos criar um 'pseudo id exclusivo' com base apenas nas informações que a API do Android nos fornece sem o uso de permissões extras. Dessa forma, podemos mostrar respeito ao usuário e tentar oferecer uma boa experiência ao usuário também.
Com um ID pseudo-exclusivo, você realmente só descobre que pode haver duplicatas com base no fato de que existem dispositivos semelhantes. Você pode ajustar o método combinado para torná-lo mais exclusivo; no entanto, alguns desenvolvedores precisam rastrear instalações de dispositivos e isso fará o truque ou o desempenho com base em dispositivos semelhantes.
Se o dispositivo Android for API 9 ou superior, é garantido que ele seja único devido ao campo 'Build.SERIAL'.
Lembre-se , tecnicamente, você está perdendo apenas cerca de 0,5% dos usuários que têm API <9 . Então você pode se concentrar no resto: são 99,5% dos usuários!
Se o dispositivo Android do usuário for menor que a API 9; esperançosamente, eles não fizeram uma redefinição de fábrica e seu 'Secure.ANDROID_ID' será preservado ou não será 'nulo'. (consulte http://developer.android.com/about/dashboards/index.html )
Se tudo mais falhar, se o usuário tiver uma API menor que a API 9 (menor que Gingerbread), redefinir o dispositivo ou 'Secure.ANDROID_ID' retornar 'nulo', simplesmente o ID retornado será baseado apenas nas informações do dispositivo Android. É aqui que as colisões podem acontecer.
Alterar:
Por favor, dê uma olhada no método abaixo:
/**
* 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();
}
No console do desenvolvedor do Google Play:
A partir de 1º de agosto de 2014, a Política do Programa para desenvolvedores do Google Play exige que todos os novos uploads e atualizações de aplicativos usem o ID da publicidade em vez de outros identificadores persistentes para fins de publicidade. Saber mais
Implementação :
Permissão:
<uses-permission android:name="android.permission.INTERNET" />
Código:
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();
}
Origem / Documentos:
http://developer.android.com/google/play-services/id.html http://developer.android.com/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.html
Pretende-se que o ID da publicidade substitua completamente o uso existente de outros identificadores para fins de anúncios (como o uso de ANDROID_ID em Settings.Secure) quando o Google Play Services estiver disponível. Os casos em que o Google Play Services não está disponível são indicados por uma exceção GooglePlayServicesNotAvailableException lançada por getAdvertisingIdInfo ().
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
Eu tentei fazer referência a todos os links dos quais tirei informações. Se você está faltando e precisa ser incluído, comente!
Build
classe não mudaria com a atualização do sistema operacional? Especialmente se a API foi atualizada? Se sim, como você garante que isso seja único? (Falando sobre o método que você escreveu)
Como Dave Webb menciona, o Android Developer Blog possui um artigo que aborda isso. Sua solução preferida é rastrear instalações de aplicativos em vez de dispositivos, e isso funcionará bem na maioria dos casos de uso. A postagem do blog mostrará o código necessário para fazer esse trabalho, e eu recomendo que você verifique.
No entanto, a postagem do blog continua discutindo soluções se você precisar de um identificador de dispositivo em vez de um identificador de instalação de aplicativo. Conversei com alguém no Google para obter esclarecimentos adicionais sobre alguns itens, caso você precise fazer isso. Aqui está o que eu descobri sobre identificadores de dispositivo que NÃO são mencionados na postagem de blog acima mencionada:
Com base nas recomendações do Google, implementei uma classe que geraria um UUID exclusivo para cada dispositivo, usando ANDROID_ID como a semente, quando apropriado, recorrendo ao TelephonyManager.getDeviceId () conforme necessário e, se isso falhar, recorrendo a um UUID exclusivo gerado aleatoriamente que persiste nas reinicializações do aplicativo (mas não nas reinstalações do aplicativo).
Note-se que para os dispositivos que têm de fallback na ID do dispositivo, a identificação única VAI persistem em redefinições de fábrica. Isso é algo para estar ciente. Se você precisar garantir que uma redefinição de fábrica redefina seu ID exclusivo, considere voltar diretamente ao UUID aleatório em vez do ID do dispositivo.
Novamente, esse código é para um ID de dispositivo, não para um ID de instalação de aplicativo. Na maioria das situações, provavelmente é o que você está procurando um ID de instalação do aplicativo. Mas se você precisar de um ID de dispositivo, o código a seguir provavelmente funcionará para você.
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;
}
}
Aqui está o código que Reto Meier usou na apresentação de E / S do Google este ano para obter um ID exclusivo para o usuário:
private static String uniqueID = null;
private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID";
public synchronized static String id(Context context) {
if (uniqueID == null) {
SharedPreferences sharedPrefs = context.getSharedPreferences(
PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
if (uniqueID == null) {
uniqueID = UUID.randomUUID().toString();
Editor editor = sharedPrefs.edit();
editor.putString(PREF_UNIQUE_ID, uniqueID);
editor.commit();
}
}
return uniqueID;
}
Se você associar isso a uma estratégia de backup para enviar preferências para a nuvem (também descrita na palestra de Reto , você deve ter um ID vinculado a um usuário e permanecer após o dispositivo ser limpo ou até substituído. Pretendo usar isso nas análises daqui para frente (em outras palavras, ainda não fiz isso :):
Além disso, você pode considerar o endereço MAC do adaptador Wi-Fi. Recuperado assim:
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
Requer permissão android.permission.ACCESS_WIFI_STATE
no manifesto.
É relatado que está disponível mesmo quando o Wi-Fi não está conectado. Se Joe, da resposta acima, tentar este em seus vários dispositivos, seria ótimo.
Em alguns dispositivos, ele não está disponível quando o Wi-Fi está desativado.
NOTA: No Android 6.x, ele retorna um endereço mac falso consistente:02:00:00:00:00:00
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
Há informações bastante úteis aqui .
Abrange cinco tipos diferentes de ID:
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)O Blog oficial do Android Developers agora tem um artigo completo sobre esse assunto, Identificando instalações de aplicativos .
No Google I / O Reto Meier lançou uma resposta robusta sobre como abordar isso, que deve atender às necessidades da maioria dos desenvolvedores para rastrear os usuários nas instalações. Anthony Nolan mostra a direção em sua resposta, mas pensei em escrever a abordagem completa para que outros possam ver facilmente como fazer isso (demorei um pouco para descobrir os detalhes).
Essa abordagem fornecerá um ID de usuário seguro e anônimo, que será persistente para o usuário em diferentes dispositivos (com base na conta principal do Google) e em instalações. A abordagem básica é gerar um ID de usuário aleatório e armazená-lo nas preferências compartilhadas dos aplicativos. Em seguida, você usa o agente de backup do Google para armazenar as preferências compartilhadas vinculadas à conta do Google na nuvem.
Vamos passar pela abordagem completa. Primeiro, precisamos criar um backup para nossas SharedPreferences usando o Serviço de Backup do Android. Comece registrando seu aplicativo via http://developer.android.com/google/backup/signup.html
.
O Google fornecerá uma chave de serviço de backup que você precisará adicionar ao manifesto. Você também precisa informar ao aplicativo para usar o BackupAgent da seguinte maneira:
<application android:label="MyApplication"
android:backupAgent="MyBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key"
android:value="your_backup_service_key" />
</application>
Em seguida, você precisa criar o agente de backup e solicitar que ele use o agente auxiliar para preferências compartilhadas:
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);
}
}
Para concluir o backup, você precisa criar uma instância do BackupManager em sua Atividade principal:
BackupManager backupManager = new BackupManager(context);
Por fim, crie um ID do usuário, se ele ainda não existir, e armazene-o nas 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;
}
Este User_ID agora será persistente nas instalações, mesmo se o usuário mover o dispositivo.
Para mais informações sobre essa abordagem, consulte a palestra de Reto .
E para obter detalhes completos de como implementar o agente de backup, consulte Backup de dados . Eu particularmente recomendo a seção na parte inferior do teste, pois o backup não ocorre instantaneamente e, para testar, você precisa forçar o backup.
Eu acho que essa é a maneira certa de construir um esqueleto para uma identificação única ... confira.
ID pseudo-exclusivo, que funciona em todos os dispositivos Android Alguns dispositivos não têm telefone (por exemplo, tablets) ou, por algum motivo, não deseja incluir a permissão READ_PHONE_STATE. Ainda é possível ler detalhes como versão da ROM, nome do fabricante, tipo de CPU e outros detalhes de hardware, que serão adequados se você desejar usar o ID para uma verificação de chave serial ou outros propósitos gerais. O ID calculado dessa maneira não será exclusivo: é possível encontrar dois dispositivos com o mesmo ID (com base no mesmo hardware e na imagem da ROM), mas as alterações nos aplicativos do mundo real são desprezíveis. Para esse fim, você pode usar a 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
A maioria dos membros do Build são strings, o que estamos fazendo aqui é medir o comprimento e transformá-lo via módulo em um dígito. Temos 13 dígitos e estamos adicionando mais dois na frente (35) para ter o mesmo tamanho de ID que o IMEI (15 dígitos). Existem outras possibilidades aqui estão bem, basta dar uma olhada nessas seqüências de caracteres. Retorna algo como 355715565309247
. Nenhuma permissão especial é necessária, tornando essa abordagem muito conveniente.
(Informação extra: A técnica fornecida acima foi copiada de um artigo no Pocket Magic .)
O código a seguir retorna o número de série do dispositivo usando uma API Android oculta. Mas, esse código não funciona no Samsung Galaxy Tab porque "ro.serialno" não está definido neste dispositivo.
String serial = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
}
catch (Exception ignored) {
}
ro.serialno
é usado para gerar o arquivo Settings.Secure.ANDROID_ID
. Portanto, são basicamente representações diferentes do mesmo valor.
ANDROID_ID
é derivado dele.
android.os.Build.SERIAL
android.os.Build.SERIAL
será descontinuado no Android O, consulte android-developers.googleblog.com/2017/04/…
É uma pergunta simples, sem resposta simples.
Além disso, todas as respostas existentes aqui são desatualizadas ou não confiáveis.
Portanto, se você estiver procurando uma solução em 2020 .
Aqui estão algumas coisas a ter em mente:
Todos os identificadores baseados em hardware (SSAID, IMEI, MAC etc.) não são confiáveis para dispositivos que não são do Google (Tudo, exceto Pixels e Nexuses), que são mais de 50% dos dispositivos ativos em todo o mundo. Portanto, as práticas recomendadas para identificadores oficiais do Android declaram claramente:
Evite usar identificadores de hardware , como SSAID (Android ID), IMEI, endereço MAC, etc ...
Isso torna a maioria das respostas acima inválidas. Também devido a diferentes atualizações de segurança do Android, algumas delas exigem permissões de tempo de execução mais novas e mais rigorosas, que podem ser simplesmente negadas pelo usuário.
Como exemplo CVE-2018-9489
que afeta todas as técnicas baseadas em WIFI mencionadas acima.
Isso torna esses identificadores não apenas não confiáveis, mas também inacessíveis em muitos casos.
Então, em palavras mais simples: não use essas técnicas .
Muitas outras respostas aqui estão sugerindo o uso de AdvertisingIdClient
, o que também é incompatível, pois seu design deve ser usado apenas para criação de perfil de anúncios. Também é indicado na referência oficial
Use apenas um ID de publicidade para criação de perfil de usuário ou casos de uso de anúncios
Não é apenas confiável para a identificação do dispositivo, mas você também deve seguir a privacidade do usuário em relação ao rastreamento de anúncios política de , que afirma claramente que o usuário pode redefini-lo ou bloqueá-lo a qualquer momento.
Portanto, também não use .
Como você não pode ter o identificador de dispositivo estático e exclusivo globalmente estático desejado. A referência oficial do Android sugere:
Use um FirebaseInstanceId ou um GUID armazenado em particular sempre que possível para todos os outros casos de uso, exceto para prevenção de fraude de pagamento e telefonia.
É exclusivo para a instalação do aplicativo no dispositivo; portanto, quando o usuário desinstala o aplicativo, ele é eliminado e, portanto, não é 100% confiável, mas é a próxima melhor coisa.
Para usar, FirebaseInstanceId
adicione a dependência mais recente de mensagens do firebase ao seu gradle
implementation 'com.google.firebase:firebase-messaging:20.2.0'
E use o código abaixo em um thread de segundo plano:
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
Se você precisar armazenar a identificação do dispositivo no servidor remoto, não a armazene como está (texto sem formatação), mas com um hash com sal .
Hoje, não é apenas uma prática recomendada, você deve fazê-lo por lei, de acordo com o GDPR - identificadores e regulamentos semelhantes.
Usando o código abaixo, você pode obter o ID exclusivo de um dispositivo Android OS como uma string.
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Um campo Serial foi adicionado aoBuild
classe no nível 9 da API (Android 2.3 - Gingerbread). A documentação diz que representa o número de série do hardware. Portanto, deve ser exclusivo, se existir no dispositivo.
Não sei se ele é realmente suportado (= não nulo) por todos os dispositivos com nível de API> = 9.
Uma coisa que vou acrescentar - eu tenho uma dessas situações únicas.
Usando:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Acontece que, embora meu Viewsonic G Tablet reporte um DeviceID que não seja Nulo, cada G Tablet reporta o mesmo número.
Torna interessante jogar "Pocket Empires", que fornece acesso instantâneo à conta de alguém com base no DeviceID "único".
Meu dispositivo não possui um rádio celular.
9774d56d682e549c
?
Para obter instruções detalhadas sobre como obter um identificador exclusivo para cada dispositivo Android do qual seu aplicativo está instalado, consulte o Blog oficial do Android Developers, postando a identificação de instalações de aplicativos .
Parece que a melhor maneira é você gerar um você mesmo durante a instalação e depois lê-lo quando o aplicativo for reiniciado.
Pessoalmente, acho isso aceitável, mas não ideal. Nenhum identificador fornecido pelo Android funciona em todas as instâncias, pois a maioria depende dos estados de rádio do telefone (Wi-Fi ativado / desativado, celular ativado / desativado, Bluetooth ativado / desativado). Os outros, como Settings.Secure.ANDROID_ID
devem ser implementados pelo fabricante e não têm garantia de serem exclusivos.
A seguir, é apresentado um exemplo de gravação de dados em um arquivo de instalação que seria armazenado juntamente com outros dados que o aplicativo salva localmente.
public class Installation {
private static String sID = null;
private static final String INSTALLATION = "INSTALLATION";
public synchronized static String id(Context context) {
if (sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists())
writeInstallationFile(installation);
sID = readInstallationFile(installation);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
return sID;
}
private static String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}
private static void writeInstallationFile(File installation) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
}
Adicione código abaixo no arquivo 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);
Adicione AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
TelephonyManager
e ANDROID_ID
, é obtido por: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);
}
Mas recomendo fortemente um método sugerido pelo Google, consulte Identificando instalações de aplicativos .
Existem várias abordagens diferentes para solucionar esses ANDROID_ID
problemas ( null
às vezes, ou dispositivos de um modelo específico sempre retornam o mesmo ID) com prós e contras:
Eu próprio prefiro usar uma implementação OpenUDID existente (consulte https://github.com/ylechelle/OpenUDID ) para Android (consulte https://github.com/vieux/OpenUDID ). É fácil de integrar e utiliza o ANDROID_ID
recurso com fallbacks para os problemas mencionados acima.
E o IMEI ? Isso é exclusivo para Android ou outros dispositivos móveis.
Aqui está como eu estou gerando o ID exclusivo:
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;
}
Meus dois centavos - NB, isto é para um ID exclusivo de dispositivo (err) - não para a instalação, conforme discutido no blog dos desenvolvedores do Android .
Observe que a solução fornecida pelo @emmby recai em um ID por aplicativo, pois as SharedPreferences não são sincronizadas entre os processos (veja aqui e aqui ). Então eu evitei isso completamente.
Em vez disso, encapsulei as várias estratégias para obter um ID (dispositivo) em uma enumeração - alterar a ordem das constantes da enumeração afeta a prioridade das várias maneiras de obter a identificação. O primeiro ID não nulo é retornado ou uma exceção é lançada (conforme as boas práticas Java de não dar um significado nulo). Por exemplo, eu tenho o TELEPHONY primeiro - mas uma boa opção padrão seria a versão beta do 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);
}
}
}
Existem mais de 30 respostas aqui e algumas são iguais e outras são únicas. Esta resposta é baseada em algumas dessas respostas. Um deles é a resposta de @Lenn Dolling.
Ele combina três IDs e cria uma cadeia hexadecimal de 32 dígitos. Funcionou muito bem para mim.
3 IDs são:
Pseudo-ID - É gerado com base nas especificações físicas do dispositivo
ANDROID_ID - Settings.Secure.ANDROID_ID
Endereço Bluetooth - Endereço do adaptador Bluetooth
Ele retornará algo como isto: 551F27C060712A72730B0A0F734064B1
Nota: Você sempre pode adicionar mais IDs à longId
sequência. Por exemplo, número de série. endereço do adaptador wifi. IMEI. Dessa forma, você o torna mais exclusivo por dispositivo.
@SuppressWarnings("deprecation")
@SuppressLint("HardwareIds")
public static String generateDeviceIdentifier(Context context) {
String pseudoId = "35" +
Build.BOARD.length() % 10 +
Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 +
Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 +
Build.HOST.length() % 10 +
Build.ID.length() % 10 +
Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 +
Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 +
Build.TYPE.length() % 10 +
Build.USER.length() % 10;
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String btId = "";
if (bluetoothAdapter != null) {
btId = bluetoothAdapter.getAddress();
}
String longId = pseudoId + androidId + btId;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(longId.getBytes(), 0, longId.length());
// get md5 bytes
byte md5Bytes[] = messageDigest.digest();
// creating a hex string
String identifier = "";
for (byte md5Byte : md5Bytes) {
int b = (0xFF & md5Byte);
// if it is a single digit, make sure it have 0 in front (proper padding)
if (b <= 0xF) {
identifier += "0";
}
// add number to string
identifier += Integer.toHexString(b);
}
// hex string to uppercase
identifier = identifier.toUpperCase();
return identifier;
} catch (Exception e) {
Log.e("TAG", e.toString());
}
return "";
}
longId
e armazená-lo em um arquivo, irá torná-lo o identificador mais exclusivo:String uuid = UUID.randomUUID().toString();
longId
. Altere essa linha como esta: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
Isso garante que o ID gerado seja exclusivo.
Outra maneira é usar /sys/class/android_usb/android0/iSerial
em um aplicativo sem nenhuma permissão.
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
Para fazer isso em Java, basta usar um FileInputStream para abrir o arquivo iSerial e ler os caracteres. Apenas certifique-se de envolvê-lo em um manipulador de exceções, porque nem todos os dispositivos possuem esse arquivo.
Sabe-se que pelo menos os seguintes dispositivos têm esse arquivo legível por todo o mundo:
Você também pode ver minha postagem no blog Vazando o número de série do hardware Android para aplicativos não privilegiados, onde discuto quais outros arquivos estão disponíveis para obter informações.
TelephonyManger.getDeviceId () Retorna o ID do dispositivo exclusivo, por exemplo, o IMEI para GSM e o MEID ou ESN para telefones CDMA.
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
Mas eu recomendo usar:
Settings.Secure.ANDROID_ID que retorna o ID do Android como uma sequência hexadecimal exclusiva de 64 bits.
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Às vezes, TelephonyManger.getDeviceId () retornará nulo; portanto, para garantir um ID exclusivo, você utilizará este método:
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;
}
Para reconhecimento de hardware de um dispositivo Android específico, você pode verificar os endereços MAC.
você pode fazer assim:
no AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
agora no seu código:
List<NetworkInterface> interfacesList = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface interface : interfacesList) {
// This will give you the interface MAC ADDRESS
interface.getHardwareAddress();
}
Em todos os dispositivos Android, há pelo menos uma interface "wlan0", que é o chip WI-FI. Esse código funciona mesmo quando o WI-FI não está ativado.
PS Existem várias outras interfaces que você obterá na lista que contém o MACS. Mas isso pode mudar entre os telefones.
Eu uso o código a seguir para obter o IMEI
ou usar o Secure. ANDROID_ID
Como alternativa, quando o dispositivo não possui recursos de telefone:
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);
Mais especificamente Settings.Secure.ANDROID_ID
,. Essa é uma quantidade de 64 bits que é gerada e armazenada quando o dispositivo inicializa. É reiniciado quando o dispositivo é limpo.
ANDROID_ID
parece uma boa escolha para um identificador de dispositivo exclusivo. Existem desvantagens: primeiro, não é 100% confiável em lançamentos do Android anteriores à 2.2. (“Froyo”).
Além disso, houve pelo menos um bug amplamente observado em um telefone celular popular de um grande fabricante, onde cada instância tem o mesmo ANDROID_ID.
Para entender os códigos únicos disponíveis em dispositivos Android. Use este guia oficial.
Práticas recomendadas para identificadores exclusivos:
IMEI, endereços Mac, ID da instância, GUIDs, SSAID, ID da publicidade, API da Safety Net para verificar dispositivos.
https://developer.android.com/training/articles/user-data-ids
ID da instância do Google
Lançado em I / O 2015; no Android requer serviços de reprodução 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
Parece que o Google pretende que esse ID seja usado para identificar instalações no Android, Chrome e iOS.
Ele identifica uma instalação e não um dispositivo, mas novamente ANDROID_ID (que é a resposta aceita) agora também não identifica dispositivos. Com o tempo de execução do ARC, um novo ANDROID_ID é gerado para cada instalação ( detalhes aqui ), assim como esse novo ID de instância. Além disso, acho que identificar instalações (não dispositivos) é o que a maioria de nós está procurando.
As vantagens do ID da instância
Parece-me que o Google pretende que seja usado para esse fim (identificando suas instalações), seja multiplataforma e possa ser usado para vários outros fins (veja os links acima).
Se você usar o GCM, eventualmente precisará usar esse ID da instância, pois é necessário para obter o token do GCM (que substitui o antigo ID de registro do GCM).
As desvantagens / questões
Na implementação atual (GPS 7.5), o ID da instância é recuperado de um servidor quando seu aplicativo solicita. Isso significa que a chamada acima é uma chamada de bloqueio - nos meus testes não científicos, leva 1-3 segundos se o dispositivo estiver online e 0,5 - 1,0 segundos se estiver off-line (presumivelmente esse é o tempo que espera antes de desistir e gerar um ID aleatório). Isso foi testado na América do Norte no Nexus 5 com Android 5.1.1 e GPS 7.5.
Se você usar o ID para os fins que eles pretendem - por exemplo. autenticação de aplicativo, identificação de aplicativo, GCM - acho que esses 1-3 segundos podem ser um incômodo (dependendo do seu aplicativo, é claro).
ANDROID_ID
leia esta resposta e este bug .