Android 앱에 고유 ID를 사용해야하는데 기기의 일련 번호가 좋은 후보라고 생각했습니다. 내 앱에서 Android 장치의 일련 번호를 검색하려면 어떻게합니까?
Android 앱에 고유 ID를 사용해야하는데 기기의 일련 번호가 좋은 후보라고 생각했습니다. 내 앱에서 Android 장치의 일련 번호를 검색하려면 어떻게합니까?
답변:
TelephonyManager tManager = (TelephonyManager)myActivity.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
getSystemService는 Activity 클래스의 메소드입니다. getDeviceID ()는 전화기가 사용하는 무선 (GSM 또는 CDMA)에 따라 장치의 MDN 또는 MEID를 반환합니다.
각 기기는 여기에서 고유 한 값을 반환해야합니다 (휴대 전화라고 가정). 이것은 sim 슬롯 또는 CDMA 라디오가있는 모든 Android 장치에서 작동합니다. Android 구동 전자 레인지를 사용하면됩니다 ;-)
Dave Webb이 언급했듯이 Android 개발자 블로그에는 이를 다루는 가 있습니다.
몇 가지 항목에 대한 추가 설명을 얻기 위해 Google 직원과 이야기했습니다. 앞서 언급 한 블로그 게시물에서 언급되지 않은 내용은 다음과 같습니다.
Google의 권장 사항에 따라 적절한 경우 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 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()
태블릿과 같은 휴대폰이 아닌 안드로이드 기기에서는 작동하지 않으므로 사용 을 권장 하지 않습니다.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 기기에 대한 지원도 고려해야합니다.
클래스 멤버 빌드, BT MAC, WLAN MAC 등의 대안이 있습니다.이 모든 것의 조합입니다.
내 블로그의 기사에서 이러한 세부 사항을 설명했습니다. http://www.pocketmagic.net/?p=1662
여기에는 시스템 업데이트를 통해 지속되고 모든 장치에 존재하는 완벽한 오류 방지 ID가 언급되어 있지 않기 때문에 (주로 Google의 개별 솔루션이 없다는 사실로 인해) 저는 다음과 같은 방법을 게시하기로 결정했습니다. 두 개의 사용 가능한 식별자를 결합하고 런타임에 둘 중에서 선택하는 검사를 통해 차선책입니다.
코드 이전에 3 가지 사실 :
TelephonyManager.getDeviceId()
(akaIMEI)는 GSM, 3G, LTE 등이 아닌 기기에서는 잘 작동하지 않거나 전혀 작동하지 않지만 관련 하드웨어가 있으면 항상 고유 ID를 반환합니다. SIM이 삽입되지 않았거나 SIM 슬롯이없는 경우에도 마찬가지입니다 ( 일부 OEM이이 작업을 수행했습니다.)
Gingerbread (Android 2.3) android.os.Build.SERIAL
는 IMEI를 제공하지 않는 모든 기기에 있어야합니다. 즉, Android 정책에 따라 앞서 언급 한 하드웨어가 없습니다.
사실 (2.)로 인해이 두 고유 식별자 중 적어도 하나는 항상 존재 하며 SERIAL 은 IMEI와 동시에 존재할 수 있습니다.
참고 : 사실 (1.) 및 (2.)는 Google 진술을 기반으로합니다.
해결책
위의 사실을 통해 IMEI 바인딩 하드웨어가 있는지 확인하여 항상 고유 식별자를 가질 수 있으며, 기존 SERIAL이 유효한지 확인할 수 없으므로 그렇지 않은 경우 SERIAL로 폴백 할 수 있습니다. 다음 정적 클래스는 이러한 존재를 확인하고 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 존재 요구 사항에 맞게 조정해야한다는 점을 고려하십시오.
추신 : OEM은 Google 정책 (동일한 SERIAL을 사용하는 여러 기기) 에 대해 SERIAL을 버그로 처리 했으며 Google은 대형 OEM에 알려진 사례가 하나 이상 있다고 언급했습니다 (공개되지 않았고 어떤 브랜드인지 모르겠습니다) 어느 쪽이든, 나는 삼성을 추측하고있다).
면책 조항 : 이것은 고유 장치 ID를 얻는 원래 질문에 대한 답변이지만 OP는 APP에 대한 고유 ID가 필요하다는 것을 명시하여 모호성을 도입했습니다. 이러한 시나리오의 경우 Android_ID가 더 좋더라도 2 개의 다른 ROM 설치를 통한 앱의 티타늄 백업 (동일한 ROM 일 수도 있음) 후에는 작동하지 않습니다. 내 솔루션은 플래시 또는 공장 초기화와 무관 한 지속성을 유지하며 해킹 / 하드웨어 모드를 통해 IMEI 또는 SERIAL 변조가 발생할 때만 실패합니다.
위의 모든 접근 방식에는 문제가 있습니다. Google i / o에서 Reto Meier는 설치 전반에 걸쳐 사용자를 추적하는 데 필요한 대부분의 개발자를 충족해야하는 접근 방법에 대한 강력한 답변을 발표했습니다.
이 접근 방식은 다른 기기 (기본 Google 계정 기반 태블릿 포함) 및 동일한 기기의 설치 전반에 걸쳐 사용자에게 영구적 인 익명의 안전한 사용자 ID를 제공합니다. 기본 접근 방식은 임의의 사용자 ID를 생성하고이를 앱 공유 환경 설정에 저장하는 것입니다. 그런 다음 Google의 백업 에이전트를 사용하여 Google 계정에 연결된 공유 환경 설정을 클라우드에 저장합니다.
전체 접근 방식을 살펴 보겠습니다. 먼저 Android 백업 서비스를 사용하여 SharedPreferences에 대한 백업을 만들어야합니다. 다음 링크를 통해 앱을 등록하세요. 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);
마지막으로 사용자 ID가없는 경우 생성하고 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의 이야기를 참조 하십시오. 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 여기서 정보를 얻을 수있는 다른 파일에 대해 논의합니다.
Android OS 기기의 고유 기기 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에서 제안한이 방법을 강력히 권장합니다.
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를 제공 할 수 있습니다.
절대적으로 필요한 경우 (즉, 임의로 생성 된 UUID를 사용할 때) XML로만 유지되도록 코드를 업데이트하고 @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 를 사용 하여 가져올 수 있습니다. 2.3 API 레벨 미만의 경우 TelephonyManager.getDeviceID ()를 사용하십시오 .
http://android-developers.blogspot.in/2011/03/identifying-app-installations.html을 읽을 수 있습니다.