Existe um ID de dispositivo Android exclusivo?

Respostas:

2026

Settings.Secure#ANDROID_IDretorna 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); 
Anthony Forloney
fonte
477
É conhecido por ser nulo às vezes, e está documentado como "pode ​​ser alterado após a redefinição de fábrica". Use por sua conta e risco e pode ser facilmente alterado em um telefone root.
Seva Alekseyev 23/06
18
Acho que precisamos ter cuidado ao usar ANDROID_ID no hash na primeira resposta, porque pode não ser definido quando o aplicativo é executado pela primeira vez, pode ser definido posteriormente ou pode até mudar na teoria, portanto, o ID exclusivo pode mudar
46
Esteja ciente existem grandes limitações com esta solução: android-developers.blogspot.com/2011/03/...
emmby
35
ANDROID_ID não identifica mais exclusivamente um dispositivo (a partir de 4.2): stackoverflow.com/a/13465373/150016
Tom
1146

ATUALIZAÇÃO : Nas versões recentes do Android, muitos dos problemas ANDROID_IDforam 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):

  1. Todos os dispositivos testados retornaram um valor para TelephonyManager.getDeviceId()
  2. Todos os dispositivos GSM (todos testados com um SIM) retornaram um valor para TelephonyManager.getSimSerialNumber()
  3. Todos os dispositivos CDMA retornaram nulos para getSimSerialNumber()(conforme o esperado)
  4. Todos os dispositivos com uma conta do Google adicionada retornaram um valor para ANDROID_ID
  5. Todos os dispositivos CDMA retornaram o mesmo valor (ou derivação do mesmo valor) para ambos ANDROID_IDe TelephonyManager.getDeviceId()- desde que uma conta do Google tenha sido adicionada durante a instalação.
  6. Ainda não tive a chance de testar dispositivos GSM sem SIM, um dispositivo GSM sem uma Conta do Google adicionada ou qualquer um dos dispositivos no modo aviã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 TelephonyManagerpropriedades, 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;
Joe
fonte
151
O código baseado em telefonia não estará presente em tablets, não é?
Seva Alekseyev 23/06
22
Por isso, como eu disse que a maioria não funcionará o tempo todo :) Ainda não encontrei nenhuma resposta para essa pergunta que seja confiável para todos os dispositivos, todos os tipos de dispositivos e configurações de hardware. É por isso que esta questão está aqui para começar. É bastante claro que não existe uma solução completa para isso. Fabricantes de dispositivos individuais podem ter números de série, mas eles não estão expostos para uso, e isso não é um requisito. Assim, ficamos com o que está disponível para nós.
Joe
31
O exemplo de código funciona muito bem. Lembre-se de adicionar <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.
Richard Richard
10
Esteja ciente existem grandes limitações com esta solução: android-developers.blogspot.com/2011/03/...
emmby
18
@softarn: Eu acredito que o que você está se referindo é o Blog do desenvolvedor Android que já está vinculado, o que explica o que você está tentando dizer, então talvez você devesse simplesmente ter votado positivamente no comentário dele. De qualquer forma, como Emmby menciona em sua resposta, ainda existem problemas, mesmo com as informações do blog. A pergunta pede um identificador único do DEVICE (não o identificador da instalação), por isso discordo da sua declaração. O blog está assumindo que o que você deseja não é necessariamente rastrear o dispositivo, enquanto a pergunta exige exatamente isso. Eu concordo com o blog de outra forma.
21411 Joe
439

Última atualização: 2/6/15


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.

Problema principal: Hardware vs Software

Hardware

  • Os usuários podem alterar seu hardware, tablet ou telefone Android, para que IDs únicos baseados em hardware não sejam boas ideias para TRACKING USERS
  • Para TRACKING HARDWARE , é uma ótima ideia

Programas

  • Os usuários podem limpar / alterar sua ROM se estiverem enraizados
  • Você pode rastrear usuários em várias plataformas (iOS, Android, Windows e Web)
  • O melhor desejo de rastrear um usuário individual com o seu consentimento é simplesmente fazer com que ele faça o login (simplifique usando o OAuth)

Repartição geral com Android

- Garantir exclusividade (incluir dispositivos raiz) para API> = 9/10 (99,5% dos dispositivos Android)

- Sem permissões extras

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

Lista de opções - razões pelas quais / por que não usá-las:

  • E-mail do usuário - Software

    • O usuário pode alterar o e-mail - ALTAMENTE improvável
    • API 5+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />ou
    • API 14+ <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" />( Como obter o endereço de e-mail principal do dispositivo Android )
  • Número de telefone do usuário - Software

    • Os usuários podem alterar os números de telefone - ALTAMENTE improvável
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI - Hardware (apenas telefones, necessidades android.permission.READ_PHONE_STATE)

    • A maioria dos usuários odeia o fato de que diz "Chamadas telefônicas" na permissão. Alguns usuários dão classificações ruins, porque acreditam que você está simplesmente roubando suas informações pessoais quando tudo o que você realmente quer fazer é rastrear as instalações do dispositivo. É óbvio que você está coletando dados.
    • <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)

    • Como ele pode ser 'nulo', podemos verificar 'nulo' e alterar seu valor, mas isso significa que não será mais exclusivo.
    • Se você tiver um usuário com um dispositivo de redefinição de fábrica, o valor poderá ter sido alterado ou alterado no dispositivo raiz, portanto, poderá haver entradas duplicadas se você estiver acompanhando as instalações do usuário.
  • Endereço MAC da WLAN - Hardware (necessário android.permission.ACCESS_WIFI_STATE)

    • Essa poderia ser a segunda melhor opção, mas você ainda está coletando e armazenando um identificador exclusivo que vem diretamente de um usuário. É óbvio que você está coletando dados.
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Endereço MAC Bluetooth - Hardware (dispositivos com Bluetooth, necessidades android.permission.BLUETOOTH)

    • A maioria dos aplicativos no mercado não usa Bluetooth e, portanto, se seu aplicativo não usa Bluetooth e você o inclui, o usuário pode suspeitar.
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • ID pseudo-exclusivo - software (para todos os dispositivos Android)

    • Muito possível, pode conter colisões - Veja meu método postado abaixo!
    • Isso permite que você tenha um ID 'quase único' do usuário sem usar nada privado. Você pode criar seu próprio ID anônimo a partir das informações do dispositivo.

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.

API> = 9:

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!

API <9:

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 todo o resto falhar:

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:

  • Removido 'Android.SECURE_ID' devido a redefinições de fábrica pode causar alterações no valor
  • Editou o código para alterar na API
  • Mudou o pseudo

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

Novo (para aplicativos com anúncios E Google Play Services):

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

Importante:

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

Aviso, os usuários podem redefinir:

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!

ID da Instância dos Serviços do Google Player

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

Jared Burrows
fonte
Mas a Buildclasse 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)
LuckyMe
2
Eu usei seu método no meu aplicativo para enviar comentários. Tenho más notícias. infelizmente, o PsuedoID não é totalmente exclusivo. meu servidor registrou mais de 100 para 5 ID e mais de 30 para quase 30 ID. os IDs mais repetidos são 'ffffffff-fc8f-6093-ffff-ffffd8' (159 registros) e 'ffffffff-fe99-b334-ffff-ffffef' (154 vezes). Também com base no tempo e nos comentários, é óbvio que existem povos diferentes. o total de registros até agora é de 10.000. por favor, deixe-me saber por que isso aconteceu. tanques.
Hojjat reyhane
1
Eu escrevi isso há mais de 1,5 anos. Não sei por que não é exclusivo para você. Você pode tentar o ID da publicidade. Caso contrário, você pode criar sua própria solução.
Jared Burrows
2
sorta..I'd realmente aprecio se você passar pela questão e dar seus pensamentos sobre isso
Durai Amuthan.H
1
@ user1587329 Obrigado. Estou tentando manter isso atualizado para todos. Essa pergunta é complicada quando se trata de hardware x software e plataforma cruzada.
Jared Burrows
340

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:

  • ANDROID_ID é o identificador de dispositivo preferido. ANDROID_ID é perfeitamente confiável nas versões do Android <= 2.1 ou> = 2.3. Apenas 2.2 tem os problemas mencionados no post.
  • Vários dispositivos de vários fabricantes são afetados pelo bug ANDROID_ID na 2.2.
  • Até onde pude determinar, todos os dispositivos afetados têm o mesmo ANDROID_ID , que é 9774d56d682e549c . Que também é o mesmo ID de dispositivo relatado pelo emulador, btw.
  • O Google acredita que os OEMs corrigiram o problema para muitos ou a maioria de seus dispositivos, mas pude verificar que, no início de abril de 2011, pelo menos, ainda é muito fácil encontrar dispositivos com o ANDROID_ID quebrado.

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;
    }
}
empapar
fonte
6
Você não deveria misturar os vários IDs para que eles tenham o mesmo tamanho? Além disso, você deve fazer o hash do ID do dispositivo para não expor acidentalmente informações particulares.
9788 Steve Jobs
2
Bons pontos, Steve. Eu atualizei o código para sempre retornar um UUID. Isso garante que: a) os IDs gerados tenham sempre o mesmo tamanho eb) os IDs do dispositivo e do Android sejam agrupados antes de serem devolvidos para evitar a exposição acidental de informações pessoais. Também atualizei a descrição para observar que o ID do dispositivo persistirá nas redefinições de fábrica e que isso pode não ser desejável para alguns usuários.
emmby
1
Eu acredito que você está incorreto; a solução preferida é rastrear instalações, não identificadores de dispositivos. Seu código é substancialmente mais longo e mais complexo que o da postagem do blog e não é óbvio para mim que agrega algum valor.
Tim Bray
7
Bom ponto, atualizei o comentário para sugerir fortemente que os usuários usem IDs de instalação de aplicativos em vez de IDs de dispositivo. No entanto, acho que essa solução ainda é valiosa para pessoas que precisam de um dispositivo em vez de um ID de instalação.
emmby
8
ANDROID_ID pode mudar em reposição de fábrica, por isso não pode identificar dispositivos bem
Samuel
180

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

Anthony Nolan
fonte
Eu estava usando o método de @Lenn Dolling com o horário atual anexado para o ID exclusivo. Mas esta parece ser a maneira mais simples e confiável. Obrigado Reto Meier e Antony Nolan
Gökhan Barış Aker
É ótimo, mas e os dispositivos enraizados? Eles podem acessar isso e mudar o uid para outro facilmente.
tasomaniac
3
Ótima opção se você não precisar do ID exclusivo para persistir após uma desinstalação e reinstalação (por exemplo, evento / jogo promocional em que você tem três chances de ganhar, ponto final).
Kyle Clegg
2
A apresentação do Meier depende do uso do Android Backup Manager, que por sua vez depende da escolha do usuário para ativar esse recurso. Isso é bom para as preferências do usuário do aplicativo (uso de Meier), porque se o usuário não tiver selecionado essa opção, ele simplesmente não fará o backup delas. No entanto, a pergunta original é sobre a geração de um ID exclusivo para o dispositivo , e esse ID é gerado por aplicativo, e nem mesmo por instalação, muito menos por dispositivo, e, como conta com o usuário selecionar a opção de backup, seus usos além do usuário as preferências (por exemplo, para um estudo por tempo limitado) são limitadas.
Carl
12
Isso não funcionará na desinstalação ou limpeza de dados.
John Shelley
106

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

Seva Alekseyev
fonte
8
Isso é necessárioandroid.permission.ACCESS_WIFI_STATE
ohhorob
5
Acho que você descobrirá que ele não está disponível quando o Wi-Fi está desligado, em praticamente todos os dispositivos Android. Desligar o WiFi remove o dispositivo no nível do kernel.
Chrisdowney
12
@ Sanandrea - vamos ser sinceros, em um dispositivo enraizado TUDO pode ser falsificado.
ocodo 4/09/13
5
O acesso ao endereço MAC do Wi-Fi foi bloqueado no Android M: stackoverflow.com/questions/31329733/…
breez
6
No Android 6.x, ele retorna um endereço mac falso e consistente:02:00:00:00:00:00
Behrouz.M 10/16/16
87

Há informações bastante úteis aqui .

Abrange cinco tipos diferentes de ID:

  1. IMEI (apenas para dispositivos Android com uso do telefone; necessidades android.permission.READ_PHONE_STATE)
  2. ID pseudo-exclusivo (para todos os dispositivos Android)
  3. ID do Android (pode ser nulo, pode ser alterado após a redefinição de fábrica, pode ser alterado no telefone raiz)
  4. Sequência de endereços MAC da WLAN (necessidades android.permission.ACCESS_WIFI_STATE)
  5. Cadeia de endereços MAC BT (dispositivos com Bluetooth, necessidades android.permission.BLUETOOTH)
stansult
fonte
2
Ponto importante deixado de fora (aqui e no artigo): você não pode obter WLAN ou BT MAC a menos que estejam ligados! Caso contrário, acho que o WLAN MAC seria o identificador perfeito. Você não tem garantia de que o usuário alguma vez ativará o Wi-Fi, e eu realmente não acho 'apropriado' ativá-lo.
Tom
1
@ Tom você está errado. Você ainda pode ler WLAN ou BT MAC, mesmo quando estão desligados. No entanto, não há garantia de que o dispositivo tenha módulos WLAN ou BT disponíveis.
Marqs
2
O endereço getMacAddress () do objeto aWifiInfo e o método BluetoothAdapter.getDefaultAdapter (). GetAddress () retornarão02: 00: 00: 00: 00: 00 a partir de agora
sarika kate
4
@sarikakate É verdade apenas no Marshmallow 6.0 e acima ... Ele ainda está funcionando conforme o esperado no Marshmallow abaixo do 6.0.
Smeet
@Smeet Sim, você é right.I esqueci de mencionar que o seu trabalho abaixo de 6,0
sarika kate
51

O Blog oficial do Android Developers agora tem um artigo completo sobre esse assunto, Identificando instalações de aplicativos .

BoD
fonte
4
E o ponto principal desse argumento é que, se você está tentando obter um ID exclusivo do hardware, provavelmente está cometendo um erro.
Tim Bray
3
E se você estiver permitindo que o bloqueio do dispositivo seja redefinido por uma redefinição de fábrica, o modelo do seu trialware está quase morto.
Seva Alekseyev
43

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.

TechnoTony
fonte
5
Isso não leva a vários dispositivos com o mesmo ID quando um usuário tem vários dispositivos? Um tablet e um telefone, por exemplo.
Tosa
O objetivo mínimo 8 é necessário para isso.
precisa
Essa seria a maneira preferida de criar uma carga útil de verificação ao fazer compras no aplicativo? No exemplo de código do exemplo de cobrança no aplicativo, comente: "Portanto, uma boa carga útil do desenvolvedor possui essas características: 1. Se dois usuários diferentes comprarem um item, a carga útil será diferente entre eles, para que a compra de um usuário não possa ser repetida para outro usuário. 2. A carga útil deve ser tal que você possa verificá-la mesmo quando o aplicativo não foi quem iniciou o fluxo de compras (para que os itens comprados pelo usuário em um dispositivo funcionem em outros dispositivos pertencentes ao usuário). "
TouchBoarder
@ Tosa eu tinha a mesma pergunta. Mas não poderíamos usar essa mesma técnica novamente para criar IDs de dispositivos virtuais e simplesmente não fazer o backup da mesma maneira? A identificação do dispositivo não persistiria após uma limpeza ou reinstalação, mas se tivermos uma identificação de usuário persistente, talvez não precisemos muito disso.
jwehrle
39

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

Lenn Dolling
fonte
7
Solução interessante. Parece que esta é uma situação em que você realmente deve apenas mesclar todos os dados concatenados em vez de tentar criar sua própria função "hash". Há muitos casos em que você pode obter colisões, mesmo que haja dados substanciais diferentes para cada valor. Minha recomendação: use uma função de hash e, em seguida, transforme os resultados binários em decimal e truncá-lo conforme necessário. Para fazer isso da maneira certa, você realmente deve usar um UUID ou uma string de hash completa.
21811 Steve
21
Você deve dar crédito a suas fontes ... Isto foi retirado diretamente do seguinte artigo: pocketmagic.net/?p=1662
Steve Haley
8
Esse ID está aberto a colisões como você não sabe o quê. É praticamente garantido o mesmo em dispositivos idênticos da mesma operadora.
Seva Alekseyev 26/05
7
Isso também pode mudar se o dispositivo for atualizado.
David Given
8
Solução muito, muito ruim. Testado em dois Nexus 5 ... Retorne os mesmos números.
Sinan Dizdarević
38

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

}
Roman SL
fonte
Acabei de ler no desenvolvedor xda que o ro.serialnoé usado para gerar o arquivo Settings.Secure.ANDROID_ID. Portanto, são basicamente representações diferentes do mesmo valor.
Martin
@ Martin: mas provavelmente o número de série não muda ao redefinir o dispositivo. Não é? Apenas um novo valor ANDROID_IDé derivado dele.
Ronnie
Na verdade, em todos os dispositivos que eu testei eles onde o mesmo. Ou pelo menos os valores de hash são os mesmos (por motivos de privacidade, não escrevo os valores verdadeiros nos arquivos de log).
Martin
Este valor é o mesmo queandroid.os.Build.SERIAL
eugeneek
android.os.Build.SERIALserá descontinuado no Android O, consulte android-developers.googleblog.com/2017/04/…
EpicPandaForce
32

É 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, FirebaseInstanceIdadicione 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.

Nikita Kurtin
fonte
3
Por enquanto, esta é a melhor resposta e a primeira frase é o melhor resumo: "É uma pergunta simples, sem uma resposta simples
adore
Você deve apreciar o comentário "exceto para prevenção de fraude de pagamento e telefonia" sem resposta fornecida sobre como resolver esse caso de uso.
Eran Boudjnah
@EranBoudjnah Essa citação da referência oficial, que está vinculada na resposta. Posso tentar resolver esse caso de uso, mas como não é específico da questão do OP, de acordo com a política do stackoverflow - isso deve ser feito em uma pergunta dedicada separada.
Nikita Kurtin 12/02
Só estou dizendo que a resposta está incompleta. Mas sei que não é sua culpa, pois isso é uma citação.
Eran Boudjnah 12/02
1
@ M.UsmanKhan, a resposta está escrita logo após: " Hoje não é apenas uma prática recomendada, você deve fazê-lo por lei de acordo com o GDPR - identificadores e regulamentos similares. "
Nikita Kurtin
27

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); 
Mohit Kanada
fonte
21

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.

rony l
fonte
2
Infelizmente, é "desconhecido".
m0skit0
18

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.

Tony Maro
fonte
Qual é o id? é por acaso 9774d56d682e549c?
precisa saber é o seguinte
Uau, isso foi há tanto tempo que eu joguei aquele tablet há muito tempo. Não sabia dizer.
Tony Maro
Trabalho. Além disso, é muito mais compacto do que o ID que recebo do UUID aleatório.
Treewallie 31/07/19
1
@Treewallie funciona? Você pode obter o mesmo ID de dispositivo de diferentes aplicativos?
Arnold Brown
@ArnoldBrown Sim. Certifique-se de testá-lo completamente. Tenha um ótimo dia: D
Treewallie
16

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_IDdevem 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();
    }
}
Kevin Parker
fonte
Se você deseja rastrear instalações de aplicativos, isso é perfeito. Embora os dispositivos de rastreamento sejam muito mais complicados, não parece haver uma solução totalmente estanque ao ar.
Luca Spiller #
E os dispositivos enraizados? Eles podem alterar esse ID de instalação facilmente, certo?
tasomaniac
Absolutamente. Raiz pode alterar o ID da instalação. Você pode verificar raiz usando este bloco de código: stackoverflow.com/questions/1101380/...
Kevin Parker
se redefinirmos a fábrica, o arquivo será excluído?
21815 Jamshid
Se você redefinir de fábrica e excluir ou formatar a partição / data, o UUID será diferente.
Kevin Parker
12

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" />
Android
fonte
10

O ID exclusivo de um dispositivo Android OS como String, usando TelephonyManagere 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 .

Jorgesys
fonte
9

Existem várias abordagens diferentes para solucionar esses ANDROID_IDproblemas ( nullàs vezes, ou dispositivos de um modelo específico sempre retornam o mesmo ID) com prós e contras:

  • Implementando um algoritmo de geração de ID personalizado (com base nas propriedades do dispositivo que devem ser estáticas e não serão alteradas -> quem sabe)
  • Abusar de outros IDs como IMEI , número de série, endereço Wi-Fi / Bluetooth-MAC (eles não existirão em todos os dispositivos ou serão necessárias permissões adicionais)

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_IDrecurso com fallbacks para os problemas mencionados acima.

Andreas Klöber
fonte
8

E o IMEI ? Isso é exclusivo para Android ou outros dispositivos móveis.

Elzo Valugi
fonte
9
Não para meus tablets, que não têm um IMEI, pois não se conectam à minha operadora de celular.
Brill Pappin
2
Sem mencionar os dispositivos CDMA que possuem um ESN em vez de um IMEI.
David Given
@ David dado Existe algum CDMA com Android?
Elzo Valugi
1
Só fará isso: é um telefone :) Um Tablet pode não.
Brill Pappin
3
@ElzoValugi Já é "hoje em dia" e nem todos os tablets têm cartões SIM.
Matthew Quiros
8

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;
}
Eng.Fouad
fonte
se ReadPhoneState usuário que na versão 6.0 pedindo permissão runtime
Harsha
8

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);
        }
    }
}
Mr_and_Mrs_D
fonte
8

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 à longIdsequê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 "";
}
ᴛʜᴇᴘᴀᴛᴇʟ
fonte
1
Adicionando o UUID ao longIde armazená-lo em um arquivo, irá torná-lo o identificador mais exclusivo:String uuid = UUID.randomUUID().toString();
Mousa Alfhaily
1
Se tudo mais falhar, se o usuário tiver uma API menor que a 9 (menor que Gingerbread), o telefone foi redefinido ou 'Secure.ANDROID_ID'. se retornar 'nulo', simplesmente o ID retornado será baseado apenas nas informações do dispositivo Android. É aqui que as colisões podem acontecer. Tente não usar DISPLAY, HOST ou ID - esses itens podem mudar. Se houver colisões, haverá dados sobrepostos. A fonte: gist.github.com/pedja1/fe69e8a80ed505500caa
Mousa Alfhaily
Se tentarmos obter um número exclusivo por essa linha de código, podemos dizer que é o ID exclusivo e ele nunca entrará em conflito com outro dispositivo?
Ninja
1
@Ninja Como o endereço MAC do BLE é único, sim, o ID gerado será sempre único. No entanto, se você realmente quer ter certeza, sugiro adicionar um UUID ao arquivo longId. Altere essa linha como esta: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();Isso garante que o ID gerado seja exclusivo.
ᴛʜᴇᴘᴀᴛᴇʟ
@ ᴛʜᴇᴘᴀᴛᴇʟ Muito obrigado por esta enorme ajuda. Na verdade, meu aplicativo tem dados muito confidenciais, por isso preciso ter certeza de que é por isso que estou apenas confirmando essas coisas.
Ninja #
7

Outra maneira é usar /sys/class/android_usb/android0/iSerialem 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:

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

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.

insitusec
fonte
Acabei de ler sua postagem no blog. Acredito que isso não seja exclusivo: o Build.SERIAL também está disponível sem permissões e é (em teoria) um número de série exclusivo do hardware.
Tom
1
Você está certo. É apenas mais uma maneira de rastrear seu dispositivo e, como você disse, as duas maneiras não exigem permissões de aplicativos.
insitusec
7

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;
}
Jorgesys
fonte
Descobri recentemente que o dispositivo de um cliente do tipo SM-G928F / Galaxy S6 edge + oferece apenas 15 em vez de 16 dígitos hexadecimais para o Android ID.
precisa
7

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.

Ilan.b
fonte
7

Eu uso o código a seguir para obter o IMEIou usar o Secure. ANDROID_IDComo 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);
Asaf Pinhassi
fonte
7

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

mumu123
fonte
1
esta resposta é um copypaste do antigo blog do google android-developers.googleblog.com/2011/03/… . Então o bug já está resolvido?
Sergii 27/01
7

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

Waheed Nazir
fonte
6

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

Tom
fonte
1
Outra desvantagem significativa do instanceID é que um novo instanceID será gerado para você se o usuário limpar os dados do aplicativo.
Id27kav
Interessante, mas não acho que isso realmente mude os possíveis casos de uso: o ID da instância, como android_id, não é adequado para identificar um dispositivo. Portanto, seu servidor verá os dados de limpeza do usuário como desinstalação e reinstalação do aplicativo, o que não é razoável.
Tom