Android 기기에는 고유 ID가 있습니까? 그렇다면 Java를 사용하여 액세스하는 간단한 방법은 무엇입니까?
Android 기기에는 고유 ID가 있습니까? 그렇다면 Java를 사용하여 액세스하는 간단한 방법은 무엇입니까?
답변:
Settings.Secure#ANDROID_ID
각 사용자 64 비트 16 진 문자열 에 대해 고유 한 Android ID를 리턴합니다 .
import android.provider.Settings.Secure;
private String android_id = Secure.getString(getContext().getContentResolver(),
Secure.ANDROID_ID);
업데이트 : 최신 버전의 Android에서 많은 문제 ANDROID_ID
가 해결 되었으며이 접근법이 더 이상 필요하지 않다고 생각합니다. Anthony의 답변을 살펴보십시오 .
전체 공개 : 내 앱은 원래 아래 접근법을 사용했지만 더 이상이 접근법을 사용하지 않으며 이제는 emmby의 답변이 링크 된 (즉, 생성 및 저장 ) Android 개발자 블로그 항목에 설명 된 접근법을 사용합니다 .UUID#randomUUID()
이 질문에 대한 많은 답변이 있으며, 대부분은 "일부"만 작동하지만 불행히도 충분하지 않습니다.
장치에 대한 나의 테스트 (적어도 하나는 활성화되지 않은 모든 전화기)를 기반으로합니다.
TelephonyManager.getDeviceId()
TelephonyManager.getSimSerialNumber()
getSimSerialNumber()
가 예상대로ANDROID_ID
ANDROID_ID
하고 TelephonyManager.getDeviceId()
- 길이만큼 구글 계정을 설치하는 동안 추가되었습니다.따라서 장치 자체에 고유 한 것을 원하면 충분 TM.getDeviceId()
해야 합니다. 분명히 일부 사용자는 다른 사용자보다 편집증이 많으므로 문자열이 실제로 장치에 고유하지만 사용자의 실제 장치를 명시 적으로 식별하지 않도록 이러한 식별자 중 하나 이상을 해시하는 것이 유용 할 수 있습니다. 예를 들어, 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
그것은 나를 위해 충분히 잘 작동합니다.
Richard가 아래에 언급했듯이 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 자입니다.
고유 ID, Google 개발자 블로그 및 Android 설명서 작성에 대한 모든 스택 오버플로 게시물을 읽은 후 '의사 ID'가 최상의 옵션 인 것 같습니다.
슈도 코드 :
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-하드웨어 (null 일 수 있음, 초기화시 변경 가능, 루팅 된 기기에서 변경 가능)
WLAN MAC 주소-하드웨어 (필요 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 "/>
의사 고유 ID-소프트웨어 (모든 Android 장치 용)
권한을 사용하지 않고 고유 ID를 얻는 '완벽한'방법이 없다는 것을 알고 있습니다. 그러나 때로는 장치 설치 만 추적하면됩니다. 고유 ID를 만들 때 추가 권한을 사용하지 않고 Android API가 제공 한 정보만으로 '의사 고유 ID'를 만들 수 있습니다. 이런 식으로, 우리는 사용자를 존중하고 좋은 사용자 경험을 제공하려고 노력할 수 있습니다.
의사 고유 ID를 사용하면 실제로 유사한 장치가 있다는 사실을 기반으로 중복이있을 수 있다는 사실 만 알게됩니다. 결합 된 방법을보다 독창적으로 조정할 수 있습니다. 그러나 일부 개발자는 장치 설치를 추적해야하며 유사한 장치를 기반으로 트릭이나 성능을 수행합니다.
Android 기기가 API 9 이상인 경우 'Build.SERIAL'필드 때문에 고유해야합니다.
기억하십시오 , API <9 인 사용자의 약 0.5 %만이 기술적으로 누락되었습니다 . 나머지 부분에만 집중할 수 있습니다. 사용자의 99.5 %입니다.
사용자의 Android 기기가 API 9보다 낮은 경우 희망적으로, 그들은 공장 초기화를하지 않았고 그들의 'Secure.ANDROID_ID'는 보존되거나 'null'이 아닐 것이다. ( http://developer.android.com/about/dashboards/index.html 참조 )
사용자가 API 9보다 낮거나 (Gingerbread보다 낮은) 기기를 재설정했거나 'Secure.ANDROID_ID'가 'null'을 반환하면 다른 모든 방법이 실패하면 단순히 반환 된 ID는 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 개발자 콘솔에서 :
2014 년 8 월 1 일부터 Google Play 개발자 프로그램 정책에 따라 광고 목적으로 다른 영구 식별자 대신 광고 ID를 사용하려면 모든 새로운 앱 업로드 및 업데이트가 필요합니다. 더 알아보기
이행 :
허가:
<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
Google Play 서비스를 사용할 수있는 경우 광고 ID는 광고 목적으로 다른 식별자의 기존 사용법 (예 : Settings.Secure에서 ANDROID_ID 사용)을 완전히 대체합니다. Google Play 서비스를 사용할 수없는 경우 getAdvertisingIdInfo ()에 의해 발생한 GooglePlayServicesNotAvailableException이 표시됩니다.
http://en.kioskea.net/faq/34732-android-reset-your-advertising-id
정보를 얻은 모든 링크를 참조하려고했습니다. 누락되어 포함해야 할 경우 의견을 말하십시오!
Build
OS 업데이트시 클래스가 변경 되지 않습니까? 특히 API가 업데이트 된 경우? 그렇다면 이것이 고유하다는 것을 어떻게 보증합니까? (당신이 쓴 방법에 대해 말하기)
Dave Webb가 언급했듯이 Android 개발자 블로그에는 이를 다루는 기사 가 있습니다. 그들이 선호하는 솔루션은 기기가 아닌 앱 설치를 추적하는 것이며 대부분의 사용 사례에서 잘 작동합니다. 블로그 게시물에는 해당 코드를 작동시키는 데 필요한 코드가 표시되어 있으므로 확인하시기 바랍니다.
그러나 앱 설치 식별자가 아닌 장치 식별자가 필요한 경우 블로그 게시물에서 솔루션에 대해 논의합니다. Google 담당자에게 문의하여 필요한 경우 몇 가지 항목에 대한 추가 설명을 얻었습니다. 위에서 언급 한 블로그 게시물에서 언급하지 않은 장치 식별자에 대해 발견 한 내용은 다음과 같습니다.
Google의 권장 사항을 바탕으로 ANDROID_ID를 적절한 시드로 사용하여 필요에 따라 TelephonyManager.getDeviceId ()로 돌아가고 실패하면 임의로 생성 된 고유 UUID를 사용하여 각 장치에 대해 고유 한 UUID를 생성하는 클래스를 구현했습니다. 이는 앱을 다시 시작해도 지속되지만 앱을 다시 설치하지는 않습니다.
장치 ID를 대체해야하는 장치의 경우 고유 ID 가 공장 재설정을 통해 유지됩니다. 이것은 알아야 할 사항입니다. 출고시 재설정으로 고유 ID를 재설정해야하는 경우 장치 ID 대신 임의의 UUID로 직접 폴백하는 것이 좋습니다.
이 코드는 장치 ID 용이며 앱 설치 ID가 아닙니다. 대부분의 경우 앱 설치 ID가 원하는 것일 수 있습니다. 그러나 장치 ID가 필요한 경우 다음 코드가 적합 할 것입니다.
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 I / O 프레젠테이션에서 사용자의 고유 ID를 얻기 위해 사용한 코드입니다 .
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 's talk에 설명되어 있음), 사용자를 연결하고 장치가 지워지거나 교체 된 후에도 연결되는 ID가 있어야합니다. 앞으로 분석에서 (즉, 아직 그 일을하지 않았습니다 :).
또한 Wi-Fi 어댑터의 MAC 주소를 고려할 수 있습니다. 따라서 검색 :
WifiManager wm = (WifiManager)Ctxt.getSystemService(Context.WIFI_SERVICE);
return wm.getConnectionInfo().getMacAddress();
권한이 필요합니다 android.permission.ACCESS_WIFI_STATE
매니페스트에 이 .
Wi-Fi가 연결되지 않은 경우에도 사용 가능한 것으로보고되었습니다. 위의 답변에서 Joe 가이 장치를 많은 장치에서 사용해 볼 수 있다면 좋을 것입니다.
일부 기기에서는 Wi-Fi가 꺼져있을 때 사용할 수 없습니다.
참고 : Android 6.x에서는 일관된 가짜 mac 주소를 반환합니다.02:00:00:00:00:00
android.permission.ACCESS_WIFI_STATE
02:00:00:00:00:00
다섯 가지 ID 유형이 있습니다.
android.permission.READ_PHONE_STATE
)android.permission.ACCESS_WIFI_STATE
)android.permission.BLUETOOTH
)공식 Android 개발자 블로그에는 이제 바로이 주제에 대한 전체 기사 인 앱 설치 식별 이 있습니다.
에서 구글 I / O 레토 마이어 설치를 통해 사용자를 추적하는 대부분의 개발자의 요구를 충족해야하는이 문제를 접근하는 방법에 강력한 대답을 발표했다. Anthony Nolan은 그의 대답에 방향을 보여 주지만 다른 사람들이 쉽게 방법을 볼 수 있도록 전체 접근 방식을 작성한다고 생각했습니다 (세부 사항을 파악하는 데 시간이 걸렸습니다).
이 방법을 사용하면 익명의 안전한 사용자 ID가 제공되며, 이는 기본 Google 계정을 기반으로하는 여러 기기와 설치에서 사용자에게 지속됩니다. 기본 접근 방식은 임의의 사용자 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 's talk을 참조하십시오 .
백업 에이전트를 구현하는 방법에 대한 자세한 내용은 데이터 백업을 참조하십시오 . 백업이 즉시 수행되지 않으므로 테스트를 수행하려면 백업 맨 아래 섹션을 사용하는 것이 좋습니다.
나는 이것이 독특한 ID를위한 골격을 구축하는 확실한 방법이라고 생각합니다 ... 확인하십시오.
모든 Android 기기에서 작동하는 의사 고유 ID 일부 기기에는 전화 (예 : 태블릿)가 없거나 어떤 이유로 READ_PHONE_STATE 권한을 포함하고 싶지 않습니다. ROM 버전, 제조업체 이름, CPU 유형 및 기타 하드웨어 세부 사항과 같은 세부 사항을 계속 읽을 수 있습니다.이 세부 사항은 일련 키 확인 또는 기타 일반적인 목적으로 ID를 사용하려는 경우에 적합합니다. 이러한 방식으로 계산 된 ID는 고유하지 않습니다. 동일한 하드웨어 및 ROM 이미지를 기반으로 동일한 ID를 가진 두 개의 장치를 찾을 수는 있지만 실제 응용 프로그램의 변경 사항은 무시할 수 있습니다. 이를 위해 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 자리 숫자를 가지고 있으며 IMEI (15 자리)와 동일한 크기의 ID를 갖기 위해 앞에 두 개를 더 추가합니다 (35). 여기에 다른 가능성이 있습니다.이 문자열을 살펴보십시오. 와 같은 것을 반환합니다 355715565309247
. 특별한 접근이 필요하지 않으므로이 접근 방식이 매우 편리합니다.
(추가 정보 : 위에 제공된 기술은 Pocket Magic 의 기사에서 복사 한 것 입니다.)
다음 코드는 숨겨진 Android API를 사용하여 장치 일련 번호를 반환합니다. 그러나이 코드는이 장치에서 "ro.serialno"가 설정되어 있지 않아 Samsung Galaxy Tab에서 작동하지 않습니다.
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
는 안드로이드 O에서 더 이상 사용되지 않습니다, android-developers.googleblog.com/2017/04/…를
간단한 대답이없는 간단한 질문입니다.
또한 기존의 모든 답변은 오래되었거나 신뢰할 수 없습니다.
따라서 2020 년에 솔루션을 찾고 있다면 .
명심해야 할 몇 가지 사항은 다음과 같습니다.
모든 하드웨어 기반 식별자 (SSAID, IMEI, MAC 등)는 전 세계 활성 기기의 50 % 이상인 Google 이외의 기기 (픽셀 및 Nexus 제외)에는 신뢰할 수 없습니다. 따라서 공식 Android 식별자 모범 사례 에는 다음과 같이 명확하게 명시되어 있습니다.
SSAID (Android ID), IMEI, MAC 주소 등과 같은 하드웨어 식별자를 사용하지 마십시오 .
위의 답변 대부분이 유효하지 않습니다. 또한 다양한 안드로이드 보안 업데이트로 인해 일부는 더 새롭고 더 엄격한 런타임 권한이 필요하며 이는 사용자가 간단히 거부 할 수 있습니다.
예를 들어 CVE-2018-9489
위에서 언급 한 모든 WIFI 기반 기술에 영향을주는 로서.
따라서 이러한 식별자를 신뢰할 수 없을뿐만 아니라 많은 경우 액세스 할 수 없습니다.
더 간단한 말로 : 그 기술을 사용하지 마십시오 .
여기에 나오는 다른 많은 답변은를 사용 AdvertisingIdClient
하는 것이 좋습니다.이 디자인은 의도적으로 광고 프로파일 링에만 사용해야하므로 호환되지 않습니다. 공식 참조 에도 언급되어 있습니다.
사용자 프로파일 링 또는 광고 사용 사례에만 광고 ID를 사용하십시오.
기기 식별에는 신뢰할 수 없을뿐만 아니라 광고 추적과 관련 하여 사용자의 개인 정보를 준수해야합니다 정책 정책은 사용자가 언제든지 재설정하거나 차단할 수 있음을 명시합니다.
그래서 그것을 사용하지 마십시오 .
원하는 정적 전역 고유하고 안정적인 장치 식별자를 가질 수 없기 때문입니다. 안드로이드의 공식 참조 제안 :
지불 사기 방지 및 전화 통화를 제외하고 다른 모든 사용 사례에 대해 가능 하면 FirebaseInstanceId 또는 비공개 저장된 GUID를 사용하십시오 .
장치에 응용 프로그램을 설치하는 데 고유하므로 사용자가 응용 프로그램을 제거하면 응용 프로그램이 지워 지므로 100 % 신뢰할 수는 없지만 다음으로 가장 좋습니다.
최신 firebase -messaging 의존성 을 gradle에 FirebaseInstanceId
추가 하려면
implementation 'com.google.firebase:firebase-messaging:20.2.0'
그리고 백그라운드 스레드에서 아래 코드를 사용하십시오.
String reliableIdentifier = FirebaseInstanceId.getInstance().getId();
원격 서버에 장치 식별 정보를 저장해야하는 경우 그대로 (일반 텍스트) 저장하지 말고 소금이 있는 해시 .
오늘날 그것은 모범 사례 일뿐 만 아니라 실제로 GDPR-식별자 및 유사한 규정 에 따라 법으로해야합니다 .
내가 추가 할 한 가지-나는 독특한 상황 중 하나를 가지고 있습니다.
사용 :
deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);
Viewsonic G Tablet이 Null이 아닌 DeviceID를보고하더라도 모든 G Tablet은 모두 같은 숫자를보고합니다.
"고유 한"DeviceID를 기반으로 누군가의 계정에 즉시 액세스 할 수있는 "Pocket Empires"를 재생하는 것이 재미있어집니다.
내 장치에는 셀 라디오가 없습니다.
9774d56d682e549c
입니까?
애플리케이션이 설치된 각 Android 기기마다 고유 식별자를 얻는 방법에 대한 자세한 지침은 공식 Android 개발자 블로그 게시물 앱 설치 식별을 참조하십시오 .
가장 좋은 방법은 설치시 직접 생성하여 응용 프로그램을 다시 시작할 때 읽는 것입니다.
나는 개인적으로 이것을 받아 들일 수 있지만 이상적이지는 않다. 대부분의 경우 휴대 전화의 라디오 상태 (Wi-Fi 켜기 / 끄기, 셀룰러 켜기 / 끄기, 블루투스 켜기 / 끄기)에 따라 Android에서 제공하는 식별자가 모든 경우에 작동하지 않습니다. 다른 것들은 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
and를 사용하여 Android OS 기기의 고유 기기 ID는 String 이며 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
종종 또는 특정 모델의 장치가 항상 동일한 ID를 반환 할 수도 있음 ).
본인 은 Android 용 기존 OpenUDID 구현 ( https://github.com/ylechelle/OpenUDID 참조 )을 선호합니다 ( https://github.com/vieux/OpenUDID 참조 ). ANDROID_ID
위에서 언급 한 문제에 대해 통합이 쉽고 대체 기능을 사용합니다 .
IMEI 는 어떻습니까 . 이는 Android 또는 다른 모바일 장치에 고유합니다.
고유 ID를 생성하는 방법은 다음과 같습니다.
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) 고유 ID를위한 것입니다 - 안드로이드 개발자 블로그 에서 논의 된 설치 ID가 아닙니다 .
@emmby가 제공 하는 솔루션 은 SharedPreferences가 프로세스간에 동기화되지 않기 때문에 애플리케이션 ID별로 폴백됩니다 ( 여기 및 여기 참조 ). 그래서 나는 이것을 피했습니다.
대신 열거 형에서 (장치) ID를 얻는 다양한 전략을 요약했습니다. 열거 상수의 순서를 변경하면 ID를 얻는 다양한 방법의 우선 순위에 영향을 미칩니다. 널 (null)이 아닌 ID가 리턴되거나 예외가 발생합니다 (좋은 Java 관행에 따라 널 (null)에 의미를 부여하지 않음). 예를 들어 TELEPHONY를 먼저 가지고 있지만 기본적으로 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 개 이상의 답변이 있으며 일부는 동일하고 일부는 독특합니다. 이 답변은 그 답변 중 일부를 기반으로합니다. 그들 중 하나는 @Len Dolling의 답변입니다.
3 개의 ID를 결합하여 32 자리 16 진 문자열을 작성합니다. 그것은 나를 위해 아주 잘 작동했습니다.
3 개의 ID는 다음과 같습니다.
의사 ID- 실제 장치 사양을 기반으로 생성됩니다.
ANDROID_ID - Settings.Secure.ANDROID_ID
Bluetooth 주소 -Bluetooth 어댑터 주소
다음과 같이 반환됩니다 : 551F27C060712A72730B0A0F734064B1
참고 : 언제든지 longId
문자열에 ID를 더 추가 할 수 있습니다 . 예를 들어, 일련 번호입니다. 와이파이 어댑터 주소. 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 longId = pseudoId + androidId + btId + UUID.randomUUID().toString();
이렇게하면 생성 된 ID가 고유합니다.
다른 방법은 /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 () 고유 장치 ID (예 : GSM의 경우 IMEI, CDMA 전화의 경우 MEID 또는 ESN )를 반환합니다.
final TelephonyManager mTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String myAndroidDeviceId = mTelephony.getDeviceId();
그러나 나는 다음을 사용하는 것이 좋습니다.
Android ID를 고유 한 64 비트 16 진수 문자열로 반환하는 Settings.Secure.ANDROID_ID
String myAndroidDeviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID);
때때로 TelephonyManger.getDeviceId () 는 null을 반환하므로 고유 한 ID를 보장하기 위해이 메소드를 사용합니다.
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
사용하여 보안 을 얻 거나 사용합니다. 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
고유 한 기기 식별자에 적합합니다. 단점 : 첫째, 2.2 이전의 Android 릴리스에서는 100 % 신뢰할 수 없음 (“Froyo”).
또한 주요 제조업체의 인기있는 핸드셋에서 모든 인스턴스가 동일한 ANDROID_ID를 갖는 버그가 하나 이상 발견되었습니다.
Android 기기에서 사용 가능한 고유 ID를 이해합니다. 이 공식 안내서를 사용하십시오.
고유 식별자에 대한 모범 사례 :
장치를 확인하기위한 IMEI, Mac 주소, 인스턴스 ID, GUID, SSAID, 광고 ID, Safety Net API.
https://developer.android.com/training/articles/user-data-ids
Google 인스턴스 ID
I / O 2015에서 발표; 안드로이드에서는 플레이 서비스 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은이 ID를 사용하여 Android, Chrome 및 iOS에서 설치를 식별하려고합니다.
장치가 아닌 설치를 식별하지만 다시 ANDROID_ID (허용되는 답변)는 더 이상 장치를 식별하지 않습니다. ARC 런타임을 사용하면 이 새 인스턴스 ID와 마찬가지로 모든 설치에 대해 새 ANDROID_ID가 생성됩니다 ( 자세한 내용은 여기 참조 ). 또한, 우리는 대부분의 사람들이 실제로 장치를 찾는 것이 설치를 식별하는 것이라고 생각합니다.
인스턴스 ID의 장점
Google 은이 목적으로 (설치를 식별하는) 목적으로 사용하려고하며, 플랫폼 간이며 여러 다른 목적으로 사용할 수 있습니다 (위 링크 참조).
GCM을 사용하는 경우 GCM 토큰 (이전 GCM 등록 ID를 대체 함)을 가져 오기 위해이 인스턴스 ID가 필요하므로 결국이 인스턴스 ID를 사용해야합니다.
단점 / 문제
현재 구현 (GPS 7.5)에서 앱이 요청하면 인스턴스 ID가 서버에서 검색됩니다. 이것은 위의 호출이 차단 호출이라는 것을 의미합니다. 비과학적인 테스트에서는 장치가 온라인 상태 인 경우 1 ~ 3 초, 오프라인 상태 인 경우 0.5 ~ 1.0 초 (아마도 포기하고 생성하기까지 대기하는 시간입니다) 임의의 ID). Android 5.1.1 및 GPS 7.5가 설치된 Nexus 5에서 북미 지역에서 테스트되었습니다.
의도 한 목적으로 ID를 사용하는 경우-예. 앱 인증, 앱 식별, GCM-이 1-3 초가 번거로울 수 있다고 생각합니다 (물론 앱에 따라 다름).
ANDROID_ID
반드시 읽어주십시오 이 답변 하고 이 버그를 .