Чи існує унікальний ідентифікатор пристрою Android?


2751

Чи мають пристрої Android унікальний ідентифікатор, і якщо так, то який простий спосіб отримати доступ до нього за допомогою Java?


38
Якщо ви використовуєте, ANDROID_IDне забудьте прочитати цю відповідь і цю помилку .
Dheeraj Vepakomma

Якщо вам потрібно рішення в 2020 році, вам слід прочитати андроїд-ідентифікатори в 2020 році
Микита Куртин

Відповіді:


2024

Settings.Secure#ANDROID_IDповертає Android ID як унікальний для кожного користувача 64-бітний шістнадцятковий рядок.

import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID); 

476
Іноді це, як відомо, нульово, це документально підтверджується тим, що "може змінитися після скидання заводу". Використовуйте на свій страх і ризик, і це можна легко змінити на вкоріненому телефоні.
Сева Алексєєва


18
Я думаю, що нам потрібно бути обережними щодо використання ANDROID_ID в хеші в першій відповіді про те, тому що він може не бути встановлений під час першого запуску програми, може бути встановлений пізніше або навіть може змінитися теоретично, отже, унікальний ідентифікатор може змінитися

46
Будьте в курсі, що у цьому рішенні є величезні обмеження: android-developers.blogspot.com/2011/03/…
emmby

35
ANDROID_ID більше не однозначно ідентифікує пристрій (як 4,2): stackoverflow.com/a/13465373/150016
Том

1145

ОНОВЛЕННЯ : Станом на останніх версіях Android, багато проблем із проблемою ANDROID_IDвирішено, і я вважаю, що цей підхід вже не потрібен. Будь ласка, подивіться на відповідь Ентоні .

Повне розкриття: в моєму додатку спочатку використовувався нижченаведений підхід, але він більше не використовує цей підхід, і тепер ми використовуємо підхід, викладений у записі Блог розробника Android, на який посилаються відповіді emmby (а саме генерування та збереження а 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
Будьте в курсі, що в цьому рішенні є величезні обмеження: android-developers.blogspot.com/2011/03/…
emmby

18
@softarn: Я вважаю, що ви маєте на увазі блог для розробників Android, до якого вже пов’язано emmby, який пояснює, що ви намагаєтеся сказати, тому, можливо, вам слід було б просто підтримати його коментар. Так чи інакше, як згадує Ембі у своїй відповіді, проблеми з інформацією в блозі все ще є. Питання задає унікальний DEVICE ідентифікатор (НЕ ідентифікатор установки), так що я НЕ згоден з вашим твердженням. У блозі робиться припущення, що те, що ви хочете , не обов’язково відстежувати на пристрої, тоді як у цьому питанні задається саме це. Я погоджуюся з блогом інакше.
Джо

438

Останнє оновлення: 2.06.15


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

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

Обладнання

  • Користувачі можуть змінити обладнання, планшетний ПК або телефон Android, тому унікальні ідентифікатори, засновані на апаратному забезпеченні, не є гарними ідеями для відслідковування користувачів
  • Для відслідковування обладнання - це чудова ідея

Програмне забезпечення

  • Користувачі можуть стерти / змінити ROM, якщо вони вкорінені
  • Ви можете відстежувати користувачів на різних платформах (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 ID - апаратне забезпечення (може бути недійсним, може змінюватися після скидання на заводські налаштування, може бути змінено на вкоріненому пристрої)

    • Оскільки він може бути "null", ми можемо перевірити "null" та змінити його значення, але це означає, що він більше не буде унікальним.
    • Якщо у вас є користувач із пристроєм скидання заводських налаштувань, значення, можливо, змінилося або змінилося на пристрої з укоріненими файлами, тому можуть бути записи дублікатів, якщо ви відстежуєте встановлення користувача.
  • MLAN-адреса WLAN - Обладнання (потреби android.permission.ACCESS_WIFI_STATE)

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

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

    • Дуже можливо, можуть містити зіткнення - Дивіться мій метод, розміщений нижче!
    • Це дозволяє вам отримати «майже унікальний» ідентифікатор від користувача, не беручи нічого приватного. Ви можете створити власний анонімний ідентифікатор, використовуючи інформацію про пристрій.

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

Маючи псевдо-унікальний ідентифікатор, ви дійсно стикаєтесь лише з тим, що можуть бути дублікати, засновані на тому, що є подібні пристрої. Ви можете налаштувати комбінований метод, щоб зробити його більш унікальним; проте деяким розробникам потрібно відстежувати встановлення пристроїв, і це дозволить зробити фокус або продуктивність на основі подібних пристроїв.

API> = 9:

Якщо їх Android-пристрій має API 9 або більше, це гарантовано унікальність через поле "Build.SERIAL".

ПАМ’ЯТАЙТЕ , ви технічно не вистачаєте лише приблизно на 0,5% користувачів, які мають API <9 . Тож ви можете зосередитись на решті: це 99,5% користувачів!

API <9:

Якщо Android-пристрій користувача нижчий за API 9; сподіваємось, вони не виконали скидання заводських налаштувань, і їх "Secure.ANDROID_ID" буде збережено або не буде "null". (див. http://developer.android.com/about/dashboards/index.html )

Якщо все інше не вдається:

Якщо все інше не вдається, якщо користувач має менший ніж API 9 (нижче Gingerbread), скинув їх пристрій або "Secure.ANDROID_ID" повертає "null", просто повернутий ідентифікатор буде виключно виходячи з інформації про їх пристрої 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();
}

Джерело / Документи:

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

Важливо:

Мається на увазі, що рекламний ідентифікатор повністю замінить існуюче використання інших ідентифікаторів для цілей реклами (наприклад, використання ANDROID_ID у налаштуваннях. Безпечно), коли сервіси Google Play доступні. Випадки, коли Служби Google Play недоступні, позначаються GooglePlayServicesNotAvailableException, що передається getAdvertisingIdInfo ().

Попередження, користувачі можуть скинути:

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

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

Ідентифікатор служби Google Player InstanceID

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


Але чи не Buildзмінився б клас після оновлення ОС? Особливо, якщо API було оновлено? Якщо так, то як ви гарантуєте, що це унікальне? (Якщо говорити про метод, який ви написали)
LuckyMe

2
Я використовував ваш метод у своєму додатку для надсилання коментарів. у мене погані новини. на жаль, PsuedoID не є унікальним повністю. мій сервер записав більше 100 для 5 ідентифікаторів і понад 30 для майже 30 ідентифікаторів. найбільш повторювані ідентифікатори - 'ffffffff-fc8f-6093-ffff-ffffd8' (запис 159) та 'ffffffff-fe99-b334-ffff-ffffef' (154 раз). також на основі часу та коментарів очевидно, що існують різні народи. загальна кількість записів до цих пір становить 10 000. будь ласка, дайте мені знати, чому це сталося. танки.
hojjat reyhane

1
Я писав це 1,5+ років тому. Я не впевнений, чому це не унікально для вас. Ви можете спробувати рекламний ідентифікатор. Якщо ні, ви можете придумати власне рішення.
Джаред Берроуз

2
сорта..Я дуже вдячний, якщо ви перейдете до питання і висловите свої думки з цього приводу
Durai Amuthan.H

1
@ user1587329 Дякую Я намагаюсь бути актуальним для всіх. Це питання є складним, коли мова йде про апаратне забезпечення та програмне забезпечення та крос-платформу.
Джаред Берроуз

340

Як зазначає Дейв Вебб, у блозі розробників Android є стаття, яка висвітлює цю проблему. Їх кращим рішенням є відстеження встановлень додатків, а не пристроїв, і це буде добре працювати в більшості випадків використання. Повідомлення в блозі покаже вам необхідний код, щоб зробити цю роботу, і я рекомендую вам перевірити його.

Однак у блозі продовжується обговорення рішень, якщо вам потрібен ідентифікатор пристрою, а не ідентифікатор встановлення програми. Я спілкувався з кимось із Google, щоб отримати додаткові роз'яснення щодо кількох предметів у випадку, якщо вам це потрібно зробити. Ось що я дізнався про ідентифікатори пристроїв, про які НЕ згадується у вищезгаданому дописі блогу:

  • ANDROID_ID - кращий ідентифікатор пристрою. ANDROID_ID ідеально надійний у версіях Android <= 2.1 або> = 2.3. Лише у 2.2 є проблеми, про які йдеться у публікації.
  • Помилка ANDROID_ID у версії 2.2 впливає на кілька пристроїв кількох виробників.
  • Наскільки мені вдалося визначити, усі постраждалі пристрої мають однаковий ANDROID_ID , який становить 9774d56d682e549c . Це також той самий ідентифікатор пристрою, про який повідомляв емулятор, btw.
  • Google вважає, що OEM-виробники виправили цю проблему для багатьох або більшості своїх пристроїв, але мені вдалося переконатись, що станом на початок квітня 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;
    }
}

6
Чи не варто ви хешувати різні ідентифікатори, щоб вони були однакового розміру? Крім того, вам слід пересипати ідентифікатор пристрою, щоб випадково не викрити приватну інформацію.
Стів Померой

2
Гарні бали, Стів. Я оновив код, щоб завжди повертати UUID. Це гарантує, що а) згенеровані ідентифікатори завжди однакового розміру; б) ідентифікатори андроїда та пристрою хешируються перед поверненням, щоб уникнути випадкового викриття особистої інформації. Я також оновив опис, щоб зазначити, що ідентифікатор пристрою зберігатиметься в заводських налаштуваннях, а для деяких користувачів це може не бути бажаним.
emmby

1
Я вважаю, що ви неправі; кращим рішенням є відстеження установок, а не ідентифікаторів пристроїв. Ваш код істотно довший і складніший, ніж у публікації в блозі, і мені не очевидно, що він додає ніякої цінності.
Тім Брей

7
Добре, що я оновив коментар, настійно пропоную користувачам використовувати ідентифікатори встановлення додатків, а не ідентифікатори пристроїв. Однак я вважаю, що це рішення все ще цінне для людей, яким потрібен пристрій, а не ідентифікатор встановлення.
emmby

8
ANDROID_ID може змінитись при скиданні до заводських налаштувань, тому він також не може ідентифікувати пристрої
Самюель

180

Ось код, який Reto Meier використовував у презентації вводу / виводу Google цього року, щоб отримати унікальний ідентифікатор для користувача:

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 з доданим поточним часом для унікального ідентифікатора. Але це здається більш простим і надійним способом. Дякую Рето Меєру та Антоні Нолану
Gökhan Barış Aker

Це чудово, але як бути з укоріненими пристроями? Вони можуть отримати доступ до цього і легко змінити uid на інший.
тасоман

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 він повертає послідовну підроблену мак-адресу:02:00:00:00:00:00


8
Це вимагалоandroid.permission.ACCESS_WIFI_STATE
оххороб

5
Я думаю, ви побачите, що він недоступний, коли WiFi вимкнено, майже на всіх пристроях Android. Якщо вимкнути WiFi, вилучаєте пристрій на рівні ядра.
chrisdowney

12
@Sanandrea - давайте поглянемо, на вкоріненому пристрої ВСЕ може бути підроблено.
ocodo

5
Доступ до WiFi MAC - адреса був заблокований на Android M: stackoverflow.com/questions/31329733 / ...
Breez

6
З Android 6.x він повертає послідовну підроблену мак-адресу:02:00:00:00:00:00
Behrouz.M

87

Там досить корисна інформація тут .

Він охоплює п'ять різних типів ідентифікаторів:

  1. IMEI (лише для пристроїв Android з використанням телефону; потреби android.permission.READ_PHONE_STATE)
  2. Псевдо-унікальний ідентифікатор (для всіх пристроїв Android)
  3. Android ID (може бути недійсним, може змінюватися після скидання на завод, може бути змінено на вкоріненому телефоні)
  4. Рядок адреси MAC WLAN (потреби android.permission.ACCESS_WIFI_STATE)
  5. Рядок адрес BT MAC (пристрої з Bluetooth, потреби android.permission.BLUETOOTH)

2
Немає важливого моменту (тут і в статті): ви не можете отримати WLAN або BT MAC, якщо вони не включені! В іншому випадку я думаю, що WAC MAC був би ідеальним ідентифікатором. Ви не маєте гарантії, що користувач коли-небудь увімкне Wi-Fi, і я не думаю, що це "доцільно", щоб увімкнути його.
Том

1
@Том ти помиляється. Ви все ще можете читати WLAN або BT MAC, навіть коли вони вимкнено. Однак немає гарантії, що у пристрої доступні модулі WLAN або BT.
Маркс

2
Найбільш помітно, що локальні MAC-адреси WiFi та Bluetooth вже недоступні. Метод getMacAddress () об’єкта aWifiInfo та BluetoothAdapter.getDefaultAdapter (). Метод getAddress () повернеться 02: 00: 00: 00: 00: 00 відтепер
саріка кате

4
@sarikakate Це правда лише в 6,0 зефіру і вище ... Він як і раніше працює, як очікувалося, нижче 6,0 зефіру.
Сміт

@Smeet Так, ти маєш рацію. Я забув згадати, що його працює нижче 6.0
sarika kate

51

В офіційному блозі розробників Android зараз є повна стаття саме про цю саму тему, що визначає встановлення додатків .


4
І ключовим моментом цього аргументу є те, що якщо ви намагаєтеся отримати унікальний ідентифікатор з обладнання, ви, ймовірно, помиляєтесь.
Тім Брей

3
І якщо ви дозволяєте скинути блокування пристрою шляхом скидання заводських налаштувань, модель пробного програмного забезпечення є такою ж доброю, як і мертва.
Сева Алексєєва

43

У службі 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>

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

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

Більш детальну інформацію про такий підхід див. У розмові Reto .

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


5
Чи це не призводить до того, що декілька пристроїв з однаковим ідентифікатором, коли користувач має кілька пристроїв? Наприклад, планшет і телефон.
Тоса

Для цього потрібно мінімальна ціль 8.
халксінат

Це буде кращим способом створення корисного навантаження для підтвердження під час покупки через додаток? З коментаря до прикладу коду для виставлення рахунків: "Отже, корисний набір розробника має такі характеристики: 1. Якщо два різні користувачі купують товар, корисне навантаження між ними відрізняється, так що покупка одного користувача не може бути відтворена іншому користувачеві. 2. Корисна навантаження повинна бути такою, що ви можете перевірити її навіть тоді, коли додаток не був тим, хто ініціював потік покупки (щоб товари, придбані користувачем на одному пристрої, працювали на інших пристроях, які належать користувачеві). "
TouchBoarder

@Tosa У мене було саме те питання. Але хіба ми не могли знову використати цю саму методику для створення ідентифікаторів віртуальних пристроїв і просто не повернути їх назад таким же чином? Ідентифікатор пристрою не зберігатиметься після видалення або перевстановлення, але якщо у нас є стійкий ідентифікатор користувача, нам може не знадобитися стільки з ідентифікатора пристрою.
jwehrle

39

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

Псевдо-унікальний ідентифікатор, який працює на всіх пристроях Android. Деякі пристрої не мають телефону (наприклад, планшети) або чомусь не потрібно включати дозвіл READ_PHONE_STATE. Ви все ще можете прочитати деталі, такі як версія ROM, назва виробника, тип процесора та інші деталі апаратного забезпечення, які добре підходять, якщо ви хочете використовувати ідентифікатор для перевірки послідовного ключа чи інших загальних цілей. Обчислений таким чином ідентифікатор не буде унікальним: можна знайти два пристрої з однаковим ідентифікатором (на основі одного і того ж апаратного та ROM-зображення), але зміни в реальних програмах незначні. Для цього ви можете використовувати клас 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
Ви повинні дати кредит своїм джерелам ... Це було знято прямо з наступної статті: pocketmagic.net/?p=1662
Стів Хейлі

8
Цей ідентифікатор відкритий для зіткнень, наче ви не знаєте що. Практично гарантовано бути однаковим на однакових пристроях одного і того ж носія.
Сева Алексєєва

7
Це також може змінитися, якщо пристрій оновиться.
Давид Дано

8
Дуже, дуже погане рішення. Тестовано на двох Nexus 5 ... Поверніть однакові номери.
Синан Діздаревич

38

Наступний код повертає серійний номер пристрою за допомогою прихованого Android API. Але цей код не працює на 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 (Все, крім пікселів і Nexusів), що становить понад 50% активних пристроїв у всьому світі. Тому в офіційних найкращих практиках Android-ідентифікаторів чітко зазначено:

Уникайте використання апаратних ідентифікаторів , таких як SSAID (Android ID), IMEI, MAC-адреса тощо ...

Це робить більшість відповідей вище недійсними. Також через різні оновлення безпеки для Android, деякі з них потребують новіших і жорстких дозволів виконання, які користувач може просто відмовити.

Як приклад CVE-2018-9489 який стосується всіх методів на основі WIFI, згаданих вище.

Це робить ці ідентифікатори не тільки ненадійними, але й неприйнятними у багатьох випадках.

Отже, простішими словами: не використовуйте цих прийомів .

Багато інших відповідей тут пропонують використовувати те AdvertisingIdClient, що також несумісне, оскільки його задумом слід використовувати лише для профілювання оголошень. Це також зазначено в офіційній довідці

Використовуйте рекламний ідентифікатор лише для профілювання користувачів або випадків використання оголошень

Це не тільки ненадійно для ідентифікації пристрою, але ви також повинні дотримуватися конфіденційності користувачів щодо відстеження оголошень політики , де чітко зазначено, що користувач може скинути або заблокувати його в будь-який момент.

Тому і не використовуйте його .

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

Використовуйте FirebaseInstanceId або приватний GUID, коли це можливо, для всіх інших випадків використання, за винятком запобігання шахрайству з платежами та телефонії.

Це унікально для встановлення програми на пристрої, тому коли користувач видаляє додаток - його видаляють, тож це не на 100% надійно, але це наступне найкраще.

Для використання FirebaseInstanceIdдодайте останню залежність обміну повідомленнями в базі даних у свій градус

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

І використовуйте код нижче у фоновому потоці:

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

Якщо вам потрібно зберегти ідентифікацію пристрою на віддаленому сервері, не зберігайте його таким, як є (звичайний текст), а хеш із сіллю .

Сьогодні це не лише найкраща практика, ви насправді повинні робити це відповідно до закону GDPR - ідентифікаторів та подібних норм.


3
На даний момент це найкраща відповідь, і перше речення - найкраще резюме: "Це просте запитання, без простої відповіді - просто люблю це",
b2mob

Ви повинні цінувати коментар "за винятком запобігання шахрайству та оплати телефонних платежів" без відповіді про те, як вирішити цю справу використання.
Eran Boudjnah

@EranBoudjnah Ця цитата з офіційного посилання, яка пов'язана у відповіді. Я можу спробувати вирішити цей випадок використання, але оскільки він не є специфічним для питання про ОП, Відповідно до політики stackoverflow - це слід зробити в окремому спеціальному питанні.
Микита Куртін

Я просто кажу, що відповідь неповна. Але я усвідомлюю, що ви не винні, тому що це цитата.
Eran Boudjnah

1
@ M.UsmanKhan, відповідь написана одразу після цього: " Сьогодні це не лише найкраща практика. Ви насправді повинні робити це за законом відповідно до GDPR - ідентифікаторів та подібних норм. "
Микита Куртін,

27

Використовуючи наведений нижче код, ви можете отримати унікальний ідентифікатор пристрою пристрою ОС Android у якості рядка.

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, який не є Null, кожен G Tablet повідомляє однакову кількість.

Це робить цікавою гру в "Pocket Empires", яка дає вам миттєвий доступ до чийогось облікового запису на основі "унікального" DeviceID.

У мого пристрою немає стільникового радіо.


Що таке ідентифікатор? це досяжність 9774d56d682e549c?
Mr_and_Mrs_D

Нічого собі, це було так давно, я давно кинув цей планшет. Не могла сказати.
Тоні Маро

Працює. Крім того, набагато компактніше, ніж ідентифікатор, який я отримую від випадкового UUID.
Treewallie

1
@Treewallie це працює? Чи можете ви отримати однаковий ідентифікатор пристрою в різних додатках?
Арнольд Браун

@ArnoldBrown Так. Не забудьте ретельно перевірити це. Приємного дня: D
Treewallie

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

Якщо ви хочете відслідковувати установки додатків, це ідеально. Хоча пристрої для відстеження набагато складніше, і це, здається, не є повністю герметичним рішенням.
Лука Спіллер

Що з укоріненими пристроями? Вони можуть легко змінити цей інсталяційний ідентифікатор, правда?
тасоман

Абсолютно. Корінь може змінити ідентифікатор установки. Ви можете перевірити корінь за допомогою цього кодового блоку: stackoverflow.com/questions/1101380/…
Кевін Паркер

якщо ми скинемо завод, чи видалить файл?
Джамшид

Якщо ви відновите заводські налаштування та видалите або відформатуєте розділ / дані, 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" />

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, див. Визначення установок додатків .


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, які замість IMEI мають ESN.
Давид Дано

@David Враховуючи, чи є 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;
}

якщо ми користуємося ReadPhoneState у версії 6.0 із запитом дозволу на виконання
Harsha

8

Мої два центи - зауважте, що це унікальний ідентифікатор пристрою (помилка) - не той інсталяційний, як обговорювалося в блозі розробників Android .

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

Натомість я інкапсулював різні стратегії отримання (пристрою) ідентифікатора в enum - зміна порядку констант enum впливає на пріоритет різних способів отримання ідентифікатора. Перший ненульовий ідентифікатор повертається або викидається виняток (згідно з хорошими методами Java, щоб не надавати значення null). Так, наприклад, у мене TELEFHONY перший, але хорошим вибором за замовчуванням буде бета-версія 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);
        }
    }
}

8

Тут є відповіді 30+, деякі - однакові, а деякі - унікальні. Ця відповідь ґрунтується на кількох таких відповідях. Один з них - відповідь @Lenn Dolling.

Він поєднує 3 ідентифікатори та створює 32-значний шістнадцятковий рядок. Це дуже добре спрацювало для мене.

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

Він поверне щось подібне: 551F27C060712A72730B0A0F734064B1

Примітка. Ви завжди можете додати більше ідентифікаторів до longIdрядка. Наприклад, серійний номер #. адреса адаптера wifi. 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 (нижче пряників), скинув їх телефон або "Безпечний.ANDROID_ID". якщо повертається "null", то просто повернутий ідентифікатор буде виключно виходячи з інформації про їх пристрої Android. Тут можуть статися зіткнення. Намагайтеся не використовувати DISPLAY, HOST або ID - ці елементи можуть змінитися. Якщо трапляються зіткнення, будуть дані, що перетинаються. Джерело: gist.github.com/pedja1/fe69e8a80ed505500caa
Mousa Alfhaily

Якщо ми спробуємо отримати унікальний номер за цим рядком коду, чи можемо ми сказати, що це унікальний ідентифікатор і він ніколи не буде конфліктувати з будь-яким іншим пристроєм?
Ніндзя

1
@Ninja Оскільки BLE mac-адреса унікальна, так, генерований ідентифікатор завжди буде унікальним. Однак якщо ви дійсно хочете бути впевненими, я б запропонував додати 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
  • Міні MK802
  • Samsung Galaxy S II

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


Я просто прочитав вашу публікацію в блозі. Я вважаю, що це не унікально: 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 ID як унікальну 64-бітну шістнадцяткову рядок.

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

Іноді TelephonyManger.getDeviceId () поверне нуль, тому для впевненості в унікальному ідентифікаторі ви будете використовувати цей метод:

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

Нещодавно я виявив, що клієнтський пристрій типу SM-G928F / Galaxy S6 edge + забезпечує лише 15 замість 16 шістнадцяткових цифр для Android ID.
Хольгер Якобс

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, але це може змінюватися між телефонами.


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.


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

Випущено в I / O 2015; для Android потрібні ігрові послуги 7.5.

https://developers.google.com/instan-id/
https://developers.google.com/instan-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, якщо користувач очистить дані програми.
idanakav

Цікаво, але я не думаю, що це насправді змінює потенційні випадки використання: ідентифікатор екземпляра, як android_id, не підходить для ідентифікації пристрою. Тож ваш сервер побачить дані очищення користувачів, як-от видалення та повторну інсталяцію програми, - що не є розумним.
Том
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.