Чи мають пристрої Android унікальний ідентифікатор, і якщо так, то який простий спосіб отримати доступ до нього за допомогою Java?
Чи мають пристрої Android унікальний ідентифікатор, і якщо так, то який простий спосіб отримати доступ до нього за допомогою Java?
Відповіді:
Settings.Secure#ANDROID_ID
повертає Android ID як унікальний для кожного користувача 64-бітний шістнадцятковий рядок.
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
ОНОВЛЕННЯ : Станом на останніх версіях Android, багато проблем із проблемою ANDROID_ID
вирішено, і я вважаю, що цей підхід вже не потрібен. Будь ласка, подивіться на відповідь Ентоні .
Повне розкриття: в моєму додатку спочатку використовувався нижченаведений підхід, але він більше не використовує цей підхід, і тепер ми використовуємо підхід, викладений у записі Блог розробника Android, на який посилаються відповіді emmby (а саме генерування та збереження а UUID#randomUUID()
).
На це питання є багато відповідей, більшість з яких буде працювати лише "деякий" час, і, на жаль, це недостатньо добре.
На основі моїх тестів пристроїв (усі телефони, принаймні один з яких не активований):
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
(як очікувалося)ANDROID_ID
ANDROID_ID
і TelephonyManager.getDeviceId()
- до тих пір, поки обліковий запис 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 символів.
Прочитавши кожну публікацію про переповнення стека про створення унікального ідентифікатора, блогу розробників Google та документації на 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.GET_ACCOUNTS" />
або<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
( як отримати основну електронну адресу пристрою Android )Номер телефону користувача - Програмне забезпечення
<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 - апаратне забезпечення (може бути недійсним, може змінюватися після скидання на заводські налаштування, може бути змінено на вкоріненому пристрої)
MLAN-адреса WLAN - Обладнання (потреби android.permission.ACCESS_WIFI_STATE
)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
Адреса Bluetooth MAC - обладнання (пристрої з Bluetooth, потреби android.permission.BLUETOOTH
)
<uses-permission android:name="android.permission.BLUETOOTH "/>
Псевдо-унікальний ідентифікатор - програмне забезпечення (для всіх пристроїв Android)
Я знаю, що немає жодного «ідеального» способу отримання унікального ідентифікатора без використання дозволів; Однак іноді нам потрібно лише відстежувати встановлення пристрою. Що стосується створення унікального ідентифікатора, ми можемо створити "псевдо-унікальний ідентифікатор" на основі виключно інформації, яку надає нам API Android, не використовуючи додаткових дозволів. Таким чином, ми можемо виявити повагу користувачів і спробувати запропонувати хороший досвід користувача.
Маючи псевдо-унікальний ідентифікатор, ви дійсно стикаєтесь лише з тим, що можуть бути дублікати, засновані на тому, що є подібні пристрої. Ви можете налаштувати комбінований метод, щоб зробити його більш унікальним; проте деяким розробникам потрібно відстежувати встановлення пристроїв, і це дозволить зробити фокус або продуктивність на основі подібних пристроїв.
Якщо їх Android-пристрій має API 9 або більше, це гарантовано унікальність через поле "Build.SERIAL".
ПАМ’ЯТАЙТЕ , ви технічно не вистачаєте лише приблизно на 0,5% користувачів, які мають API <9 . Тож ви можете зосередитись на решті: це 99,5% користувачів!
Якщо Android-пристрій користувача нижчий за API 9; сподіваємось, вони не виконали скидання заводських налаштувань, і їх "Secure.ANDROID_ID" буде збережено або не буде "null". (див. http://developer.android.com/about/dashboards/index.html )
Якщо все інше не вдається, якщо користувач має менший ніж API 9 (нижче Gingerbread), скинув їх пристрій або "Secure.ANDROID_ID" повертає "null", просто повернутий ідентифікатор буде виключно виходячи з інформації про їх пристрої Android. Тут можуть статися зіткнення.
Зміни:
Перегляньте метод нижче:
/**
* 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:
Починаючи з 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
Я намагався посилатися на кожне посилання, з якого я брав інформацію. Якщо ви відсутні та потребуєте включення, будь ласка, прокоментуйте!
Build
змінився б клас після оновлення ОС? Особливо, якщо API було оновлено? Якщо так, то як ви гарантуєте, що це унікальне? (Якщо говорити про метод, який ви написали)
Як зазначає Дейв Вебб, у блозі розробників Android є стаття, яка висвітлює цю проблему. Їх кращим рішенням є відстеження встановлень додатків, а не пристроїв, і це буде добре працювати в більшості випадків використання. Повідомлення в блозі покаже вам необхідний код, щоб зробити цю роботу, і я рекомендую вам перевірити його.
Однак у блозі продовжується обговорення рішень, якщо вам потрібен ідентифікатор пристрою, а не ідентифікатор встановлення програми. Я спілкувався з кимось із Google, щоб отримати додаткові роз'яснення щодо кількох предметів у випадку, якщо вам це потрібно зробити. Ось що я дізнався про ідентифікатори пристроїв, про які НЕ згадується у вищезгаданому дописі блогу:
Виходячи з рекомендацій 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;
}
}
Ось код, який 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;
}
Якщо ви поєднаєте це із стратегією резервного копіювання, щоб надсилати налаштування до хмари (також описано в розмові про Рето , у вас повинен бути ідентифікатор, який зв’язується з користувачем і закріплюється після того, як пристрій буде стерто, або навіть замінено. Планую використовувати це в аналітиці, що йде вперед (іншими словами, я цього ще не зробив :).
Також ви можете врахувати 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
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
Там досить корисна інформація тут .
Він охоплює п'ять різних типів ідентифікаторів:
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)В офіційному блозі розробників Android зараз є повна стаття саме про цю саму тему, що визначає встановлення додатків .
У службі 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 .
А для отримання детальної інформації про те, як реалізувати агент резервного копіювання, див. Резервне копіювання даних . Я особливо рекомендую розділ внизу при тестуванні, оскільки резервне копіювання не відбувається миттєво, і тому для тестування вам потрібно змусити створити резервну копію.
Я думаю, що це впевнений вогневий спосіб побудови каркаса для унікального посвідчення ... перевірити це.
Псевдо-унікальний ідентифікатор, який працює на всіх пристроях 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 .)
Наступний код повертає серійний номер пристрою за допомогою прихованого 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) {
}
ro.serialno
використовується для генерації Settings.Secure.ANDROID_ID
. Таким чином, вони є в основному різними уявленнями однакової цінності.
ANDROID_ID
з нього випливає нове значення .
android.os.Build.SERIAL
android.os.Build.SERIAL
буде застаріло в Android O, дивіться android-developers.googleblog.com/2017/04/…
Це просте запитання, без простої відповіді.
Більше того, всі існуючі відповіді тут є застарілими або ненадійними.
Тож якщо ви шукаєте рішення у 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 - ідентифікаторів та подібних норм.
Використовуючи наведений нижче код, ви можете отримати унікальний ідентифікатор пристрою пристрою ОС Android у якості рядка.
deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
Серійне поле було додано доBuild
класу рівня API 9 (Android 2.3 - Пряник). Документація говорить, що вона представляє серійний номер обладнання. Таким чином, він повинен бути унікальним, якщо він існує на пристрої.
Я не знаю, чи підтримується вона насправді (= не нульовою) усіма пристроями з рівнем API> = 9.
Додам одне - у мене є одна з тих унікальних ситуацій.
Використання:
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Виявляється, що навіть якщо мій Viewsonic G Tablet повідомляє про DeviceID, який не є Null, кожен G Tablet повідомляє однакову кількість.
Це робить цікавою гру в "Pocket Empires", яка дає вам миттєвий доступ до чийогось облікового запису на основі "унікального" DeviceID.
У мого пристрою немає стільникового радіо.
9774d56d682e549c
?
Детальні вказівки щодо отримання унікального ідентифікатора для кожного пристрою 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" />
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, див. Визначення установок додатків .
Існує багато різних підходів для вирішення цих ANDROID_ID
питань (можливо, null
іноді або пристрої певної моделі завжди повертають однаковий ідентифікатор) з плюсами і мінусами:
Я сам вважаю за краще використовувати існуючу реалізацію OpenUDID (див. Https://github.com/ylechelle/OpenUDID ) для Android (див. Https://github.com/vieux/OpenUDID ). Інтеграція та використання ANDROID_ID
резервних копій для цих проблем, згаданих вище, легко .
Як щодо 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;
}
Мої два центи - зауважте, що це унікальний ідентифікатор пристрою (помилка) - не той інсталяційний, як обговорювалося в блозі розробників 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);
}
}
}
Тут є відповіді 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 "";
}
longId
та зберегти його у файлі, він зробить його найбільш унікальним ідентифікатором:String uuid = UUID.randomUUID().toString();
longId
. Змініть такий один рядок: String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
Це гарантує, що створений ідентифікатор буде унікальним.
Інший спосіб - це використання /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 та прочитати символи. Просто переконайтеся, що ви зафіксували його в обробці винятків, оскільки не всі пристрої мають цей файл.
Принаймні такі пристрої, як відомо, цей файл читають у всьому світі:
Ви також можете побачити мій пост у блозі Пропуск серійного номера апаратного забезпечення 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 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;
}
Для апаратного розпізнавання конкретного пристрою 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, але це може змінюватися між телефонами.
Я використовую наступний код, щоб отримати 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);
Більш конкретно, Settings.Secure.ANDROID_ID
. Це 64-бітна кількість, яка генерується та зберігається під час першого завантаження пристрою. Він скидається, коли пристрій протирається.
ANDROID_ID
здається хорошим вибором унікального ідентифікатора пристрою. Є й мінуси: по-перше, це не на 100% надійність у версіях Android до 2.2. (“Froyo”).
Також у приналежному телефоні від великого виробника був принаймні одна помилка, яка широко спостерігається, у кожному екземплярі однаковий ANDROID_ID.
Щоб зрозуміти наявні унікальні ідентифікатори на пристроях Android. Скористайтеся цим офіційним посібником.
Кращі практики для унікальних ідентифікаторів:
IMEI, Mac адреси, ідентифікатор особи, GUID, SSAID, ідентифікатор реклами, API безпечної мережі для перевірки пристроїв.
https://developer.android.com/training/articles/user-data-ids
Ідентифікатор ідентифікатора 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 секунди можуть спричинити занепокоєння (звичайно, залежно від вашого додатка).
ANDROID_ID
не забудьте прочитати цю відповідь і цю помилку .