Существует ли уникальный идентификатор устройства Android?

2755

Есть ли у Android-устройств уникальный идентификатор, и если да, то какой простой способ получить к нему доступ с помощью Java?

Тайлер
источник
38
Если вы используете ANDROID_ID, обязательно прочитайте этот ответ и эту ошибку .
Джерадж Вепакомма
Если вам нужно решение в 2020 году, вы должны прочитать идентификаторы Android в 2020 году
Никита Куртин

Ответы:

2027

Settings.Secure#ANDROID_IDвозвращает идентификатор Android как уникальную для каждого пользователя 64-битную шестнадцатеричную строку.

import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID); 
Энтони Форлони
источник
477
Известно, что иногда оно пустое, оно задокументировано как «может измениться после сброса к заводским настройкам». Используйте на свой страх и риск, и это может быть легко изменено на рутированном телефоне.
Сева Алексеев
18
Я думаю, что мы должны быть осторожны с использованием ANDROID_ID в хэше в первом ответе, потому что он может не быть установлен при первом запуске приложения, может быть установлен позже или может даже измениться в теории, следовательно, может измениться уникальный идентификатор
46
Имейте в виду, что
emmby
35
ANDROID_ID больше не уникально идентифицирует устройство (начиная с 4.2): stackoverflow.com/a/13465373/150016
Том
1146

ОБНОВЛЕНИЕ : В последних версиях Android многие проблемы ANDROID_IDбыли решены, и я считаю, что такой подход больше не нужен. Пожалуйста, взгляните на ответ Энтони .

Полное раскрытие: мое приложение изначально использовало описанный ниже подход, но больше не использует этот подход, и теперь мы используем подход, изложенный в записи блога Android Developer Blog, на которую ссылается ответ emmby (а именно, создание и сохранение a UUID#randomUUID()).


Есть много ответов на этот вопрос, большинство из которых будет работать только «некоторое время», и, к сожалению, этого недостаточно.

На основании моих тестов устройств (все телефоны, хотя бы один из которых не активирован):

  1. Все протестированные устройства вернули значение для TelephonyManager.getDeviceId()
  2. Все устройства GSM (все протестированные с SIM-картой) вернули значение для TelephonyManager.getSimSerialNumber()
  3. Все устройства CDMA вернули ноль для getSimSerialNumber()(как и ожидалось)
  4. Все устройства с добавленной учетной записью Google вернули значение для ANDROID_ID
  5. Все устройства CDMA возвращали одно и то же значение (или производное одно и то же значение) для обоих ANDROID_IDи TelephonyManager.getDeviceId()- до тех пор, пока учетная запись Google была добавлена ​​во время установки.
  6. У меня еще не было возможности протестировать устройства GSM без SIM-карты, устройство GSM без добавленной учетной записи Google или какое-либо из устройств в режиме полета.

Так что если вы хотите что-то уникальное для самого устройства, TM.getDeviceId() должно быть достаточно. Очевидно, что некоторые пользователи более параноидальны, чем другие, поэтому может быть полезно хешировать 1 или более этих идентификаторов, чтобы строка по-прежнему оставалась практически уникальной для устройства, но явно не идентифицировала фактическое устройство пользователя. Например, используя в String.hashCode()сочетании с 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();

может привести к чему-то вроде: 00000000-54b3-e7c7-0000-000046bffd97

Это работает достаточно хорошо для меня.

Как упоминает Ричард ниже, не забывайте, что вам нужно разрешение на чтение TelephonyManagerсвойств, поэтому добавьте это в свой манифест:

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

импортные библиотеки

import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
Джо
источник
151
Телефонный идентификатор не будет на планшетных устройствах, да?
Сева Алексеев
22
Поэтому я сказал, что большинство из них не будет работать постоянно :) Я пока не вижу ответа на этот вопрос, который был бы надежным для всех устройств, всех типов устройств и всех аппаратных конфигураций. Вот почему этот вопрос здесь для начала. Совершенно очевидно, что для этого не существует универсального решения. Отдельные производители устройств могут иметь серийные номера устройств, но они не предоставляются нам для использования, и это не является обязательным требованием. Таким образом, мы остаемся с тем, что нам доступно.
Джо
31
Пример кода отлично работает. Не забудьте добавить<uses-permission android:name="android.permission.READ_PHONE_STATE" /> в файл манифеста. При хранении в базе данных возвращаемая строка имеет длину 36 символов.
Ричард
10
Имейте в виду, что
emmby
18
@softarn: Я полагаю, что вы имеете в виду блог разработчиков Android, на который уже ссылались emmby, в котором объясняется, что вы пытаетесь сказать, так что, возможно, вам следовало бы просто вместо этого проголосовать за его комментарий. В любом случае, как упоминает Эмбби в своем ответе, проблемы с информацией в блоге все еще существуют. Вопрос требует уникального идентификатора УСТРОЙСТВА (не идентификатора установки), поэтому я не согласен с вашим утверждением. В блоге делается предположение, что вы не обязательно должны отслеживать устройство, тогда как вопрос требует именно этого. Я согласен с блогом в противном случае.
Джо
439

Последнее обновление: 02.06.15


После прочтения каждого поста о переполнении стека о создании уникального идентификатора, блога разработчиков Google и документации по Android, я чувствую, что «псевдоидентификация» - лучший вариант

Основная проблема: оборудование против программного обеспечения

аппаратные средства

  • Пользователи могут менять свое оборудование, планшет Android или телефон, поэтому уникальные идентификаторы, основанные на оборудовании, не являются хорошими идеями для отслеживания пользователей
  • За ОТСЛЕЖИВАНИЯ ОБОРУДОВАНИЯ это отличная идея

Програмное обеспечение

  • Пользователи могут стереть / изменить свой ПЗУ, если они рутированы
  • Вы можете отслеживать пользователей на разных платформах (iOS, Android, Windows и Web)
  • Лучшее желание отследить индивидуального пользователя с его согласия - просто попросить его войти (сделайте это без проблем, используя OAuth)

Общий срыв с Android

- Гарантия уникальности (включая рутированные устройства) для API> = 9/10 (99,5% устройств Android)

- Нет дополнительных разрешений

Код Псуэдо:

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)

Спасибо @stansult за публикацию всех наших опций (в этом вопросе о переполнении стека).

Список опций - причины, почему / почему не стоит их использовать:

  • Электронная почта пользователя - Программное обеспечение

  • Номер телефона пользователя - Программное обеспечение

    • Пользователи могут менять номера телефонов - крайне маловероятно
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • IMEI - Hardware (только телефоны, нужны android.permission.READ_PHONE_STATE)

    • Большинство пользователей ненавидят тот факт, что в разрешении написано «Телефонные звонки». Некоторые пользователи дают плохие оценки, потому что они считают, что вы просто крадете их личную информацию, когда все, что вы действительно хотите сделать, это отслеживать установку устройства. Очевидно, что вы собираете данные.
    • <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • Идентификатор Android - Аппаратное обеспечение (может быть нулевым, может изменяться после сброса к заводским настройкам, может быть изменено на корневом устройстве)

    • Так как это может быть 'null', мы можем проверить 'null' и изменить его значение, но это означает, что он больше не будет уникальным.
    • Если у вас есть пользователь с устройством сброса к заводским настройкам, значение может быть изменено или изменено на корневом устройстве, поэтому могут быть дубликаты записей, если вы отслеживаете установки пользователей.
  • WLAN MAC-адрес - Аппаратное обеспечение (необходимо android.permission.ACCESS_WIFI_STATE)

    • Это может быть второй лучший вариант, но вы все еще собираете и храните уникальный идентификатор, который приходит непосредственно от пользователя. Это очевидно, что вы собираете данные.
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Bluetooth MAC Address - Hardware (устройства с Bluetooth, нужны android.permission.BLUETOOTH)

    • Большинство приложений на рынке не используют Bluetooth, и поэтому, если ваше приложение не использует Bluetooth, и вы включаете это, пользователь может заподозрить.
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • Pseudo-Unique ID - Программное обеспечение (для всех устройств Android)

    • Очень возможно, может содержать столкновения - см. Мой метод, опубликованный ниже!
    • Это позволяет вам иметь «почти уникальный» идентификатор пользователя, не принимая ничего личного. Вы можете создать свой собственный анонимный идентификатор из информации об устройстве.

Я знаю, что не существует «идеального» способа получения уникального идентификатора без использования разрешений; Однако иногда нам действительно нужно отслеживать установку устройства. Когда дело доходит до создания уникального идентификатора, мы можем создать «псевдо уникальный идентификатор», основанный исключительно на информации, которую нам предоставляет API Android без использования дополнительных разрешений. Таким образом, мы можем проявить уважение к пользователю и попытаться предложить хороший пользовательский опыт.

С псевдо-уникальным идентификатором вы действительно сталкиваетесь только с тем фактом, что могут быть дубликаты, основанные на том факте, что существуют подобные устройства. Вы можете настроить комбинированный метод, чтобы сделать его более уникальным; однако, некоторые разработчики должны отслеживать установку устройств, и это будет способствовать достижению цели или производительности на основе аналогичных устройств.

API> = 9:

Если их Android-устройство имеет API 9 или более поздней версии, оно гарантированно будет уникальным из-за поля «Build.SERIAL».

ПОМНИТЕ , технически вы пропускаете только около 0,5% пользователей с API <9 . Таким образом, вы можете сосредоточиться на остальном: это 99,5% пользователей!

API <9:

Если Android-устройство пользователя ниже API 9; надеюсь, что они не сделали сброс к заводским настройкам, и их «Secure.ANDROID_ID» будет сохранен или не будет «нулевым». (см. http://developer.android.com/about/dashboards/index.html )

Если все остальное не удается:

Если все остальное терпит неудачу, если пользователь имеет более низкий, чем API 9 (ниже, чем Gingerbread), сбросил свое устройство или «Secure.ANDROID_ID» возвращает «ноль», то просто возвращенный идентификатор будет основан исключительно на информации об их устройстве Android. Это где столкновения могут произойти.

Изменения:

  • Удален «Android.SECURE_ID» из-за сброса настроек, что может привести к изменению значения
  • Отредактировал код для изменения на API
  • Поменял псевдо

Пожалуйста, взгляните на метод ниже:

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

Новое (для приложений с рекламой и сервисами Google Play):

Из консоли разработчика Google Play:

Начиная с 1 августа 2014 г. в соответствии с политикой программы разработчиков Google Play все новые загрузки и обновления приложений должны использовать рекламный идентификатор вместо любых других постоянных идентификаторов для любых рекламных целей. Учить больше

Реализация :

Разрешение:

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

Код:

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

Источник / Docs:

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

Важный:

Предполагается, что рекламный идентификатор полностью заменит существующее использование других идентификаторов в рекламных целях (например, использование ANDROID_ID в Settings.Secure), когда службы Google Play доступны. Случаи, когда Сервисы Google Play недоступны, указываются в исключении GooglePlayServicesNotAvailableException, генерируемом getAdvertisingIdInfo ().

Внимание, пользователи могут сбросить:

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

Я пытался ссылаться на каждую ссылку, с которой я взял информацию. Если вы пропали без вести и должны быть включены, пожалуйста, прокомментируйте!

Идентификатор экземпляра служб Google Player

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

Джаред Барроуз
источник
Но не Buildизменится ли класс после обновления ОС? Особенно, если API обновился? Если так, как вы можете гарантировать, что это уникально? (Говоря о методе, который вы написали)
LuckyMe
2
Я использовал ваш метод в моем приложении для отправки комментариев. у меня плохие новости. к сожалению, PsuedoID не уникален полностью. Мой сервер записал более 100 для 5 ID и более 30 для почти 30 ID. наиболее повторяющиеся идентификаторы: «ffffffff-fc8f-6093-ffff-ffffd8» (159 записей) и «ffffffff-fe99-b334-ffff-ffffef» (154 раза). также, основываясь на времени и комментариях, очевидно, что есть разные народы. общее количество записей до сих пор составляет 10000. пожалуйста, дайте мне знать, почему это произошло. танки.
Ходжат Рейхане
1
Я написал это более 1,5 лет назад. Я не уверен, почему это не уникально для вас. Вы можете попробовать рекламный идентификатор. Если нет, вы можете придумать собственное решение.
Джаред Берроуз
2
Сорта ... Я был бы очень признателен, если вы ответите на этот вопрос и
поделитесь
1
@ user1587329 Спасибо. Я пытаюсь держать это в курсе для всех. Этот вопрос сложен, когда дело доходит до аппаратного и программного обеспечения и кроссплатформенности.
Джаред Берроуз
340

Как упоминает Дейв Уэбб, в блоге разработчиков Android есть статья , в . Их предпочтительным решением является отслеживание установок приложений, а не устройств, и это будет хорошо работать в большинстве случаев использования. В блоге будет показан необходимый код для этой работы, и я рекомендую вам проверить его.

Тем не менее, в блоге обсуждаются решения, если вам нужен идентификатор устройства, а не идентификатор установки приложения. Я поговорил с кем-то из Google, чтобы получить дополнительные разъяснения по нескольким пунктам на тот случай, если вам нужно это сделать. Вот что я обнаружил об идентификаторах устройств, которые НЕ упоминаются в вышеупомянутом сообщении в блоге:

  • ANDROID_ID является предпочтительным идентификатором устройства. ANDROID_ID абсолютно надежен в версиях Android <= 2.1 или> = 2.3. Только 2.2 имеет проблемы, упомянутые в посте.
  • На несколько устройств нескольких производителей влияет ошибка ANDROID_ID в 2.2.
  • Насколько я смог определить, все затронутые устройства имеют одинаковый ANDROID_ID , который 9774d56d682e549c . Который также является идентичным идентификатором устройства, сообщаемым эмулятором, кстати.
  • Google считает, что производители исправили проблему для многих или большинства своих устройств, но я смог убедиться, что по крайней мере на начало апреля 2011 года найти устройства с поврежденным ANDROID_ID по-прежнему довольно легко.

Основываясь на рекомендациях Google, я реализовал класс, который будет генерировать уникальный UUID для каждого устройства, используя ANDROID_ID в качестве начального числа, где это необходимо, используя при необходимости TelephonyManager.getDeviceId (), а в случае неудачи прибегая к случайно сгенерированному уникальному UUID. это сохраняется при перезапуске приложения (но не при переустановке приложения).

Обратите внимание, что для устройств, которые вынуждены использовать идентификатор устройства, используется уникальный идентификатор будет сохраняться при сбросе до заводских настроек. Это то, что нужно знать. Если вам необходимо убедиться, что сброс настроек к заводским установкам приведет к сбросу вашего уникального идентификатора, вы можете рассмотреть возможность возврата непосредственно к случайному UUID вместо идентификатора устройства.

Опять же, этот код предназначен для идентификатора устройства, а не идентификатора установки приложения. В большинстве случаев идентификатор установки приложения, вероятно, то, что вы ищете. Но если вам нужен идентификатор устройства, то следующий код, вероятно, будет работать для вас.

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;
    }
}
emmby
источник
6
Разве вы не должны хэшировать различные идентификаторы, чтобы они все были одинакового размера? Кроме того, вы должны хешировать идентификатор устройства, чтобы случайно не раскрыть личную информацию.
Стив Померой
2
Хорошие моменты, Стив. Я обновил код, чтобы всегда возвращать UUID. Это гарантирует, что a) сгенерированные идентификаторы всегда имеют одинаковый размер, и b) идентификаторы android и устройства хешируются перед возвратом, чтобы избежать случайного раскрытия личной информации. Я также обновил описание, чтобы отметить, что идентификатор устройства будет сохраняться после сброса настроек и что это может быть нежелательным для некоторых пользователей.
Эмбби
1
Я считаю, что вы не правы; предпочтительным решением является отслеживание установок, а не идентификаторов устройств. Ваш код значительно длиннее и сложнее, чем в сообщении в блоге, и для меня не очевидно, что он добавляет какую-либо ценность.
Тим Брей
7
Хорошо, я обновил комментарий, чтобы настоятельно рекомендовать пользователям использовать идентификаторы установки приложения, а не идентификаторы устройств. Тем не менее, я думаю, что это решение по-прежнему ценно для людей, которым нужно устройство, а не ID установки.
Эмбби
8
ANDROID_ID может измениться при сбросе к заводским настройкам, поэтому он также не может идентифицировать устройства
Самуил
180

Вот код, который Reto Meier использовал в презентации Google I / O в этом году, чтобы получить уникальный идентификатор для пользователя:

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

Если вы соедините это со стратегией резервного копирования для отправки предпочтений в облако (также описано в выступлении Рето , у вас должен быть идентификатор, который привязывается к пользователю и остается после того, как устройство было стерто или даже заменено. Я планирую использовать это в аналитике, идущей вперед (другими словами, я еще этого не сделал :).

Энтони Нолан
источник
Я использовал метод @Lenn Dolling с текущим временем, добавленным для уникального идентификатора. Но это кажется более простым и надежным способом. Спасибо Рето Майер и Энтони Нолан
Гекхан Барыш Акер
Это здорово, но как насчет рутованных устройств? Они могут получить доступ к этому и легко изменить UID на другой.
Tasomaniac
3
Отличный вариант, если вам не нужен уникальный идентификатор, чтобы сохранить его после удаления и переустановки (например, рекламное мероприятие / игра, в которой у вас есть три шанса на победу, точка).
Кайл Клегг
2
Презентация Meier основана на использовании диспетчера резервного копирования Android, который, в свою очередь, зависит от выбора пользователем этой функции. Это хорошо для пользовательских настроек приложения (использование Мейера), потому что, если пользователь не выбрал эту опцию, он просто не получит эти резервные копии. Однако первоначальный вопрос касается генерации уникального идентификатора для устройства , и этот идентификатор генерируется для каждого приложения, а не для каждой установки, не говоря уже о каждом устройстве, и, поскольку он полагается на то, что пользователь выбирает параметр резервного копирования, его использование не зависит от пользователя. предпочтения (например, для ограниченного по времени испытания) ограничены.
Карл
12
Это не будет работать при удалении или очистке данных.
Джон Шелли
106

Также вы можете рассмотреть MAC-адрес адаптера Wi-Fi. Получено так:

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

Требуется разрешение android.permission.ACCESS_WIFI_STATEв манифесте.

Сообщается о доступности, даже если Wi-Fi не подключен. Если Джо из приведенного выше ответа попробует это на своих многочисленных устройствах, это было бы неплохо.

На некоторых устройствах он недоступен при выключенном Wi-Fi.

ПРИМЕЧАНИЕ. В Android 6.x возвращается поддельный mac-адрес:02:00:00:00:00:00

Сева Алексеев
источник
8
Это требуетсяandroid.permission.ACCESS_WIFI_STATE
охороб
5
Я думаю, вы обнаружите, что он недоступен, когда WiFi выключен, практически на всех устройствах Android. Отключение WiFi удаляет устройство на уровне ядра.
chrisdowney
12
@Sanandrea - давайте посмотрим правде в глаза, на рутованном устройстве все может быть подделано.
ocodo
5
Доступ к MAC-адресу WiFi был заблокирован на Android M: stackoverflow.com/questions/31329733/…
breez
6
Из Android 6.x он возвращает постоянный поддельный mac-адрес:02:00:00:00:00:00
Behrouz.M
87

Там довольно полезная информация здесь .

Он охватывает пять различных типов удостоверений личности:

  1. IMEI (только для устройств Android с использованием телефона; необходимо android.permission.READ_PHONE_STATE)
  2. Псевдо-уникальный идентификатор (для всех устройств Android)
  3. Идентификатор Android (может быть нулевым, может быть изменен при сбросе настроек, может быть изменен на рутированном телефоне)
  4. Строка MAC-адреса WLAN (необходимо android.permission.ACCESS_WIFI_STATE)
  5. Строка MAC-адреса BT (устройства с Bluetooth, нужны android.permission.BLUETOOTH)
stansult
источник
2
Упущенный важный момент (здесь и в статье): вы не можете получить WLAN или BT MAC, если они не включены! В противном случае я думаю, что WLAN MAC будет идеальным идентификатором. У вас нет гарантии, что пользователь когда-либо включит свой Wi-Fi, и я не думаю, что «уместно» включать его самостоятельно.
Том
1
@ То есть ты не прав. Вы по-прежнему можете читать WLAN или BT MAC, даже если они выключены. Тем не менее, нет никаких гарантий, что устройство имеет доступные модули WLAN или BT.
Marqs
2
В частности, локальные MAC-адреса WiFi и Bluetooth больше не доступны. Метод getMacAddress () объекта aWifiInfo и метод BluetoothAdapter.getDefaultAdapter (). GetAddress () теперь будут возвращать 02: 00: 00: 00: 00: 00: 00
Сарика Кейт
4
@sarikakate Это верно только в 6.0 Зефир и выше ... Он все еще работает, как и ожидалось в ниже 6.0 Зефир.
Смит
@ Смит Да, вы правы. Я забыл упомянуть, что он работает ниже 6.0
Сарика Кейт
51

В официальном блоге разработчиков Android теперь есть полная статья, посвященная этой теме, « Определение установок приложений» .

Совет директоров
источник
4
И ключевой момент этого аргумента в том, что если вы пытаетесь получить уникальный идентификатор из аппаратного обеспечения, вы, вероятно, делаете ошибку.
Тим Брей
3
И если вы позволяете сбросить блокировку устройства с помощью заводского сброса, ваша модель пробной версии ничем не уступает.
Сева Алексеев
43

В Google I / O Рето Мейер (Rto Meier) выпустил исчерпывающий ответ о том, как подойти к этому, что должно удовлетворить потребности большинства разработчиков для отслеживания пользователей в различных установках. Энтони Нолан показывает направление в своем ответе, но я решил написать полный подход, чтобы другие могли легко увидеть, как это сделать (мне потребовалось некоторое время, чтобы выяснить детали).

Этот подход даст вам анонимный, безопасный идентификатор пользователя, который будет постоянным для пользователя на разных устройствах (в зависимости от основной учетной записи Google) и во время установки. Основной подход заключается в создании случайного идентификатора пользователя и сохранении его в общих настройках приложений. Затем вы используете агент резервного копирования Google для хранения общих настроек, связанных с учетной записью Google, в облаке.

Давайте пройдем через полный подход. Во-первых, нам нужно создать резервную копию наших SharedPreferences с помощью службы резервного копирования Android. Начните с регистрации вашего приложения черезhttp://developer.android.com/google/backup/signup.html .

Google предоставит вам ключ службы резервного копирования, который необходимо добавить в манифест. Вы также должны указать приложению использовать BackupAgent следующим образом:

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

Затем вам нужно создать агент резервного копирования и указать ему использовать вспомогательный агент для общих настроек:

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

Для завершения резервного копирования вам необходимо создать экземпляр BackupManager в вашей основной деятельности:

BackupManager backupManager = new BackupManager(context);

Наконец, создайте идентификатор пользователя, если он еще не существует, и сохраните его в 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;
}

Этот User_ID теперь будет постоянным во всех установках, даже если пользователь перемещает устройство.

Для получения дополнительной информации об этом подходе см. Доклад Рето .

А для полной информации о том, как реализовать агент резервного копирования, см. Резервное копирование данных . Я особенно рекомендую раздел внизу, посвященный тестированию, поскольку резервное копирование не происходит мгновенно, поэтому для тестирования необходимо принудительно создать резервную копию.

TechnoTony
источник
5
Не приводит ли это к нескольким устройствам с одинаковым идентификатором, когда у пользователя есть несколько устройств? Например, планшет и телефон.
Тоса
Для этого требуется минимум 8.
Halxinate
Будет ли это предпочтительным способом создания полезной нагрузки для проверки при совершении покупок внутри приложения? Из комментария к коду биллинга в приложении: «Итак, хорошая полезная нагрузка разработчика имеет следующие характеристики: 1. Если товар приобретают два разных пользователя, полезная нагрузка у них разная, поэтому покупка одного пользователя не может быть воспроизведена другим пользователем. 2. Полезная нагрузка должна быть такой, чтобы вы могли проверить ее, даже если приложение не было тем, кто инициировал процесс покупки (чтобы товары, приобретенные пользователем на одном устройстве, работали на других устройствах, принадлежащих пользователю). "
TouchBoarder
@ Тоса У меня был точно такой же вопрос. Но разве мы не могли бы снова использовать эту же технику для создания идентификаторов виртуальных устройств и просто не поддерживать их таким же образом? Идентификатор устройства не будет сохраняться после стирания или переустановки, но если у нас есть постоянный идентификатор пользователя, нам может не понадобиться так много из идентификатора устройства.
jwehrle
39

Я думаю, что это верный способ построить каркас для уникального идентификатора ... проверить это.

Псевдо-уникальный идентификатор, который работает на всех устройствах Android. На некоторых устройствах нет телефона (например, планшета) или по какой-то причине вы не хотите включать разрешение READ_PHONE_STATE. Вы по-прежнему можете читать такие сведения, как версия ПЗУ, имя производителя, тип ЦП и другие сведения об оборудовании, которые хорошо подойдут, если вы хотите использовать идентификатор для проверки серийного ключа или других общих целей. Вычисленный таким образом идентификатор не будет уникальным: можно найти два устройства с одинаковым идентификатором (на основе одного и того же оборудования и образа ПЗУ), но изменения в реальных приложениях незначительны. Для этого вы можете использовать класс 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

Большинство членов Build являются строками, мы здесь берем их длину и преобразуем их по модулю в цифру. У нас есть 13 таких цифр, и мы добавляем еще две впереди (35), чтобы иметь тот же идентификатор размера, что и IMEI (15 цифр). Есть и другие возможности, просто взгляните на эти строки. Возвращает что-то вроде 355715565309247. Специального разрешения не требуется, что делает этот подход очень удобным.


(Дополнительная информация: техника, приведенная выше, была скопирована из статьи о Pocket Magic .)

Ленн Доллинг
источник
7
Интересное решение. Похоже, это ситуация, когда вам действительно нужно просто хэшировать все эти соединенные данные вместо того, чтобы пытаться придумать свою собственную функцию «хеширования». Есть много случаев, когда вы можете столкнуться, даже если есть существенные данные, которые отличаются для каждого значения. Моя рекомендация: использовать хеш-функцию, а затем преобразовать двоичные результаты в десятичные и обрезать их при необходимости. Чтобы сделать это правильно, хотя вы действительно должны использовать UUID или полную строку хеша.
Стив Померой
21
Вы должны отдать должное своим источникам ... Это было взято
Стив Хейли
8
Этот идентификатор открыт для столкновений, как вы не знаете, что. Практически гарантированно будет одинаковым на одинаковых устройствах одного и того же оператора.
Сева Алексеев
7
Это также может измениться, если устройство будет обновлено.
Дэвид Дарил
8
Очень, очень плохое решение. Проверено на двух Nexus 5 ... Вернуть одинаковые номера.
Синан Диздаревич
38

Следующий код возвращает серийный номер устройства с помощью скрытого API Android. Но этот код не работает на Samsung Galaxy Tab, потому что "ro.serialno" не установлен на этом устройстве.

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

}
Роман СЛ
источник
Я только что прочитал на XDA Developer, что ro.serialnoиспользуется для создания Settings.Secure.ANDROID_ID. Таким образом, они в основном разные представления одного и того же значения.
Мартин
@Martin: но, возможно, серийный номер не меняется при перезагрузке устройства. Не так ли? Просто новое значение для ANDROID_IDявляется производным от него.
Ронни
На самом деле на всех устройствах, которые я тестировал, они были одинаковыми. Или, по крайней мере, значения хеш-функции, где они одинаковы (из соображений конфиденциальности я не записываю истинные значения в файлы журнала).
Мартин,
Это значение совпадает сandroid.os.Build.SERIAL
eugeneek
android.os.Build.SERIALустарела в Android O, см. android-developers.googleblog.com/2017/04/…
EpicPandaForce
32

Это простой вопрос, без простого ответа.

Более того, все существующие здесь ответы являются либо устаревшими, либо ненадежными.

Так что, если вы ищете решение в 2020 году .

Вот несколько вещей, которые следует иметь в виду:

Все аппаратные идентификаторы (SSAID, IMEI, MAC и т. Д.) Ненадежны для устройств, не принадлежащих Google (все, кроме пикселей и связующих), которые составляют более 50% активных устройств во всем мире. Поэтому в официальных рекомендациях по идентификаторам Android четко говорится:

Избегайте использования аппаратных идентификаторов , таких как SSAID (идентификатор Android), IMEI, MAC-адрес и т. Д.

Это делает большинство ответов выше недействительными. Кроме того, из-за различных обновлений безопасности для Android некоторые из них требуют более новых и более строгих разрешений во время выполнения, которые могут быть просто отклонены пользователем.

В качестве примера, CVE-2018-9489который влияет на все технологии, основанные на WIFI, упомянутые выше.

Это делает эти идентификаторы не только ненадежными, но и недоступными во многих случаях.

Итак, проще говоря: не используйте эти методы .

Многие другие ответы здесь предлагают использовать AdvertisingIdClient, что также несовместимо, так как его дизайн должен использоваться только для профилирования рекламы. Это также указано в официальной ссылке

Используйте рекламный идентификатор только для профилирования пользователей или для случаев использования рекламы.

Это не только ненадежно для идентификации устройства, но вы также должны соблюдать конфиденциальность пользователя в отношении политики отслеживания рекламы , в которой четко указано, что пользователь может сбросить или заблокировать ее в любой момент.

Так что не используйте его тоже .

Поскольку вы не можете иметь желаемый статический глобально уникальный и надежный идентификатор устройства. Официальный справочник Android предлагает:

Используйте FirebaseInstanceId или конфиденциальный GUID, когда это возможно, для всех других случаев использования, кроме предотвращения мошенничества с платежами и телефонии.

Он уникален для установки приложения на устройстве, поэтому, когда пользователь удаляет приложение - оно стирается, поэтому оно не на 100% надежно, но это следующая лучшая вещь.

Чтобы использовать, FirebaseInstanceIdдобавьте последнюю версию Firebase -сообщений в ваш Gradle

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

И используйте код ниже в фоновом потоке:

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

Если вам нужно сохранить идентификацию устройства на вашем удаленном сервере, не храните его как есть (простой текст), но хеш с солью .

Сегодня это не только лучшая практика, вы должны делать это по закону в соответствии с GDPR - идентификаторами и аналогичными правилами.

Никита Куртин
источник
3
На данный момент это лучший ответ, а первое предложение - это лучшее резюме: «Это простой вопрос, без простого ответа - просто любите его»
b2mob
Вы должны оценить комментарий «кроме предотвращения мошенничества с платежами и телефонии» без ответа относительно того, как решить этот вариант использования.
Эран Буджна
@EranBoudjnah Это цитата из официальной ссылки, которая связана в ответе. Я могу попытаться рассмотреть этот вариант использования, но поскольку он не является специфическим для вопроса OP, в соответствии с политикой stackoverflow - это должно быть сделано в отдельном отдельном вопросе.
Никита Куртин
Я просто говорю, что ответ неполный. Но я знаю, что это не твоя вина, так как это цитата.
Эран Буджна
1
@ M.UsmanKhan, ответ записывается сразу после этого: « Сегодня это не только лучшие практики, вы на самом деле должны делать это по закону в соответствии с GDPR - идентификаторов и аналогичных правил. »
Никита Куртин
27

Используя приведенный ниже код, вы можете получить уникальный идентификатор устройства Android OS в виде строки.

deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 
Мохит Канада
источник
21

Серийное поле было добавлено к Buildклассу уровня API 9 (Android 2.3 - Пряник). Документация говорит, что это представляет серийный номер оборудования. При этом он должен быть уникальным, если он существует на устройстве.

Я не знаю, поддерживается ли он (= не ноль) всеми устройствами с уровнем API> = 9.

Рони Л
источник
2
К сожалению, это «неизвестно».
m0skit0
18

Я добавлю одну вещь - у меня одна из тех уникальных ситуаций.

С помощью:

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

Оказывается, что хотя мой Viewsonic G Tablet сообщает об DeviceID, который не является нулевым, каждый G Tablet сообщает одно и то же число.

Это делает интересной игру в «Pocket Empires», которая дает вам мгновенный доступ к чьей-либо учетной записи на основе «уникального» DeviceID.

У моего устройства нет сотового радио.

Тони Маро
источник
Какой идентификатор? это случайно 9774d56d682e549c?
Mr_and_Mrs_D
Ого, это было так давно, я давно бросил этот планшет. Не могу сказать.
Тони Маро
Работает. Кроме того, он намного компактнее, чем идентификатор, который я получаю из случайного UUID.
Treewallie
1
@Treewallie это работает? Можно ли получить один и тот же идентификатор устройства из разных приложений?
Арнольд Браун
@ArnoldBrown Да. Убедитесь, что проверили это полностью.
Хорошего
16

Подробные инструкции о том, как получить уникальный идентификатор для каждого устройства Android, с которого установлено приложение, см. В официальном блоге разработчиков Android, в котором описывается определение установок приложений .

Кажется, лучший способ для вас - создать его самостоятельно после установки, а затем прочитать его при перезапуске приложения.

Я лично считаю это приемлемым, но не идеальным. Ни один идентификатор, предоставляемый Android, не работает во всех случаях, поскольку большинство из них зависят от состояния радиосвязи телефона (вкл / выкл Wi-Fi, вкл / выкл сотовой связи, вкл / выкл Bluetooth). Другие, подобные, Settings.Secure.ANDROID_IDдолжны быть реализованы производителем и не должны быть уникальными.

Ниже приведен пример записи данных в установочный файл, который будет храниться вместе с любыми другими данными, которые приложение сохраняет локально.

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();
    }
}
Кевин Паркер
источник
Если вы хотите отслеживать установки приложений, это идеально. Отслеживание устройств, однако, намного сложнее, и, похоже, не существует полностью герметичного решения.
Лука Спиллер
Как насчет рутированных устройств? Они могут легко изменить этот идентификатор установки, верно?
Tasomaniac
Абсолютно. Root может изменить идентификатор установки. Вы можете проверить наличие root с помощью этого блока кода: stackoverflow.com/questions/1101380/…
Кевин Паркер
если мы сбросим фабрику, файл будет удален?
Джамшид
Если вы сбросите настройки и удалите или отформатируете раздел / data, UUID будет другим.
Кевин Паркер
12

Добавьте код ниже в файл класса:

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

Добавьте в AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Android
источник
10

Уникальный идентификатор устройства Android OS как String с использованием TelephonyManagerи ANDROID_ID, получается:

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

Но я настоятельно рекомендую метод, предложенный Google, см. Определение установок приложений .

Jorgesys
источник
9

Существует много разных подходов к ANDROID_IDрешению этих проблем ( nullиногда это происходит, когда устройства определенной модели всегда возвращают один и тот же идентификатор) с плюсами и минусами:

  • Реализация собственного алгоритма генерации идентификатора (на основе свойств устройства, которые должны быть статическими и не изменятся -> кто знает)
  • Злоупотребление другими идентификаторами, такими как IMEI , серийный номер, Wi-Fi / Bluetooth-MAC-адрес (они не будут существовать на всех устройствах, или потребуются дополнительные разрешения)

Я сам предпочитаю использовать существующую реализацию OpenUDID (см. Https://github.com/ylechelle/OpenUDID ) для Android (см. Https://github.com/vieux/OpenUDID ). Он легко интегрируется и использует ANDROID_IDзапасные варианты с этими проблемами, упомянутыми выше.

Андреас Клёбер
источник
8

Как насчет IMEI . Это уникально для Android или других мобильных устройств.

Эльзо Валуги
источник
9
Не для моих планшетов, у которых нет IMEI, поскольку они не подключаются к моему мобильному оператору.
Брилл Паппин
2
Не говоря уже о устройствах CDMA, которые имеют ESN вместо IMEI.
Дэвид Дарил
@ Дэвид Учитывая, есть ли CDMA с Android?
Эльзо Валуги
1
Это только сделает это, это - телефон :), Планшет не может.
Брилл Паппин
3
@ElzoValugi Уже "этот день" и до сих пор не у всех планшетов есть SIM-карты.
Мэтью Кирос
8

Вот как я генерирую уникальный идентификатор:

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
источник
если мы будем использовать ReadPhoneState в версии 6.0 с запросом разрешения во время выполнения
Harsha
8

Мои два цента - NB это для уникального идентификатора устройства (err) - не установочный, как обсуждалось в блоге разработчиков Android .

Следует отметить, что решение, предоставляемое @emmby, использует идентификатор каждого приложения, поскольку SharedPreferences не синхронизируются между процессами (см. Здесь и здесь ). Так что я вообще этого избежал.

Вместо этого я инкапсулировал различные стратегии получения идентификатора (устройства) в перечислении - изменение порядка перечисления констант влияет на приоритет различных способов получения идентификатора. Возвращается первый ненулевой идентификатор или генерируется исключение (в соответствии с хорошими практиками Java, не предусматривающими нулевого значения). Так, например, сначала у меня есть TELEPHONY, но хорошим вариантом по умолчанию будет бета-версия 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
источник
8

Здесь более 30 ответов, и некоторые из них одинаковы, а некоторые уникальны. Этот ответ основан на нескольких из этих ответов. Одним из них является ответ @Lenn Dolling.

Он объединяет 3 идентификатора и создает 32-значную шестнадцатеричную строку. Это сработало очень хорошо для меня.

3 идентификаторов:
Псевдо-ID - Он генерируется на основе спецификации физического устройства
ANDROID_ID - Settings.Secure.ANDROID_ID
адреса Bluetooth - Bluetooth - адаптер адрес

Он вернет что-то вроде этого: 551F27C060712A72730B0A0F734064B1

Примечание. Вы всегда можете добавить дополнительные идентификаторы в longIdстроку. Например, Серийный №. адрес адаптера Wi-Fi. IMEI. Таким образом, вы делаете его более уникальным для каждого устройства.

@SuppressWarnings("deprecation")
@SuppressLint("HardwareIds")
public static String generateDeviceIdentifier(Context context) {

        String pseudoId = "35" +
                Build.BOARD.length() % 10 +
                Build.BRAND.length() % 10 +
                Build.CPU_ABI.length() % 10 +
                Build.DEVICE.length() % 10 +
                Build.DISPLAY.length() % 10 +
                Build.HOST.length() % 10 +
                Build.ID.length() % 10 +
                Build.MANUFACTURER.length() % 10 +
                Build.MODEL.length() % 10 +
                Build.PRODUCT.length() % 10 +
                Build.TAGS.length() % 10 +
                Build.TYPE.length() % 10 +
                Build.USER.length() % 10;

        String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);

        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        String btId = "";

        if (bluetoothAdapter != null) {
            btId = bluetoothAdapter.getAddress();
        }

        String longId = pseudoId + androidId + btId;

        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(longId.getBytes(), 0, longId.length());

            // get md5 bytes
            byte md5Bytes[] = messageDigest.digest();

            // creating a hex string
            String identifier = "";

            for (byte md5Byte : md5Bytes) {
                int b = (0xFF & md5Byte);

                // if it is a single digit, make sure it have 0 in front (proper padding)
                if (b <= 0xF) {
                    identifier += "0";
                }

                // add number to string
                identifier += Integer.toHexString(b);
            }

            // hex string to uppercase
            identifier = identifier.toUpperCase();
            return identifier;
        } catch (Exception e) {
            Log.e("TAG", e.toString());
        }
        return "";
}
ᴛʜᴇᴘᴀᴛᴇʟ
источник
1
Добавление UUID к longIdи сохранение его в файле сделает его самым уникальным идентификатором:String uuid = UUID.randomUUID().toString();
Mousa Alfhaily
1
Если все остальное терпит неудачу, если пользователь имеет более низкий, чем API 9 (ниже, чем Gingerbread), сбросил свой телефон или «Secure.ANDROID_ID». если возвращает 'null', то просто возвращенный идентификатор будет основан исключительно на информации об их Android-устройстве. Это где столкновения могут произойти. Старайтесь не использовать DISPLAY, HOST или ID - эти элементы могут измениться. Если есть столкновения, будут перекрывающиеся данные. Источник: gist.github.com/pedja1/fe69e8a80ed505500caa
Муса Альфайли
Если мы попытаемся получить уникальный номер по этой строке кода, можем ли мы сказать, что это уникальный уникальный идентификатор, и он никогда не будет конфликтовать с любым другим устройством?
ниндзя
1
@Ninja Так как MAC-адрес BLE уникален, то сгенерированный идентификатор всегда будет уникальным. Однако, если вы действительно хотите быть уверенным, я бы предложил добавить UUID в longId. Измените эту строку следующим образом: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();Это гарантирует, что сгенерированный идентификатор будет уникальным.
ᴛʜᴇᴘᴀᴛᴇʟ
@ ᴛʜᴇᴘᴀᴛᴇʟ Большое спасибо за огромную помощь. На самом деле мое приложение очень конфиденциальные данные, поэтому мне нужно быть уверенным в этом, поэтому я просто подтверждаю это.
ниндзя
7

Другой способ - использовать /sys/class/android_usb/android0/iSerialприложение без каких-либо разрешений.

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

Чтобы сделать это в Java, достаточно использовать FileInputStream, чтобы открыть файл iSerial и прочитать символы. Просто убедитесь, что вы поместили его в обработчик исключений, потому что не у всех устройств есть этот файл.

По крайней мере, следующие устройства имеют этот файл для чтения:

  • Galaxy Nexus
  • Nexus S
  • Motorola Xoom 3G
  • Toshiba AT300
  • HTC One V
  • Мини МК802
  • Samsung Galaxy S II

Вы также можете увидеть мой пост в блоге « Утечка серийного номера оборудования Android» в непривилегированных приложениях, где я обсуждаю, какие другие файлы доступны для информации.

insitusec
источник
Я только что прочитал твой пост в блоге. Я считаю, что это не уникально: Build.SERIAL также доступен без каких-либо разрешений и является (теоретически) уникальным серийным номером оборудования.
Том
1
Вы правы. Это еще один способ отслеживания вашего устройства, и, как вы сказали, оба эти способа не требуют никаких разрешений для приложений.
insitusec
7

TelephonyManger.getDeviceId () Возвращает уникальный идентификатор устройства, например, IMEI для GSM и MEID или ESN для телефонов CDMA.

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

Но я рекомендую использовать:

Settings.Secure.ANDROID_ID, который возвращает идентификатор Android в виде уникальной 64-разрядной шестнадцатеричной строки.

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

Иногда TelephonyManger.getDeviceId () возвращает null, поэтому для обеспечения уникального идентификатора вы будете использовать этот метод:

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
источник
Недавно я обнаружил, что клиентское устройство типа SM-G928F / Galaxy S6 Edge + предоставляет только 15 вместо 16 шестнадцатеричных цифр для идентификатора Android.
Хольгер Якобс
7

Для аппаратного распознавания определенного устройства Android вы можете проверить MAC-адреса.

Вы можете сделать это так:

в AndroidManifest.xml

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

теперь в вашем коде:

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

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

В каждом устройстве Android есть, по крайней мере, интерфейс "wlan0", который является чипом WI-FI. Этот код работает, даже когда WI-FI не включен.

PS Это множество других интерфейсов, которые вы получите из списка, содержащего MACS, но это может измениться между телефонами.

Ilan.b
источник
7

Я использую следующий код, чтобы получить IMEIили использовать Secure. ANDROID_IDв качестве альтернативы, когда устройство не имеет возможностей телефона:

String identifier = null;
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE));
if (tm != null)
      identifier = tm.getDeviceId();
if (identifier == null || identifier .length() == 0)
      identifier = Secure.getString(activity.getContentResolver(),Secure.ANDROID_ID);
Асаф Пинхасси
источник
7

Более конкретно Settings.Secure.ANDROID_ID. Это 64-разрядная величина, которая генерируется и сохраняется при первой загрузке устройства. Сбрасывается при стирании устройства.

ANDROID_IDкажется хорошим выбором для уникального идентификатора устройства. Есть недостатки: во-первых, это не на 100% надежно в выпусках Android до 2.2. (“Froyo”).Кроме того, в популярном телефоне от одного крупного производителя была обнаружена, по крайней мере, одна широко распространенная ошибка, где каждый экземпляр имеет одинаковый ANDROID_ID.

mumu123
источник
1
Этот ответ является копией пасты из старого блога Google android-developers.googleblog.com/2011/03/… . Значит, ошибка уже устранена?
Сергей
7

Чтобы понять доступные уникальные идентификаторы в устройствах Android. Используйте это официальное руководство.

Лучшие практики для уникальных идентификаторов:

IMEI, адреса Mac, идентификатор экземпляра, GUID, SSAID, идентификатор рекламы, API сети безопасности для проверки устройств.

https://developer.android.com/training/articles/user-data-ids

Вахид Назир
источник
6

Идентификатор Google Instance

Выпущено в I / O 2015; на Android требуются игровые сервисы 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

Похоже, что Google намеревается использовать этот идентификатор для идентификации установок через Android, Chrome и iOS.

Он идентифицирует установку, а не устройство, но опять же, ANDROID_ID (который является принятым ответом) теперь больше не идентифицирует устройства. Во время выполнения ARC для каждой установки создается новый ANDROID_ID ( подробности здесь ), как и этот новый идентификатор экземпляра. Кроме того, я думаю, что большинство из нас на самом деле ищут определение установок (не устройств).

Преимущества экземпляра ID

Мне кажется, что Google намеревается использовать его для этой цели (идентифицируя ваши установки), он кроссплатформенный и может использоваться для ряда других целей (см. Ссылки выше).

Если вы используете GCM, то в конечном итоге вам понадобится использовать этот идентификатор экземпляра, потому что он нужен вам для получения токена GCM (который заменяет старый регистрационный идентификатор GCM).

Недостатки / проблемы

В текущей реализации (GPS 7.5) идентификатор экземпляра извлекается с сервера, когда ваше приложение запрашивает его. Это означает, что вышеуказанный вызов является блокирующим вызовом - в моем ненаучном тестировании это занимает 1-3 секунды, если устройство подключено к сети, и 0,5-1,0 секунды, если устройство находится в автономном режиме (предположительно, это то, сколько времени он ожидает, прежде чем сдаться и сгенерировать случайный идентификатор). Это было протестировано в Северной Америке на Nexus 5 с Android 5.1.1 и GPS 7.5.

Если вы используете идентификатор для целей, для которых они предназначены - например, аутентификация приложения, идентификация приложения, GCM - я думаю, что это 1-3 секунды, что может быть неприятно (в зависимости от вашего приложения, конечно).

Том
источник
1
Еще одним существенным недостатком instanceID является то, что для вас будет создан новый instanceID, если пользователь очистит данные приложения.
idanakav
Интересно, но я не думаю, что это действительно меняет потенциальные варианты использования: идентификатор экземпляра, как android_id, не подходит для идентификации устройства. Таким образом, ваш сервер увидит, как пользователь удаляет данные, как пользователь удаляет и переустанавливает ваше приложение - что не является необоснованным.
Том