Мне нужно использовать уникальный идентификатор для Android-приложения, и я подумал, что серийный номер устройства будет хорошим кандидатом. Как мне получить серийный номер Android-устройства в моем приложении?
Мне нужно использовать уникальный идентификатор для Android-приложения, и я подумал, что серийный номер устройства будет хорошим кандидатом. Как мне получить серийный номер Android-устройства в моем приложении?
Ответы:
TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
getSystemService - это метод из класса Activity. getDeviceID () вернет MDN или MEID устройства в зависимости от того, какое радио использует телефон (GSM или CDMA).
Каждое устройство ДОЛЖНО возвращать здесь уникальное значение (при условии, что это телефон). Это должно работать на любом устройстве Android с слотом для SIM-карты или радио CDMA. Вы сами по себе с этой микроволновой печью на базе Android ;-)
Как упоминает Дэйв Уэбб, в блоге разработчиков Android есть статья об этом.
Я поговорил с кем-то в Google, чтобы получить дополнительные разъяснения по некоторым вопросам. Вот что я обнаружил, но НЕ упоминается в вышеупомянутом сообщении блога:
Основываясь на рекомендациях Google, я реализовал класс, который будет генерировать уникальный UUID для каждого устройства, используя ANDROID_ID в качестве начального значения там, где это необходимо, при необходимости используя TelephonyManager.getDeviceId (), а если это не удается, прибегая к случайному сгенерированному уникальному 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 static volatile 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;
}
}
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 API.
String deviceId = Settings.System.getString(getContentResolver(),
Settings.System.ANDROID_ID);
Хотя не гарантируется, что Android ID будет уникальным идентификатором.
getContentResolver
возвращается null
. Однако, возможно, стоит задать вопрос и опубликовать свой код.
В блоге разработчиков Android есть отличный пост, в котором обсуждается это .
Он не рекомендует использовать, TelephonyManager.getDeviceId()
поскольку он не работает на устройствах Android, кроме телефонов, таких как планшеты, для этого требуетсяREAD_PHONE_STATE
разрешение и он не работает надежно на всех телефонах.
Вместо этого вы можете использовать одно из следующего:
В посте обсуждаются плюсы и минусы каждого из них, и его стоит прочитать, чтобы вы могли решить, какой из них лучше всего подходит для вашего использования.
Чтобы получить простой номер, который является уникальным для устройства и постоянным на протяжении всего его срока службы (за исключением восстановления заводских настроек или взлома), используйте Settings.Secure.ANDROID_ID .
String id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
Чтобы использовать серийный номер устройства (тот, который показан в «Системные настройки / О программе / Статус»), если он доступен, и вернуться к Android ID:
String serialNumber = Build.SERIAL != Build.UNKNOWN ? Build.SERIAL : Secure.getString(getContentResolver(), Secure.ANDROID_ID);
IMEI хороший, но работает только на устройствах Android с телефоном. Вам также следует рассмотреть возможность поддержки планшетов или других устройств Android, на которых нет телефона.
У вас есть несколько альтернатив, например: Build class members, BT MAC, WLAN MAC или даже лучше - комбинация всего этого.
Я объяснил эти детали в статье в своем блоге, см .: http://www.pocketmagic.net/?p=1662
Поскольку ни в одном из ответов здесь нет упоминания об идеальном, отказоустойчивом идентификаторе, который ПОСТОЯННЫЙ при обновлении системы и существует на ВСЕХ устройствах (в основном из-за того, что от Google нет индивидуального решения), я решил опубликовать метод, который является следующая лучшая вещь - объединение двух доступных идентификаторов и проверка выбора между ними во время выполнения.
Перед кодом 3 факта:
TelephonyManager.getDeviceId()
(akaIMEI) не будет работать хорошо или вообще не будет работать для устройств, отличных от GSM, 3G, LTE и т. д., но всегда будет возвращать уникальный идентификатор при наличии соответствующего оборудования , даже если SIM-карта не вставлена или даже если нет слота для SIM-карты ( некоторые OEM-производители сделали это).
Поскольку Gingerbread (Android 2.3) android.os.Build.SERIAL
должен существовать на любом устройстве, которое не предоставляет IMEI , т.е. не имеет вышеупомянутого оборудования, в соответствии с политикой Android.
Согласно факту (2.), по крайней мере, один из этих двух уникальных идентификаторов будет присутствовать ВСЕГДА , и SERIAL может присутствовать одновременно с IMEI.
Примечание. Факт (1.) и (2.) основан на утверждениях Google.
РЕШЕНИЕ
С учетом приведенных выше фактов всегда можно получить уникальный идентификатор, проверив, есть ли оборудование с привязкой к IMEI, и вернуться к SERIAL, когда его нет, поскольку нельзя проверить, действителен ли существующий SERIAL. Следующий статический класс представляет 2 метода проверки такого присутствия с использованием IMEI или SERIAL:
import java.lang.reflect.Method;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
public class IDManagement {
public static String getCleartextID_SIMCHECK (Context mContext){
String ret = "";
TelephonyManager telMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if(isSIMAvailable(mContext,telMgr)){
Log.i("DEVICE UNIQUE IDENTIFIER",telMgr.getDeviceId());
return telMgr.getDeviceId();
}
else{
Log.i("DEVICE UNIQUE IDENTIFIER", Settings.Secure.ANDROID_ID);
// return Settings.Secure.ANDROID_ID;
return android.os.Build.SERIAL;
}
}
public static String getCleartextID_HARDCHECK (Context mContext){
String ret = "";
TelephonyManager telMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if(telMgr != null && hasTelephony(mContext)){
Log.i("DEVICE UNIQUE IDENTIFIER",telMgr.getDeviceId() + "");
return telMgr.getDeviceId();
}
else{
Log.i("DEVICE UNIQUE IDENTIFIER", Settings.Secure.ANDROID_ID);
// return Settings.Secure.ANDROID_ID;
return android.os.Build.SERIAL;
}
}
public static boolean isSIMAvailable(Context mContext,
TelephonyManager telMgr){
int simState = telMgr.getSimState();
switch (simState) {
case TelephonyManager.SIM_STATE_ABSENT:
return false;
case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
return false;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
return false;
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
return false;
case TelephonyManager.SIM_STATE_READY:
return true;
case TelephonyManager.SIM_STATE_UNKNOWN:
return false;
default:
return false;
}
}
static public boolean hasTelephony(Context mContext)
{
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null)
return false;
//devices below are phones only
if (Build.VERSION.SDK_INT < 5)
return true;
PackageManager pm = mContext.getPackageManager();
if (pm == null)
return false;
boolean retval = false;
try
{
Class<?> [] parameters = new Class[1];
parameters[0] = String.class;
Method method = pm.getClass().getMethod("hasSystemFeature", parameters);
Object [] parm = new Object[1];
parm[0] = "android.hardware.telephony";
Object retValue = method.invoke(pm, parm);
if (retValue instanceof Boolean)
retval = ((Boolean) retValue).booleanValue();
else
retval = false;
}
catch (Exception e)
{
retval = false;
}
return retval;
}
}
Я бы посоветовал использовать getCleartextID_HARDCHECK
. Если отражение не прилипает к вашей среде, используйте getCleartextID_SIMCHECK
вместо этого метод, но учтите, что он должен быть адаптирован к вашим конкретным потребностям присутствия SIM-карты.
PS : Обратите внимание, что OEM-производителям удалось настроить SERIAL против политики Google (несколько устройств с одинаковым SERIAL), и Google, как заявляет, есть по крайней мере один известный случай в большом OEM (не раскрывается, и я не знаю, какой бренд это тоже, наверное, Самсунг).
Отказ от ответственности : это отвечает на исходный вопрос о получении уникального идентификатора устройства, но OP внес двусмысленность, заявив, что ему нужен уникальный идентификатор для приложения. Даже если для таких сценариев лучше использовать Android_ID, он НЕ БУДЕТ РАБОТАТЬ после, скажем, титановой резервной копии приложения через 2 разных установки ПЗУ (может быть даже одно и то же ПЗУ). Мое решение поддерживает постоянство, которое не зависит от прошивки или сброса до заводских настроек, и будет терпеть неудачу только в том случае, если происходит подделка IMEI или SERIAL с помощью хаков / аппаратных модов.
У всех вышеперечисленных подходов есть проблемы. В 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 в основном действии:
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 теперь будет постоянным для всех установок, даже если пользователь переключает устройства.
Дополнительные сведения об этом подходе см. В выступлении Рето здесь http://www.google.com/events/io/2011/sessions/android-protips-advanced-topics-for-expert-android-app-developers.html.
И для получения полной информации о том, как реализовать агент резервного копирования, см. Сайт разработчика здесь: http://developer.android.com/guide/topics/data/backup.html Я особенно рекомендую раздел внизу, посвященный тестированию, поскольку резервное копирование делает не происходит мгновенно, поэтому для тестирования вам нужно принудительно выполнить резервное копирование.
Другой способ - использовать / 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 и прочитать символы. Просто убедитесь, что вы заключили его в обработчик исключений, потому что не на всех устройствах есть этот файл.
Известно, что этот файл доступен для чтения всем по крайней мере на следующих устройствах:
Вы также можете увидеть мое сообщение в блоге здесь: http://insitusec.blogspot.com/2013/01/leaking-android-hardware-serial-number.html, где я обсуждаю, какие еще файлы доступны для информации.
Как говорит @haserman:
TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
Но необходимо включить разрешение в файл манифеста:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Уникальный идентификатор устройства Android OS в виде строки.
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:
Build.SERIAL
- самый простой способ, хотя и не совсем надежный, поскольку он может быть пустым или иногда возвращать другое значение ( доказательство 1 , доказательство 2 ), чем то, которое вы можете увидеть в настройках вашего устройства.
Есть несколько способов получить это число в зависимости от производителя устройства и версии Android, поэтому я решил скомпилировать все возможные решения, которые я мог найти, в единую суть . Вот его упрощенная версия:
public static String getSerialNumber() {
String serialNumber;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serialNumber = (String) get.invoke(c, "gsm.sn1");
if (serialNumber.equals(""))
serialNumber = (String) get.invoke(c, "ril.serialnumber");
if (serialNumber.equals(""))
serialNumber = (String) get.invoke(c, "ro.serialno");
if (serialNumber.equals(""))
serialNumber = (String) get.invoke(c, "sys.serialnumber");
if (serialNumber.equals(""))
serialNumber = Build.SERIAL;
// If none of the methods above worked
if (serialNumber.equals(""))
serialNumber = null;
} catch (Exception e) {
e.printStackTrace();
serialNumber = null;
}
return serialNumber;
}
Я знаю, что этот вопрос старый, но это можно сделать в одной строке кода
String deviceID = Build.SERIAL;
Я нашел пример класса, опубликованный выше @emmby, как отличную отправную точку. Но у него есть пара недостатков, о которых говорится в других плакатах. Основная из них заключается в том, что он без необходимости сохраняет UUID в XML-файле и после этого всегда извлекает его из этого файла. Это делает класс открытым для простого взлома: любой, у кого есть рутированный телефон, может редактировать XML-файл, чтобы присвоить себе новый UUID.
Я обновил код, чтобы он сохранялся в XML только в случае крайней необходимости (например, при использовании случайно сгенерированного UUID), и переработал логику в соответствии с ответом @Brill Pappin:
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 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) || (androidId == null) ) {
final String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId();
if (deviceId != null)
{
uuid = UUID.nameUUIDFromBytes(deviceId.getBytes("utf8"));
}
else
{
uuid = UUID.randomUUID();
// Write the value out to the prefs file so it persists
prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString() ).commit();
}
}
else
{
uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
/**
* 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;
}
Да. Это серийный номер аппаратного обеспечения устройства, и он уникален. Таким образом, на уровне API 2.3 и выше вы можете использовать android.os.Build.ANDROID_ID, чтобы получить его. Для уровня API ниже 2.3 используйте TelephonyManager.getDeviceID () .
вы можете прочитать это http://android-developers.blogspot.in/2011/03/identifying-app-installations.html