Есть ли у устройств Android уникальный идентификатор, и если да, то каков простой способ получить к нему доступ с помощью Java?
Есть ли уникальный идентификатор устройства Android?
Ответы (50)
Settings.Secure#ANDROID_ID
возвращает идентификатор Android в виде < href = "https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID" rel = "noreferrer"> уникальный для каждого пользователя 64-битное шестнадцатеричное нить.
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
Также прочтите Рекомендации для уникальных идентификаторов: https://developer.android.com/training/articles/user-data-ids
ANDROID_ID
- я не могу воспроизвести это при тестировании - в любом случае стоит знать ...
- person Dori; 06.03.2014
ОБНОВЛЕНИЕ. В последних версиях Android многие проблемы с ANDROID_ID
были решены, и я считаю, что в таком подходе больше нет необходимости. Взгляните на ответ Энтони.
Полное раскрытие: в моем приложении изначально использовался приведенный ниже подход, но он больше не используется, и теперь мы используем подход, описанный в Блог разработчиков Android, на которую ссылается ответ emmby (а именно , создание и сохранение UUID#randomUUID()
).
На этот вопрос есть много ответов, большинство из которых будут работать только в некоторых случаях, и, к сожалению, этого недостаточно.
На основании моих тестов устройств (всех телефонов, хотя бы один из которых не активирован):
- Все протестированные устройства вернули значение
TelephonyManager.getDeviceId()
- Все устройства GSM (все протестированы с SIM-картой) вернули значение
TelephonyManager.getSimSerialNumber()
- Все устройства CDMA вернули нулевое значение для
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 android.content.Context;
import android.telephony.TelephonyManager;
import android.view.View;
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
в файл манифеста. При хранении в базе данных возвращаемая строка имеет длину 36 символов.
- person Richard; 28.02.2011
# Последнее обновление: 02.06.15
Прочитав все сообщения Stack Overflow о создании уникального идентификатора, блог разработчиков Google и документацию по Android, я чувствую, что «Псевдоидентификатор» - лучший из возможных вариантов.
Главный вопрос: аппаратное обеспечение против программного обеспечения
Аппаратное обеспечение
- Пользователи могут менять свое оборудование, планшет Android или телефон, поэтому уникальные идентификаторы, основанные на оборудовании, не подходят для ОТСЛЕЖИВАНИЯ ПОЛЬЗОВАТЕЛЕЙ.
- Для ОТСЛЕЖИВАНИЯ ОБОРУДОВАНИЯ это отличная идея.
Программное обеспечение
- Пользователи могут стереть / изменить свое ПЗУ, если они рутированы
- Вы можете отслеживать пользователей на разных платформах (iOS, Android, Windows и Интернет)
- Лучшее, что нужно для ОТСЛЕЖИВАНИЯ ИНДИВИДУАЛЬНОГО ПОЛЬЗОВАТЕЛЯ с его согласия, - просто попросить его войти в систему (сделать это легко с помощью 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 за публикацию всех наших вариантов (в этом вопросе о переполнении стека).
## Список опций - причины, почему / почему их не использовать:
Электронная почта пользователя - Программное обеспечение
Пользователь мог изменить адрес электронной почты - ВЕЛИКОЛЕПНО
API 5+
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
илиAPI 14+
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
(Как получить основной адрес электронной почты)Номер телефона пользователя - программное обеспечение
Пользователи могут менять номера телефонов - Маловероятно
<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» и изменить его значение, но это означает, что оно больше не будет уникальным.
Если у вас есть пользователь с устройством сброса к заводским настройкам, значение могло быть изменено или изменено на рутированном устройстве, поэтому могут быть повторяющиеся записи, если вы отслеживаете установки пользователей.
MAC-адрес WLAN - оборудование (требуется
android.permission.ACCESS_WIFI_STATE
)Это может быть второй лучший вариант, но вы по-прежнему собираете и храните уникальный идентификатор, который исходит непосредственно от пользователя. Очевидно, что вы собираете данные.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
MAC-адрес Bluetooth - оборудование (для устройств с Bluetooth требуется
android.permission.BLUETOOTH
)Большинство приложений на рынке не используют Bluetooth, поэтому, если ваше приложение не использует Bluetooth и вы его включаете, пользователь может заподозрить подозрение.
<uses-permission android:name="android.permission.BLUETOOTH "/>
Псевдоуникальный идентификатор - программное обеспечение (для всех устройств Android)
Очень возможно, могут содержать коллизии - см. Мой метод, опубликованный ниже!
Это позволяет вам получить «почти уникальный» идентификатор пользователя, не забирая ничего личного. Вы можете создать свой собственный анонимный идентификатор на основе информации об устройстве.
Я знаю, что не существует «идеального» способа получить уникальный идентификатор без использования разрешений; однако иногда нам действительно нужно только отслеживать установку устройства. Когда дело доходит до создания уникального идентификатора, мы можем создать «псевдоуникальный идентификатор» исключительно на основе информации, которую предоставляет нам Android API, без использования дополнительных разрешений. Таким образом, мы можем проявить уважение к пользователю и попытаться также предложить им хороший пользовательский опыт.
С псевдоуникальным идентификатором вы действительно сталкиваетесь только с тем фактом, что могут быть дубликаты, основанные на том факте, что существуют похожие устройства. Вы можете настроить комбинированный метод, чтобы сделать его более уникальным; однако некоторым разработчикам необходимо отслеживать установку устройств, и это поможет или повысит производительность на основе аналогичных устройств.
## 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 в 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/
Как упоминает Дэйв Уэбб, в блоге разработчиков Android есть статья Это покрывает это. Их предпочтительное решение - отслеживать установки приложений, а не устройств, и это хорошо работает для большинства случаев использования. Сообщение в блоге покажет вам код, необходимый для работы, и я рекомендую вам его проверить.
Однако в сообщении блога обсуждаются решения, если вам нужен идентификатор устройства, а не идентификатор установки приложения. Я поговорил с кем-то в 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. который сохраняется при перезапуске приложения (но не при повторной установке приложения).
Обратите внимание, что для устройств, которые должны использовать резервный идентификатор устройства, уникальный идентификатор БУДЕТ сохраняться при заводских сбросах. Об этом следует помнить. Если вам нужно убедиться, что при сбросе к заводским настройкам ваш уникальный идентификатор будет сброшен, вы можете рассмотреть возможность возврата непосредственно к случайному 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;
}
}
Вот код, который Рето Мейер использовал в презентации 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;
}
Если вы объедините это со стратегией резервного копирования для отправки настроек в облако (также описанной в Reto talk, у вас должен быть идентификатор, который привязан к пользователю и остается после очистки устройства или даже замены. Я планирую использовать его в аналитике. в будущем (другими словами, я еще не сделал этого :).
Также вы можете рассмотреть 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
android.permission.ACCESS_WIFI_STATE
- person ohhorob; 01.11.2010
02:00:00:00:00:00
- person Behrouz.M; 10.07.2016
Здесь есть довольно полезная информация .
Он охватывает пять различных типов идентификаторов:
- IMEI (только для устройств Android с телефоном; требуется
android.permission.READ_PHONE_STATE
) - Псевдоуникальный идентификатор (для всех устройств Android)
- Android ID (может быть пустым, может измениться после сброса настроек, может быть изменен на телефоне с рутированным доступом)
- Строка MAC-адреса WLAN (требуется
android.permission.ACCESS_WIFI_STATE
) - Строка MAC-адреса BT (для устройств с Bluetooth требуется
android.permission.BLUETOOTH
)
Это простой вопрос, на который нет простого ответа.
Более того, все существующие ответы здесь устарели или ненадежны.
Итак, если вы ищете решение в 2020 году.
Вот несколько вещей, о которых следует помнить:
Все аппаратные идентификаторы (SSAID, IMEI, MAC и т. Д.) Ненадежны для устройств, не принадлежащих Google (все, кроме пикселей и нексусов), которые составляют более 50% активных устройств во всем мире. Поэтому в официальном передовых методах работы с идентификаторами Android четко указано:
Избегайте использования аппаратных идентификаторов, таких как SSAID (Android ID), IMEI, MAC-адрес и т. д.
Это делает большинство приведенных выше ответов недействительными. Также из-за различных обновлений безопасности Android некоторые из них требуют более новых и более строгих разрешений времени выполнения, которые могут быть просто отклонены пользователем.
В качестве примера CVE-2018-9489
, который влияет на все упомянутые выше методы на основе WIFI.
Это делает эти идентификаторы не только ненадежными, но и во многих случаях недоступными.
Проще говоря: не используйте эти методы.
Многие другие ответы здесь предлагают использовать AdvertisingIdClient
, который также несовместим, так как его дизайн должен использоваться только для профилирования рекламы. Это также указано в официальном справочнике.
Используйте рекламный идентификатор только для профилей пользователей или вариантов использования рекламы
Это не только ненадежно для идентификации устройства, но вы также должны следовать политика конфиденциальности пользователей в отношении отслеживания рекламы, в которой четко указано, что пользователь может сбросить или заблокировать ее в любой момент.
Так что тоже не используйте его.
Поскольку у вас не может быть желаемого статического глобально уникального и надежного идентификатора устройства. Официальный справочник Android предлагает:
По возможности используйте FirebaseInstanceId или GUID, хранящийся в частном порядке для всех других случаев использования, кроме предотвращения мошенничества с платежами и телефонной связи.
Это уникально для установки приложения на устройство, поэтому, когда пользователь удаляет приложение, оно стирается, поэтому оно не на 100% надежно, но это еще одна лучшая вещь.
Чтобы использовать FirebaseInstanceId
, добавьте последнюю зависимость обмена сообщениями firebase в свой gradle
implementation 'com.google.firebase:firebase-messaging:20.2.4'
И используйте приведенный ниже код в фоновом потоке:
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
Если вам нужно сохранить идентификацию устройства на удаленном сервере, сохраняйте его не как есть (обычный текст), а как хеш с солью.
Сегодня это не только лучшая практика, вы фактически должны делать это по закону в соответствии с GDPR - идентификаторы и аналогичные правила.
UUID.randomUUID()
псевдослучайной платформы Java, зависящей от времени.
- person Nikita Kurtin; 25.02.2021
В официальном блоге разработчиков Android теперь есть полная статья именно на эту тему, Идентификация Установки приложений.
На странице Google I / O Рето Мейер опубликовал подробный ответ о том, как подойти к этому, который должен соответствовать требованиям большинства разработчиков. пользователей в разных установках. Энтони Нолан показывает направление в своем ответе, но я подумал, что напишу полный подход, чтобы другие могли легко увидеть, как это сделать (мне потребовалось время, чтобы выяснить детали).
Такой подход предоставит вам анонимный, безопасный идентификатор пользователя, который будет постоянным для пользователя на разных устройствах (на основе основной учетной записи Google) и между установками. Базовый подход - создать случайный идентификатор пользователя и сохранить его в общих настройках приложений. Затем вы используете агент резервного копирования Google, чтобы сохранить общие настройки, связанные с учетной записью Google, в облаке.
Давайте рассмотрим полный подход. Во-первых, нам нужно создать резервную копию наших SharedPreferences с помощью Android Backup Service. Начните с регистрации своего приложения через 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 в вашем основном Activity:
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 теперь будет постоянным для всех установок, даже если пользователь перемещает устройство.
Для получения дополнительной информации об этом подходе см. Выступление Рето.
А для получения полной информации о том, как реализовать агент резервного копирования, см. Резервное копирование данных em. >. Я особенно рекомендую раздел внизу, посвященный тестированию, поскольку резервное копирование не происходит мгновенно, и поэтому для тестирования вам нужно принудительно выполнить резервное копирование.
Я думаю, что это верный способ создания скелета для уникального идентификатора ... проверьте это.
Псевдоуникальный идентификатор, который работает на всех устройствах 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.)
Следующий код возвращает серийный номер устройства с помощью скрытого 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) {
}
Используя приведенный ниже код, вы можете получить уникальный идентификатор устройства на ОС Android в виде строки.
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Поле Serial было добавлено к классу Build
на уровне API 9. (Android 2.3 - Gingerbread). В документации указано, что он представляет собой серийный номер оборудования. Таким образом, он должен быть уникальным, если он существует на устройстве.
Я не знаю, поддерживается ли он на самом деле (= не null) всеми устройствами с уровнем API> = 9.
Я добавлю одну вещь - у меня одна из тех уникальных ситуаций.
С использованием:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Оказывается, даже несмотря на то, что мой Viewsonic G Tablet сообщает DeviceID, который не равен NULL, каждый G Tablet сообщает одно и то же число.
Делает интересной игру в «Pocket Empires», которая дает вам мгновенный доступ к чьей-либо учетной записи на основе «уникального» DeviceID.
В моем устройстве нет сотового радио.
Подробные инструкции о том, как получить уникальный идентификатор для каждого устройства 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();
}
}
Добавьте код ниже в файл класса:
final TelephonyManager tm = (TelephonyManager) getBaseContext()
.getSystemService(SplashActivity.TELEPHONY_SERVICE);
final String tmDevice, tmSerial, androidId;
tmDevice = "" + tm.getDeviceId();
Log.v("DeviceIMEI", "" + tmDevice);
tmSerial = "" + tm.getSimSerialNumber();
Log.v("GSM devices Serial Number[simcard] ", "" + tmSerial);
androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(),
android.provider.Settings.Secure.ANDROID_ID);
Log.v("androidId CDMA devices", "" + androidId);
UUID deviceUuid = new UUID(androidId.hashCode(),
((long) tmDevice.hashCode() << 32) | tmSerial.hashCode());
String deviceId = deviceUuid.toString();
Log.v("deviceIdUUID universally unique identifier", "" + deviceId);
String deviceModelName = android.os.Build.MODEL;
Log.v("Model Name", "" + deviceModelName);
String deviceUSER = android.os.Build.USER;
Log.v("Name USER", "" + deviceUSER);
String devicePRODUCT = android.os.Build.PRODUCT;
Log.v("PRODUCT", "" + devicePRODUCT);
String deviceHARDWARE = android.os.Build.HARDWARE;
Log.v("HARDWARE", "" + deviceHARDWARE);
String deviceBRAND = android.os.Build.BRAND;
Log.v("BRAND", "" + deviceBRAND);
String myVersion = android.os.Build.VERSION.RELEASE;
Log.v("VERSION.RELEASE", "" + myVersion);
int sdkVersion = android.os.Build.VERSION.SDK_INT;
Log.v("VERSION.SDK_INT", "" + sdkVersion);
Добавьте в AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Существует множество различных подходов к решению этих ANDROID_ID
проблем (иногда null
или устройства определенной модели всегда возвращают один и тот же идентификатор) с плюсами и минусами:
- Реализация алгоритма генерации пользовательского идентификатора (на основе свойств устройства, которые должны быть статическими и не изменятся -> кто знает)
- Злоупотребление другими идентификаторами, такими как IMEI, серийный номер, MAC-адрес Wi-Fi / Bluetooth (они выиграли ' существуют на всех устройствах или необходимы дополнительные разрешения)
Я предпочитаю использовать существующую реализацию OpenUDID (см. https://github.com/ylechelle/OpenUDID) для Android (см. https://github.com/vieux/OpenUDID). Его легко интегрировать, и он использует ANDROID_ID
с запасными вариантами для решения упомянутых выше проблем.
Уникальный идентификатор устройства 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, см. Идентификация Установки приложений.
Здесь более 30 ответов, и некоторые из них одинаковы, а некоторые уникальны. Этот ответ основан на немногих из этих ответов. Один из них - ответ @Lenn Dolling.
Он объединяет 3 идентификатора и создает 32-значную шестнадцатеричную строку. У меня это сработало очень хорошо.
3 идентификатора:
Псевдо-идентификатор - создается на основе технических характеристик физического устройства
ANDROID_ID - Settings.Secure.ANDROID_ID
Адрес Bluetooth strong > - адрес адаптера Bluetooth
Он вернет примерно следующее: 551F27C060712A72730B0A0F734064B1.
Примечание. Вы всегда можете добавить дополнительные идентификаторы в строку longId
. Например, серийный номер. адрес адаптера Wi-Fi. IMEI. Таким образом, вы делаете его более уникальным для каждого устройства.
@SuppressWarnings("deprecation")
@SuppressLint("HardwareIds")
public static String generateDeviceIdentifier(Context context) {
String pseudoId = "35" +
Build.BOARD.length() % 10 +
Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 +
Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 +
Build.HOST.length() % 10 +
Build.ID.length() % 10 +
Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 +
Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 +
Build.TYPE.length() % 10 +
Build.USER.length() % 10;
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String btId = "";
if (bluetoothAdapter != null) {
btId = bluetoothAdapter.getAddress();
}
String longId = pseudoId + androidId + btId;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(longId.getBytes(), 0, longId.length());
// get md5 bytes
byte md5Bytes[] = messageDigest.digest();
// creating a hex string
String identifier = "";
for (byte md5Byte : md5Bytes) {
int b = (0xFF & md5Byte);
// if it is a single digit, make sure it have 0 in front (proper padding)
if (b <= 0xF) {
identifier += "0";
}
// add number to string
identifier += Integer.toHexString(b);
}
// hex string to uppercase
identifier = identifier.toUpperCase();
return identifier;
} catch (Exception e) {
Log.e("TAG", e.toString());
}
return "";
}
longId
и сохранение его в файле сделает его наиболее уникальным идентификатором. : String uuid = UUID.randomUUID().toString();
- person Mousa Alfhaily; 11.04.2017
longId
. Измените эту строку следующим образом: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
Это гарантирует, что сгенерированный идентификатор будет уникальным.
- person ᴛʜᴇᴘᴀᴛᴇʟ; 03.04.2019
Как насчет IMEI. Это уникально для Android или других мобильных устройств.
Вот как я генерирую уникальный идентификатор:
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;
}
Мои два цента - 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);
}
}
}
Другой способ - использовать /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 серийный номер непривилегированных приложений, где я обсуждаю, какие еще файлы доступны для информации.
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;
}
Идентификатор экземпляра 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 секунды могут доставить неудобства (конечно, в зависимости от вашего приложения).
Для аппаратного распознавания конкретного устройства 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, но это может меняться между телефонами.
Я использую следующий код для получения 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);
Чтобы понять, какие уникальные идентификаторы доступны на устройствах Android. Используйте это официальное руководство.
Рекомендации по использованию уникальных идентификаторов.
IMEI, адреса Mac, идентификатор экземпляра, идентификаторы GUID, SSAID, рекламный идентификатор, API сети безопасности для проверки устройств.
https://developer.android.com/training/articles/user-data-ids
Теперь у Google есть рекламный идентификатор.
Он также может использоваться, но учтите, что:
Рекламный идентификатор - это уникальный сбрасываемый идентификатор, зависящий от пользователя.
а также
позволяет пользователям сбросить свой идентификатор или отказаться от рекламы на основе интересов в приложениях Google Play.
Таким образом, хотя этот идентификатор может измениться, похоже, что скоро у нас может не быть выбора, зависит от цели этого идентификатора.
Дополнительная информация @ develper.android
Скопируйте и вставьте сюда код
HTH
1. Используйте диспетчер телефонии, который предоставляет уникальный идентификатор (например, 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
от вашего пользователя, что может быть трудно оправдать, следуя типу приложения, которое вы сделали.
Устройства без услуг телефонии, такие как планшеты, должны сообщать уникальный идентификатор устройства, доступный через
android.os.Build.SERIAL
, начиная с Android 2.3 Gingerbread. Некоторые телефоны с услугами телефонии также могут определять серийный номер. Поскольку не все устройства Android имеют серийный номер, это решение ненадежно.При первой загрузке устройства генерируется и сохраняется случайное значение. Это значение доступно через
Settings.Secure.ANDROID_ID
. Это 64-битное число, которое должно оставаться постоянным на протяжении всего срока службы устройства.ANDROID_ID
кажется хорошим выбором для уникального идентификатора устройства, поскольку он доступен для смартфонов и планшетов. Чтобы получить значение, вы можете использовать следующий код,Строка androidId = Settings.Secure.getString (getContentResolver (), Settings.Secure.ANDROID_ID);
Однако значение может измениться, если на устройстве будет выполнен сброс настроек до заводских. Также есть известная ошибка в популярном телефоне от производителя, где на каждом экземпляре есть одинаковый ANDROID_ID
. Ясно, что решение не на 100% надежно.
- Используйте UUID. Поскольку требование для большинства приложений - идентифицировать конкретную установку, а не физическое устройство, хорошее решение для получения уникального идентификатора для пользователя, если использовать класс UUID. Следующее решение было представлено Рето Мейером из Google в презентации Google I / O:
SharedPreferences sharedPrefs = context.getSharedPreferences(
PREF_UNIQUE_ID, Context.MODE_PRIVATE);
uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null);
Обновление: варианты №1 и №2 больше не доступны после Android 10 в связи с обновлениями конфиденциальности от Google. поскольку вариант 2 и 3 требует критического разрешения.
DeviceInfoProvider
не входит в Android SDK
- person user924; 06.02.2020
Идентификатор Mac устройства Android также является уникальным идентификатором. Он не изменится, даже если само устройство отформатировано.
Используйте следующий код, чтобы получить идентификатор Mac:
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"/>
Напоминаем всем, кто читает и ищет более свежую информацию. В Android O внесены некоторые изменения в то, как система управляет этими идентификаторами.
https://android-developers.googleblog.com/2017/04/changes-to-device-identifiers-in.html
tl; dr Serial потребует разрешения ТЕЛЕФОНА, а идентификатор Android будет изменяться для разных приложений в зависимости от имени пакета и подписи.
Кроме того, Google составил хороший документ, в котором содержатся предложения о том, когда использовать идентификаторы оборудования и программного обеспечения.
https://developer.android.com/training/articles/user-data-ids.html
Обычно я использую уникальный идентификатор устройства для своих приложений. Но иногда пользуюсь 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;
}
Я столкнулся с этим вопросом несколько лет назад и научился реализовывать обобщенное решение, основанное на различных ответах.
Я использовал обобщенное решение в течение нескольких лет в реальном продукте. Пока он мне служит очень хорошо. Вот фрагмент кода, основанный на различных ответах.
Обратите внимание, что 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;
}
Не рекомендуется, так как deviceId можно использовать для отслеживания в сторонних руках, но это другой способ.
@SuppressLint("HardwareIds")
private String getDeviceID() {
deviceId = Settings.Secure.getString(getApplicationContext().getContentResolver(),
Settings.Secure.ANDROID_ID);
return deviceId;
}
Вот простой ответ, чтобы получить AAID, протестировано, работает правильно, июнь 2019 г.
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
String token = null;
Info adInfo = null;
try {
adInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext());
} catch (IOException e) {
// ...
} catch ( GooglePlayServicesRepairableException e) {
// ...
} catch (GooglePlayServicesNotAvailableException e) {
// ...
}
String android_id = adInfo.getId();
Log.d("DEVICE_ID",android_id);
return android_id;
}
@Override
protected void onPostExecute(String token) {
Log.i(TAG, "DEVICE_ID Access token retrieved:" + token);
}
};
task.execute();
прочтите полный ответ подробно здесь:
Получите D UUID устройства, номер модели с названием бренда и его номер версии с помощью функции ниже.
Работает в Android 10 безупречно, и вам не нужно разрешать чтение состояния телефона.
Фрагменты кода:
private void fetchDeviceInfo() {
String uniquePseudoID = "35" +
Build.BOARD.length() % 10 +
Build.BRAND.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 serial = Build.getRadioVersion();
String uuid=new UUID(uniquePseudoID.hashCode(), serial.hashCode()).toString();
String brand=Build.BRAND;
String modelno=Build.MODEL;
String version=Build.VERSION.RELEASE;
Log.e(TAG, "fetchDeviceInfo: \n "+
"\n uuid is : "+uuid+
"\n brand is: "+brand+
"\n model is: "+modelno+
"\n version is: "+version);
}
Вызовите вышеуказанную функцию и проверьте вывод вышеуказанного кода. пожалуйста, просмотрите свой лог-кот в студии Android. Это выглядит примерно так:
Чтобы включить Android 9, у меня есть только одна идея, которая (вероятно) не нарушает никаких условий, не требует разрешений и работает с разными установками и приложениями.
Отпечатки пальцев на сервере должны позволять однозначно идентифицировать устройство. Сочетание информации об оборудовании + установленных приложений и времени установки должно помочь. Время первой установки не изменится, если приложение не будет удалено и снова установлено. Но это необходимо сделать для всех приложений на устройстве, чтобы они не могли идентифицировать устройство (например, после сброса настроек).
Вот как я бы это сделал:
- Извлеките информацию об оборудовании, названиях пакетов приложений и времени первой установки.
Вот как вы извлекаете все приложения из 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();
}
}
- Вы можете захотеть создать хэш для каждой комбинации имени пакета и метки времени установки, прежде чем отправлять ее на сервер, поскольку это может или не может быть связано с вашим бизнесом, что пользователь установил на устройстве.
- Некоторые приложения (на самом деле их много) являются системными. Скорее всего, они будут иметь одинаковую метку времени установки, соответствующую последнему обновлению системы после сброса к заводским настройкам. Поскольку они имеют одинаковую метку времени установки, они не могут быть установлены пользователем и могут быть отфильтрованы.
- Отправьте информацию на сервер и позвольте ему найти ближайшее совпадение среди ранее сохраненной информации. Вам необходимо установить порог при сравнении с ранее сохраненной информацией об устройстве по мере установки и удаления приложений. Но я предполагаю, что этот порог может быть очень низким, поскольку любое сочетание имени пакета и отметки времени первой установки само по себе будет довольно уникальным для устройства, а приложения не так часто устанавливаются и удаляются. Наличие нескольких приложений только увеличивает вероятность быть уникальным.
- Верните сгенерированный уникальный идентификатор для соответствия или сгенерируйте уникальный идентификатор, сохраните с информацией об устройстве и верните этот новый идентификатор.
NB: это непроверенный и непроверенный метод! Я уверен, что это сработает, но я также почти уверен, что, если это приживется, они так или иначе закроют его.
Если вы добавите:
Settings.Secure.getString(context.contentResolver,
Settings.Secure.ANDROID_ID)
Android Lint выдаст следующее предупреждение:
Не рекомендуется использовать getString для получения идентификаторов устройств. Информация для проверки: использование этих идентификаторов устройств не рекомендуется, кроме как для предотвращения крупного мошенничества и сложных случаев использования телефонии. Для рекламных случаев используйте AdvertisingIdClient $ Info # getId, а для аналитики - InstanceId # getId.
Поэтому вам следует избегать этого.
Как указано в документации для разработчиков Android :
1: Избегайте использования идентификаторов оборудования.
В большинстве случаев вы можете избежать использования аппаратных идентификаторов, таких как SSAID (Android ID) и IMEI, без ограничения необходимой функциональности.
2. Используйте рекламный идентификатор только для профилирования пользователей или вариантов использования рекламы.
При использовании рекламного идентификатора всегда уважайте выбор пользователей в отношении отслеживания рекламы. Кроме того, убедитесь, что идентификатор не может быть связан с информацией, позволяющей установить личность (PII), и избегайте параллельного сброса идентификатора рекламы.
3. По возможности используйте идентификатор экземпляра или GUID, хранящийся в частном порядке, для всех других случаев использования, кроме предотвращения мошенничества с платежами и телефонной связи.
Для подавляющего большинства случаев использования, не связанных с рекламой, должно быть достаточно идентификатора экземпляра или GUID.
4. Используйте API-интерфейсы, подходящие для вашего варианта использования, чтобы минимизировать риск конфиденциальности.
Используйте DRM API для защиты ценных материалов и API SafetyNet для защиты от злоупотреблений. API-интерфейсы SafetyNet - это самый простой способ определить, является ли устройство подлинным, без риска для конфиденциальности.
String SERIAL_NUMER = Build.SERIAL;
Возвращает СЕРИЙНЫЙ НОМЕР в виде строки, уникальной для каждого устройства.
Серийный номер - это уникальный идентификатор устройства, доступный через 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).
Build.SERIAL
(или Build.getSerial()
) не всегда доступен. Дополнительная информация доступна в сообщении блога Изменения в Идентификаторы устройств в Android O. Также стоит прочитать: Рекомендации по использованию уникальных идентификаторов.
- person Ted Hopp; 14.12.2017
Для полноты, вот как вы можете получить Id
в Xamarin.Android
и C #:
var id = Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId);
Или, если вы не в Activity
:
var id = Settings.Secure.GetString(context.ContentResolver, Settings.Secure.AndroidId);
Где context
- это переданный контекст.
android.telephony.TelephonyManager.getDeviceId()
Это вернет любую строку, однозначно идентифицирующую устройство (IMEI в GSM, MEID для CDMA).
Вам понадобится следующее разрешение в вашем AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Да, у каждого устройства Android есть уникальный серийный номер, который можно узнать из этого кода. Build.SERIAL
. Обратите внимание, что он был добавлен только в API уровня 9 и может присутствовать не на всех устройствах. Чтобы получить уникальный идентификатор на более ранних платформах, вам нужно будет прочитать что-то вроде MAC-адреса или IMEI.
Проверьте SystemInfo.deviceUniqueIdentifier
Документация: http://docs.unity3d.com/Documentation/ScriptReference/SystemInfo-deviceUniqueIdentifier.html
Уникальный идентификатор устройства. Гарантия уникальности для каждого устройства (только чтение).
iOS: на устройствах до iOS7 возвращается хэш MAC-адреса. На устройствах iOS7 это будет UIDevice identifierForVendor или, если это не удается по какой-либо причине, ASIdentifierManager AdvertisingIdentifier.
Получите идентификатор устройства только один раз, а затем сохраните его в базе данных или файле. В этом случае, если это первая загрузка приложения, оно генерирует идентификатор и сохраняет его. В следующий раз потребуется только идентификатор, хранящийся в файле.
Чтобы получить идентификатор пользователя, вы можете использовать библиотеку лицензирования Google Play.
Чтобы загрузить эту библиотеку, откройте SDK Manager => SDK Tools. Путь к загруженным файлам библиотеки:
path_to_android_sdk_on_your_pc / extras / google / market_licensing / library
Включите библиотеку в свой проект (вы можете просто скопировать ее файлы).
Далее вам понадобится реализация интерфейса Policy
(вы можете просто использовать один из двух файлов из библиотеки: ServerManagedPolicy
или StrictPolicy
).
Идентификатор пользователя будет предоставлен вам внутри processServerResponse()
функции:
public void processServerResponse(int response, ResponseData rawData) {
if(rawData != null) {
String userId = rawData.userId
// use/save the value
}
// ...
}
Затем вам нужно создать LicenseChecker
с политикой и вызвать функцию checkAccess()
. Используйте MainActivity.java
в качестве примера того, как это сделать. MainActivity.java
находится внутри этой папки:
path_to_android_sdk_on_your_pc / extras / google / market_licensing / sample / src / com / example / android / market / licensing
Не забудьте добавить разрешение CHECK_LICENSE к вашему AndroidManifest.xml.
Подробнее о библиотеке лицензирования: https://developer.android.com/google/play/licensing а>
Android ограничивает идентификатор, связанный с оборудованием, после Android O, поэтому Android_Id является решением для уникального идентификатора, но у него есть проблема, когда отражатель устройства генерирует новый android_id, чтобы преодолеть эту проблему, мы можем использовать DRUMID.
val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
val drumIDByteArray = MediaDrm(WIDEVINE_UUID).getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID)
val drumID = android.util.Base64.encodeToString(drumIDByteArray,android.util.Base64.DEFAULT)
Вы получите MAC-адрес Wi-Fi, используя следующий код, независимо от того, использовали ли вы случайный адрес при попытке подключиться к Wi-Fi или нет, и независимо от того, был ли Wi-Fi включен или выключен.
Я использовал метод из приведенной ниже ссылки и добавил небольшую модификацию, чтобы получить точный адрес вместо рандомизированного:
Получение MAC-адреса в Android 6.0
public static String getMacAddr() {
StringBuilder res1 = new StringBuilder();
try {
List<NetworkInterface> all =
Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("p2p0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
continue;
}
res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
}
} catch (Exception ex) {
}
return res1.toString();
}
ANDROID_ID
, обязательно прочтите этот ответ и эта ошибка < / а>. - person Dheeraj Vepakomma   schedule 16.01.2013