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

2449

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

Показать ещё 2 комментария
Теги:
uniqueidentifier

45 ответов

1707
Лучший ответ

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

import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID); 
  • 419
    Известно, что иногда оно пустое, оно задокументировано как «может измениться после сброса к заводским настройкам». Используйте на свой страх и риск, и это может быть легко изменено на рутированном телефоне.
Показать ещё 24 комментария
1030

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

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


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

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

  • Все тестируемые устройства вернули значение для TelephonyManager.getDeviceId()
  • Все GSM-устройства (все протестированные с помощью SIM-карты) вернули значение для TelephonyManager.getSimSerialNumber()
  • Все устройства CDMA вернули значение null для getSimSerialNumber() (как ожидалось)
  • Все устройства с добавленной учетной записью Google вернули значение для ANDROID_ID
  • Все устройства CDMA вернули одно и то же значение (или вывод того же значения) для ANDROID_ID и TelephonyManager.getDeviceId() - если во время установки была добавлена ​​учетная запись Google.
  • У меня еще не было возможности протестировать 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 libs

import android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
  • 141
    На планшетных устройствах не будет идентификатора на основе телефонии, да?
  • 21
    Поэтому я сказал, что большинство из них не будет работать постоянно :) Я пока не вижу ответа на этот вопрос, который был бы надежным для всех устройств, всех типов устройств и всех аппаратных конфигураций. Вот почему этот вопрос здесь для начала. Совершенно очевидно, что для этого не существует универсального решения. Отдельные производители устройств могут иметь серийные номера устройств, но они не предоставляются нам для использования, и это не является обязательным требованием. Таким образом, мы остаемся с тем, что нам доступно.
Показать ещё 30 комментариев
412

Последнее обновление: 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 - Аппаратное обеспечение (только телефоны, требуется 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/

  • 0
    Но не изменится ли класс Build при обновлении ОС? Особенно, если API обновился? Если так, как вы можете гарантировать, что это уникально? (Говоря о методе, который вы написали)
  • 0
    Я все еще настраиваюсь и иду на занятия, а также тестирование прямо сейчас; однако я позаботился о том, чтобы не включать Build.DISPLAY, Build.HOST или Build.ID, что определенно изменится при обновлении OTA. Но, как я уже сказал, API => 9 практически гарантирован. Мы должны использовать только класс Android.SECURE_ID и / или Build.
Показать ещё 28 комментариев
338

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

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

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

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

Обратите внимание, что для устройств, которые должны отступать от идентификатора устройства, уникальный идентификатор WILL сохраняется в factory сбрасывается. Это то, о чем нужно знать. Если вам нужно убедиться, что factory reset будет reset вашим уникальным идентификатором, вы можете захотеть вернуться обратно к случайному 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 broken, in which case
                        // fallback on deviceId,
                        // unless it 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 persisted
     * to SharedPreferences if getDeviceID() does not return a usable value.
     * 
     * In some rare circumstances, this ID may change. In particular, if the
     * device is factory reset a new device ID may be generated. In addition, if
     * a user upgrades their phone from certain buggy implementations of Android
     * 2.2 to a newer, non-buggy version of Android, the device ID may change.
     * Or, if a user uninstalls your app on a device that has neither a proper
     * Android ID nor a Device ID, this ID may change on reinstallation.
     * 
     * Note that if the code falls back on using TelephonyManager.getDeviceId(),
     * the resulting ID will NOT change after a factory reset. Something to be
     * aware of.
     * 
     * Works around a bug in Android 2.2 for many devices when using ANDROID_ID
     * directly.
     * 
     * @see http://code.google.com/p/android/issues/detail?id=10603
     * 
     * @return a UUID that may be used to uniquely identify your device for most
     *         purposes.
     */
    public UUID getDeviceUuid() {
        return uuid;
    }
}
  • 6
    Разве вы не должны хэшировать различные идентификаторы, чтобы они все были одинакового размера? Кроме того, вы должны хешировать идентификатор устройства, чтобы случайно не раскрыть личную информацию.
  • 0
    Кроме того, устройство, конфигурация которого вызывает использование идентификатора устройства , будет привязывать идентификатор к устройству , а не обязательно к пользователю. Этот идентификатор будет действовать после сброса к заводским настройкам, что может быть потенциально плохо. Может быть, идентификатор устройства может быть хеширован с чем-то, что сообщает время последнего сброса к заводским настройкам?
Показать ещё 14 комментариев
171

Вот код, который 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;
}

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

  • 0
    Я использовал метод @Lenn Dolling с текущим временем, добавленным для уникального идентификатора. Но это кажется более простым и надежным способом. Спасибо Рето Майер и Энтони Нолан
  • 0
    Это здорово, но как насчет рутованных устройств? Они могут получить доступ к этому и легко изменить UID на другой.
Показать ещё 6 комментариев
99

Также вы можете рассмотреть 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
  • 1
    как насчет устройств без Wi-Fi?
Показать ещё 13 комментариев
84

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

Он охватывает пять разных типов идентификаторов:

  • IMEI (только для устройств Android с использованием телефона; требуется android.permission.READ_PHONE_STATE)
  • Псевдо-уникальный идентификатор (для всех устройств Android)
  • Android-идентификатор (может быть нулевым, может быть изменен при factory reset, может быть изменен на корневом телефоне)
  • MAC-адрес WLAN (требуется android.permission.ACCESS_WIFI_STATE)
  • Строка MAC MAC MAC (устройства с Bluetooth, требуется android.permission.BLUETOOTH)
  • 1
    Упущенный важный момент (здесь и в статье): вы не можете получить WLAN или BT MAC, если они не включены! В противном случае я думаю, что WLAN MAC будет идеальным идентификатором. У вас нет гарантии, что пользователь когда-либо включит свой Wi-Fi, и я не думаю, что «уместно» включать его самостоятельно.
  • 1
    @ Ты ошибаешься. Вы все еще можете читать WLAN или BT MAC, даже если они выключены. Тем не менее, нет никаких гарантий, что устройство имеет доступные модули WLAN или BT.
Показать ещё 3 комментария
47

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

  • 3
    И ключевой момент этого аргумента в том, что если вы пытаетесь получить уникальный идентификатор из аппаратного обеспечения, вы, вероятно, делаете ошибку.
  • 3
    И если вы позволяете сбросить блокировку устройства с помощью заводского сброса, модель пробной версии работает так же, как и мертвая.
36

В Google I/O Reto 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>

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

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 теперь будет постоянным во всех установках, даже если пользователь перемещает устройство.

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

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

  • 5
    Не приводит ли это к нескольким устройствам с одинаковым идентификатором, когда у пользователя есть несколько устройств? Например, планшет и телефон.
  • 0
    Минимальная цель 8 требуется для этого.
Показать ещё 3 комментария
35

Следующий код возвращает серийный номер устройства с помощью скрытого 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) {

}
  • 0
    Я только что прочитал на XDA ro.serialno что ro.serialno используется для создания Settings.Secure.ANDROID_ID . Таким образом, они в основном разные представления одного и того же значения.
  • 0
    @Martin: но, возможно, серийный номер не меняется при перезагрузке устройства. Не так ли? Просто новое значение для ANDROID_ID является производным от него.
Показать ещё 3 комментария
34

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

Псевдо-уникальный идентификатор, который работает на всех устройствах 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 или полную строку хеша.
  • 1
    Я обнаружил, что Build.CPU_ABI и Build.MANUFACTURER присутствуют не во всех версиях .. У меня возникали грубые ошибки сборки при работе с <2.2 :)
Показать ещё 9 комментариев
22

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

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

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

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

  • 2
    К сожалению, это «неизвестно».
15

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

Использование:

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

Оказывается, несмотря на то, что мой планшет Viewsonic G сообщает идентификатор устройства, который не является Null, каждый отдельный G-планшет сообщает о том же номере.

Делает интересным воспроизведение "Pocket Empires", которое дает вам мгновенный доступ к учетной записи пользователя на основе "уникального" DeviceID.

В моем устройстве нет сотовой радиостанции.

  • 0
    Какой идентификатор? это случайно 9774d56d682e549c ?
  • 0
    Ого, это было так давно, я давно бросил этот планшет. Не могу сказать.
14

Подробные инструкции о том, как получить уникальный идентификатор для каждого 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();
    }
}
  • 0
    Если вы хотите отслеживать установки приложений, это идеально. Отслеживание устройств, однако, намного сложнее, и, похоже, не существует полностью герметичного решения.
  • 0
    Как насчет рутированных устройств? Они могут легко изменить этот идентификатор установки, верно?
Показать ещё 3 комментария
10

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

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" />
9

Добавляя к тому, что говорили другие, в официальной документации на Android есть новые рекомендации по использованию уникальных идентификаторов: http://developer.android.com/training/articles/user-data-ids.html

9

Уникальный идентификатор устройства устройства Android OS в виде строки, используя 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, см. Идентификация инсталляций приложений.

8

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

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

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

7

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

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

Вместо этого я инкапсулировал различные стратегии для получения идентификатора (устройства) в перечислении - изменение порядка констант перечисления влияет на приоритет различных способов получения идентификатора. Возвращается первый ненулевой идентификатор или генерируется исключение (согласно хорошей практике Java, не дающей значения null). Так, например, у меня сначала ТЕЛЕФОН, но хорошим выбором по умолчанию будет 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);
        }
    }
}
7

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

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;
}
  • 0
    если мы будем использовать ReadPhoneState в версии 6.0 с запросом разрешения во время выполнения
7

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

  • 8
    Не для моих планшетов, у которых нет IMEI, поскольку они не подключаются к моему мобильному оператору.
  • 1
    Не говоря уже об устройствах CDMA, которые имеют ESN вместо IMEI.
Показать ещё 7 комментариев
6

В частности, Settings.Secure.ANDROID_ID. Это 64-разрядное количество, которое генерируется и сохраняется при первом загрузке устройства. При стирании устройства reset.

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

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

Для аппаратного распознавания определенного устройства 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 не включен.

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

6

Я использую следующий код, чтобы получить 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);
6

Другой способ - использовать /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
  • Mini MK802
  • Samsung Galaxy S II

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

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

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

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

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

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

Примечание. Вы всегда можете добавить больше идентификаторов в строку longId. Например, Serial #. беспроводной адаптер адрес. 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();
  • 1
    Если все остальное терпит неудачу, если пользователь имеет более низкий, чем API 9 (ниже, чем Gingerbread), сбросил свой телефон или «Secure.ANDROID_ID». если возвращает 'null', то просто возвращенный идентификатор будет основан исключительно на информации об их Android-устройстве. Это где столкновения могут произойти. Старайтесь не использовать DISPLAY, HOST или ID - эти элементы могут измениться. Если есть столкновения, будут перекрывающиеся данные. Источник: gist.github.com/pedja1/fe69e8a80ed505500caa
5

Идентификатор MAC-устройства Android также является уникальным идентификатором, он не изменится, если мы отформатируем само устройство, используя следующий код, чтобы получить mac id

WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
String address = info.getMacAddress();

Также не забудьте добавить соответствующие разрешения в свой AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  • 2
    К сожалению, это не будет работать, если нет текущего соединения WiFi. Из документов (выделение выделено): «Вернуть динамическую информацию о текущем соединении Wi-Fi, если оно активно ».
  • 1
    Также, предоставив root-доступ на устройстве, можно подменить MAC-адрес
5

Идентификатор экземпляра Google

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

Преимущества идентификатора экземпляра

Мне кажется, что 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, если пользователь очистит данные приложения.
  • 0
    Интересно, но я не думаю, что это действительно меняет потенциальные варианты использования: идентификатор экземпляра, как android_id, не подходит для идентификации устройства. Таким образом, ваш сервер будет видеть, как пользователь удаляет данные, как пользователь удаляет и переустанавливает ваше приложение - что не является необоснованным.
5

Я нашел библиотеку в Github, которая, похоже, объединяет несколько подходов, обсуждаемых в этом потоке: https://github.com/thomashaertel/android-device-identification

Не пробовал, но, возможно, это помогает.

4

1.Вы можете получить доступ к идентификатору устройства Android с помощью сервиса gms. см. приведенный ниже пример,

private DeviceInfoProvider mDeviceInfo = new DeviceInfoProvider(Context)
String mDeviceId = DeviceInfoProvider.getDeviceId(Context);
Log.d("DEVICE_ID" , mDeviceId);

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

import android.telephony.TelephonyManager;
import android.content.Context;
// ...
TelephonyManager telephonyManager;
telephonyManager = (TelephonyManager) getSystemService(Context.
                TELEPHONY_SERVICE);
/*
* getDeviceId() returns the unique device ID.
* For example,the IMEI for GSM and the MEID or ESN for CDMA phones.
*/
String deviceId = telephonyManager.getDeviceId();
/*
* getSubscriberId() returns the unique subscriber ID,
*/
String subscriberId = telephonyManager.getSubscriberId();

Это требует android.permission.READ_PHONE_STATE для вашего пользователя, что может быть трудно оправдать в зависимости от типа приложения, которое вы сделали.

  1. Устройства без служб телефонии, такие как планшеты, должны сообщать уникальный идентификатор устройства, который доступен через android.os.Build.SERIAL начиная с Android 2.3 Gingerbread. Некоторые телефоны, имеющие услуги телефонии, также могут определять серийный номер. Как не у всех устройств Android есть серийный номер, это решение не является надежным.

  2. При первой загрузке устройства генерируется и сохраняется случайное значение. Это значение доступно через Settings.Secure.ANDROID_ID. Это 64-битное число, которое должно оставаться постоянным в течение всего срока службы устройства. ANDROID_ID кажется хорошим выбором для уникального идентификатора устройства, потому что он доступен для смартфонов и планшетов. Чтобы получить значение, вы можете использовать следующий код,

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

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

  1. Используйте UUID. Поскольку требование для большинства приложений состоит в том, чтобы идентифицировать конкретную установку, а не физическое устройство, хорошим решением является получение уникального идентификатора для пользователя, если использовать класс UUID. Следующее решение было представлено Рето Мейером из Google в презентации ввода/вывода Google,

SharedPreferences sharedPrefs = context.getSharedPreferences( PREF_UNIQUE_ID, Context.MODE_PRIVATE); uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);

4

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

В Google теперь есть рекламный идентификатор.
Это также можно использовать, но обратите внимание, что:

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

и

позволяет пользователям reset их идентификатор или отказаться от рекламы на основе интересов в приложениях Google Play.

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

Подробнее @develper.android

Код копирования вставить здесь

НТН

  • 0
    Его можно сбросить: идентификатор рекламы - это уникальный, но сбрасываемый пользователем строковый идентификатор, который позволяет рекламным сетям и другим приложениям анонимно идентифицировать пользователя.
  • 1
    (@JaredBurrows да, это упоминается в посте ...)
3

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

@SuppressLint("HardwareIds")
private String getDeviceID() {
    deviceId = Settings.Secure.getString(getApplicationContext().getContentResolver(),
                    Settings.Secure.ANDROID_ID);
    return deviceId;
}
  • 0
    Android вносит некоторые изменения в: Settings.Secure.ANDROID_ID; В Android 8.0 (уровень API 26) и более поздних версиях платформы - 64-разрядное число (в виде шестнадцатеричной строки), уникальное для каждой комбинации ключа подписи приложения, пользователя и устройства. Это означает, что Settings.Secure.ANDROID_ID теперь возвращает идентификаторы, которые являются уникальными для комбинации приложения / устройства, что делает вещи более безопасными для пользователя.
2

Я столкнулся с этим вопросом несколько лет назад и научился реализовывать обобщенное решение, основанное на различных ответах.

Я использовал обобщенное решение в течение нескольких лет в реальном продукте. Пока это хорошо. Здесь фрагмент кода, основанный на различных предоставленных ответах.

Примечание. getEmail будет возвращать null большую часть времени, так как мы не запрашивали разрешение явно.

private static UniqueId getUniqueId() {
    MyApplication app = MyApplication.instance();

    // Our prefered method of obtaining unique id in the following order.
    // (1) Advertising id
    // (2) Email
    // (2) ANDROID_ID
    // (3) Instance ID - new id value, when reinstall the app.

    ////////////////////////////////////////////////////////////////////////////////////////////
    // ADVERTISING ID
    ////////////////////////////////////////////////////////////////////////////////////////////
    AdvertisingIdClient.Info adInfo = null;
    try {
        adInfo = AdvertisingIdClient.getAdvertisingIdInfo(app);
    } catch (IOException e) {
        Log.e(TAG, "", e);
    } catch (GooglePlayServicesNotAvailableException e) {
        Log.e(TAG, "", e);
    } catch (GooglePlayServicesRepairableException e) {
        Log.e(TAG, "", e);
    }

    if (adInfo != null) {
        String aid = adInfo.getId();
        if (!Utils.isNullOrEmpty(aid)) {
            return UniqueId.newInstance(aid, UniqueId.Type.aid);
        }
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    // EMAIL
    ////////////////////////////////////////////////////////////////////////////////////////////
    final String email = Utils.getEmail();
    if (!Utils.isNullOrEmpty(email)) {
        return UniqueId.newInstance(email, UniqueId.Type.eid);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    // ANDROID ID
    ////////////////////////////////////////////////////////////////////////////////////////////
    final String sid = Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID);
    if (!Utils.isNullOrEmpty(sid)) {
        return UniqueId.newInstance(sid, UniqueId.Type.sid);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////
    // INSTANCE ID
    ////////////////////////////////////////////////////////////////////////////////////////////
    final String iid = com.google.android.gms.iid.InstanceID.getInstance(MyApplication.instance()).getId();
    if (!Utils.isNullOrEmpty(iid)) {
        return UniqueId.newInstance(iid, UniqueId.Type.iid);
    }

    return null;
}

public final class UniqueId implements Parcelable {
    public enum Type implements Parcelable {
        aid,
        sid,
        iid,
        eid;

        ////////////////////////////////////////////////////////////////////////////
        // Handling Parcelable nicely.

        public static final Parcelable.Creator<Type> CREATOR = new Parcelable.Creator<Type>() {
            public Type createFromParcel(Parcel in) {
                return Type.valueOf(in.readString());
            }

            public Type[] newArray(int size) {
                return new Type[size];
            }
        };

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel parcel, int flags) {
            parcel.writeString(this.name());
        }

        // Handling Parcelable nicely.
        ////////////////////////////////////////////////////////////////////////////
    }

    public static boolean isValid(UniqueId uniqueId) {
        if (uniqueId == null) {
            return false;
        }
        return uniqueId.isValid();
    }

    private boolean isValid() {
        return !org.yccheok.jstock.gui.Utils.isNullOrEmpty(id) && type != null;
    }

    private UniqueId(String id, Type type) {
        if (org.yccheok.jstock.gui.Utils.isNullOrEmpty(id) || type == null) {
            throw new java.lang.IllegalArgumentException();
        }
        this.id = id;
        this.type = type;
    }

    public static UniqueId newInstance(String id, Type type) {
        return new UniqueId(id, type);
    }

    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + id.hashCode();
        result = 31 * result + type.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }

        if (!(o instanceof UniqueId)) {
            return false;
        }

        UniqueId uniqueId = (UniqueId)o;
        return this.id.equals(uniqueId.id) && this.type == uniqueId.type;
    }

    @Override
    public String toString() {
        return type + ":" + id;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Handling Parcelable nicely.

    public static final Parcelable.Creator<UniqueId> CREATOR = new Parcelable.Creator<UniqueId>() {
        public UniqueId createFromParcel(Parcel in) {
            return new UniqueId(in);
        }

        public UniqueId[] newArray(int size) {
            return new UniqueId[size];
        }
    };

    private UniqueId(Parcel in) {
        this.id = in.readString();
        this.type = in.readParcelable(Type.class.getClassLoader());
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(this.id);
        parcel.writeParcelable(this.type, 0);
    }

    // Handling Parcelable nicely.
    ////////////////////////////////////////////////////////////////////////////

    public final String id;
    public final Type type;
}

public static String getEmail() {
    Pattern emailPattern = Patterns.EMAIL_ADDRESS; // API level 8+
    AccountManager accountManager = AccountManager.get(MyApplication.instance());
    Account[] accounts = accountManager.getAccountsByType("com.google");
    for (Account account : accounts) {
        if (emailPattern.matcher(account.name).matches()) {
            String possibleEmail = account.name;
            return possibleEmail;
        }
    }

    accounts = accountManager.getAccounts();
    for (Account account : accounts) {
        if (emailPattern.matcher(account.name).matches()) {
            String possibleEmail = account.name;
            return possibleEmail;
        }
    }

    return null;
} 
1

Серийный номер - уникальный идентификатор устройства, доступный через android.os.Build.SERIAL.

public static String getSerial() {
    String serial = "";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
        serial = Build.getSerial();
    }else{ 
        serial = Build.SERIAL;    
    }
    return serial;
}

Перед вызовом getSerial() убедитесь, что у вас есть READ_PHONE_STATE.

ПРИМЕЧАНИЕ: - Недоступно для устройств без телефонии (например, только для Wi-Fi).

1

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

чтобы получить IMEI (международный идентификатор мобильного оборудования)

public String getIMEI(Activity activity) {
    TelephonyManager telephonyManager = (TelephonyManager) activity
            .getSystemService(Context.TELEPHONY_SERVICE);
    return telephonyManager.getDeviceId();
}

чтобы получить уникальный идентификатор устройства

public String getDeviceUniqueID(Activity activity){
    String device_unique_id = Secure.getString(activity.getContentResolver(),
            Secure.ANDROID_ID);
    return device_unique_id;
}
1

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

https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html

tl; dr Serial потребует разрешения для телефона и Android ID будет изменяться для разных приложений на основе их имени пакета и подписи.

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

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

0

Чтобы включить Android 9, у меня есть только одна идея, которая все еще может работать, которая (вероятно) не нарушает никаких условий, требует разрешений и работает в установках и приложениях.

Снятие отпечатков пальцев с участием сервера должно быть в состоянии однозначно идентифицировать устройство. Комбинация информации об оборудовании + установленных приложений и времени установки должна помочь. Время первой установки не изменится, если приложение не будет удалено и установлено снова. Но это должно быть сделано для всех приложений на устройстве, чтобы не иметь возможности идентифицировать устройство (т.е. после сброса к заводским настройкам).

Вот как я бы это сделал:

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

Вот как вы извлекаете все приложения из Android (никаких разрешений не требуется):

final PackageManager pm = application.getPackageManager();
List<ApplicationInfo> packages = 
pm.getInstalledApplications(PackageManager.GET_META_DATA);

for (ApplicationInfo packageInfo : packages) {
    try {
        Log.d(TAG, "Installed package :" + packageInfo.packageName);
        Log.d(TAG, "Installed :" + pm.getPackageInfo(packageInfo.packageName, 0).firstInstallTime);
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
}
  1. Вы можете захотеть сделать хэш каждой комбинации имени пакета и метки времени установки перед отправкой на сервер, так как это может или не может быть связано с тем, что suer установил на устройстве.
  2. Некоторые приложения (многие на самом деле) являются системными приложениями. Скорее всего, они будут иметь одинаковую метку времени установки, соответствующую последнему обновлению системы после сброса к заводским настройкам. Поскольку они имеют одинаковую метку времени установки, они не могут быть установлены пользователем и могут быть отфильтрованы.
  3. Отправьте информацию на сервер и дайте ему возможность найти ближайшее совпадение среди ранее сохраненных данных. Вам необходимо установить порог при сравнении с ранее сохраненной информацией об устройстве, поскольку приложения устанавливаются и удаляются. Но я предполагаю, что этот порог может быть очень низким, поскольку любое имя пакета и только одна комбинация меток времени установки будут довольно уникальными для устройства, а приложения не так часто устанавливаются и удаляются. Наличие нескольких приложений просто увеличивает вероятность быть уникальным.
  4. Верните сгенерированный уникальный идентификатор для совпадения или сгенерируйте уникальный идентификатор, сохраните информацию об устройстве и верните этот новый идентификатор.

NB: Это не проверенный и не проверенный метод! Я уверен, что это сработает, но я также уверен, что, если это зацепит, они так или иначе закроют его.

0

Для полноты, вот как вы можете получить Id в Xamarin.Android и С#:

var id = Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId);

Или, если вы не в Activity:

var id = Settings.Secure.GetString(context.ContentResolver, Settings.Secure.AndroidId);

Где context передается в контексте.

0

Использовать FCM сгенерированный токен как уникальный идентификатор устройства Android, потому что использование IMEI в качестве идентификатора устройства потребует разрешения, а использование Secure.ANDROID_ID может иметь некоторую проблему в корневом устройстве, поскольку я нашел здесь

поэтому всякий раз, когда токен FCM обновляется, вы должны его обновить

0
String SERIAL_NUMER = Build.SERIAL;

Возвращает SERIAL NUMBER как строку, уникальную для каждого устройства.

0

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

  • 4
    Идея состоит в том, чтобы иметь идентификатор, который выживет после удаления и повторной установки приложения. Сохранение идентификатора в базе данных не помогает, потому что база данных удаляется при удалении приложения.
-2

Проверить SystemInfo.deviceUniqueIdentifier

Документация: http://docs.unity3d.com/Documentation/ScriptReference/SystemInfo-deviceUniqueIdentifier.html

Уникальный идентификатор устройства. Он гарантированно будет уникальным для каждого устройства (только для чтения).

iOS: на устройствах pre-iOS7 он вернет хэш MAC-адреса. На устройствах iOS7 это будет идентификатор UIDevice forVendor, или, если это произойдет по какой-либо причине, ASIdentifierManager advertisingIdentifier.

-8

Я использую эту функцию:

public String GetUniqueHardwareID(){
  return Secure.getString(getApplicationContext().getContentResolver(),
          Secure.ANDROID_ID);
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню