고유 한 Android 기기 ID가 있습니까?


답변:


2024

Settings.Secure#ANDROID_ID각 사용자 64 비트 16 진 문자열 에 대해 고유 한 Android ID를 리턴합니다 .

import android.provider.Settings.Secure;

private String android_id = Secure.getString(getContext().getContentResolver(),
                                                        Secure.ANDROID_ID); 

476
때로는 null로 알려져 있으며 "초기화시 변경 가능"으로 문서화되어 있습니다. 자신의 책임하에 사용하며 루팅 된 전화기에서 쉽게 변경할 수 있습니다.
Seva Alekseyev


18
앱을 처음 실행할 때 설정되지 않거나 나중에 설정되거나 이론상 변경 될 수 있으므로 고유 ID가 변경 될 수 있기 때문에 첫 번째 답변에서 해시로 ANDROID_ID를 사용하는 경우주의해야합니다.


35
ANDROID_ID는 더 이상 장치를 고유하게 식별하지 않습니다 (4.2) : stackoverflow.com/a/13465373/150016
Tom

1145

업데이트 : 최신 버전의 Android에서 많은 문제 ANDROID_ID가 해결 되었으며이 접근법이 더 이상 필요하지 않다고 생각합니다. Anthony의 답변을 살펴보십시오 .

전체 공개 : 내 앱은 원래 아래 접근법을 사용했지만 더 이상이 접근법을 사용하지 않으며 이제는 emmby의 답변이 링크 된 (즉, 생성 및 저장 ) Android 개발자 블로그 항목에 설명 된 접근법을 사용합니다 .UUID#randomUUID()


이 질문에 대한 많은 답변이 있으며, 대부분은 "일부"만 작동하지만 불행히도 충분하지 않습니다.

장치에 대한 나의 테스트 (적어도 하나는 활성화되지 않은 모든 전화기)를 기반으로합니다.

  1. 테스트 된 모든 장치는 TelephonyManager.getDeviceId()
  2. 모든 GSM 장치 (모두 SIM으로 테스트)는 다음 값을 반환했습니다. TelephonyManager.getSimSerialNumber()
  3. 모든 CDMA 장치에서 null을 반환했습니다. getSimSerialNumber() 가 예상대로
  4. Google 계정이 추가 된 모든 기기에서 ANDROID_ID
  5. 모든 CDMA 장치는 모두 동일한 값 (또는 동일한 값을 유도)를 반환 ANDROID_ID하고 TelephonyManager.getDeviceId()- 길이만큼 구글 계정을 설치하는 동안 추가되었습니다.
  6. SIM이없는 GSM 장치, Google 계정이 추가되지 않은 GSM 장치 또는 비행기 모드의 장치는 아직 테스트 할 기회가 없었습니다.

따라서 장치 자체에 고유 한 것을 원하면 충분 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;

151
전화 기반 ID는 태블릿 장치에 없습니다.
Seva Alekseyev

22
따라서 내가 항상 작동하지 않는다고 말한 이유는 무엇입니까? 이것이 바로이 질문이 시작되는 이유입니다. 이것에 대한 최후의 해결책이 없다는 것이 분명합니다. 개별 장치 제조업체에는 장치 일련 번호가있을 수 있지만 이러한 장치 일련 번호는 노출되지 않으므로 사용하지 않아도됩니다. 따라서 우리는 우리에게 가능한 것을 남겼습니다.
Joe

31
코드 샘플은 훌륭하게 작동합니다. <uses-permission android:name="android.permission.READ_PHONE_STATE" />매니페스트 파일 에 추가 해야합니다. 데이터베이스에 저장하는 경우 반환되는 문자열의 길이는 36 자입니다.
Richard


18
@softarn : 나는 당신이 언급하는 것은 이미 당신이 무엇을 설명하는 연결 emmby 안드로이드 개발자 블로그 믿고 노력하고 그래서 아마 당신은 단순히 대신 자신의 의견을 upvoted 있어야 말을. 어느 쪽이든, emmby가 그의 답변에서 언급했듯이 블로그 정보에도 여전히 문제가 있습니다. 질문은 고유 한 DEVICE 식별자 (설치 식별자가 아님)를 요구하므로 귀하의 진술에 동의하지 않습니다. 블로그는 당신이 원하는 것이 반드시 장치를 추적하는 것은 아니라고 가정 하지만 질문은 단지 그것을 요구합니다. 그렇지 않으면 블로그에 동의합니다.
Joe

438

최종 업데이트 : 15. 6. 2.


고유 ID, Google 개발자 블로그 및 Android 설명서 작성에 대한 모든 스택 오버플로 게시물을 읽은 후 '의사 ID'가 최상의 옵션 인 것 같습니다.

주요 문제 : 하드웨어 및 소프트웨어

하드웨어

  • 사용자는 하드웨어, Android 태블릿 또는 휴대 전화를 변경할 수 있으므로 하드웨어를 기반으로 한 고유 ID는 추적 사용자 에게 적합하지 않습니다.
  • 대한 추적 하드웨어 , 이것은 좋은 생각이다

소프트웨어

  • 사용자는 루팅 된 경우 ROM을 지우거나 변경할 수 있습니다
  • 여러 플랫폼 (iOS, Android, Windows 및 웹)에서 사용자를 추적 할 수 있습니다.
  • 에 최선의 희망 TRACK 사용할 개인 사용자 자신과 동의는 단순히 (OAuth를 사용하여이 원활한을)를 로그인하는 것입니다

Android의 전반적인 고장

-API> = 9/10 (Android 기기의 99.5 %)에 대한 고유성 보장 (루팅 된 기기 포함)

-추가 권한이 없습니다

슈도 코드 :

if API >= 9/10: (99.5% of devices)

return unique ID containing serial id (rooted devices may be different)

else

return the unique ID of build information (may overlap data - API < 9)

모든 스택 옵션 (이 스택 오버플로 질문) 을 게시 해 주신 @stansult에게 감사드립니다 .

옵션 목록-사용 이유 / 사용하지 않는 이유 :

  • 사용자 이메일-소프트웨어

    • 사용자가 이메일을 변경할 수 있음-거의 없을 것
    • API 5 <uses-permission android:name="android.permission.GET_ACCOUNTS" />이상
    • API 14+ <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" />( 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 일 수 있음, 초기화시 변경 가능, 루팅 된 기기에서 변경 가능)

    • 'null'일 수 있으므로 'null'을 확인하고 값을 변경할 수 있지만 더 이상 고유하지 않습니다.
    • 출고시 재설정 장치를 사용하는 사용자가있는 경우, 루팅 된 장치에서 값이 변경되거나 변경되어 사용자 설치를 추적하는 경우 항목이 중복 될 수 있습니다.
  • WLAN MAC 주소-하드웨어 (필요 android.permission.ACCESS_WIFI_STATE)

    • 이것은 두 번째 가장 좋은 옵션 일 수 있지만 여전히 사용자가 직접 제공하는 고유 식별자를 수집하여 저장하고 있습니다. 데이터를 수집하고 있음이 분명합니다.
    • <uses-permission android:name="android.permission.ACCESS_WIFI_STATE "/>
  • Bluetooth MAC 주소-하드웨어 (Bluetooth가있는 장치, 필요 android.permission.BLUETOOTH)

    • 시장에 나와있는 대부분의 응용 프로그램은 Bluetooth를 사용하지 않으므로 응용 프로그램에서 Bluetooth를 사용하지 않고이를 포함하면 사용자가 의심 될 수 있습니다.
    • <uses-permission android:name="android.permission.BLUETOOTH "/>
  • 의사 고유 ID-소프트웨어 (모든 Android 장치 용)

    • 매우 가능하며 충돌이있을 수 있습니다-아래에 게시 된 내 방법을 참조하십시오!
    • 개인 정보를 사용하지 않고도 사용자로부터 '거의 고유 한'ID를 가질 수 있습니다. 장치 정보에서 자신의 익명 ID를 만들 수 있습니다.

권한을 사용하지 않고 고유 ID를 얻는 '완벽한'방법이 없다는 것을 알고 있습니다. 그러나 때로는 장치 설치 만 추적하면됩니다. 고유 ID를 만들 때 추가 권한을 사용하지 않고 Android API가 제공 한 정보만으로 '의사 고유 ID'를 만들 수 있습니다. 이런 식으로, 우리는 사용자를 존중하고 좋은 사용자 경험을 제공하려고 노력할 수 있습니다.

의사 고유 ID를 사용하면 실제로 유사한 장치가 있다는 사실을 기반으로 중복이있을 수 있다는 사실 만 알게됩니다. 결합 된 방법을보다 독창적으로 조정할 수 있습니다. 그러나 일부 개발자는 장치 설치를 추적해야하며 유사한 장치를 기반으로 트릭이나 성능을 수행합니다.

API> = 9 :

Android 기기가 API 9 이상인 경우 'Build.SERIAL'필드 때문에 고유해야합니다.

기억하십시오 , API <9 인 사용자의 약 0.5 %만이 기술적으로 누락되었습니다 . 나머지 부분에만 집중할 수 있습니다. 사용자의 99.5 %입니다.

API <9 :

사용자의 Android 기기가 API 9보다 낮은 경우 희망적으로, 그들은 공장 초기화를하지 않았고 그들의 'Secure.ANDROID_ID'는 보존되거나 'null'이 아닐 것이다. ( http://developer.android.com/about/dashboards/index.html 참조 )

다른 모든 것이 실패하면 :

사용자가 API 9보다 낮거나 (Gingerbread보다 낮은) 기기를 재설정했거나 'Secure.ANDROID_ID'가 'null'을 반환하면 다른 모든 방법이 실패하면 단순히 반환 된 ID는 Android 기기 정보를 기반으로합니다. 충돌이 발생할 수있는 곳입니다.

변경 사항 :

  • 초기화로 인해 'Android.SECURE_ID'가 제거되어 값이 변경 될 수 있음
  • API에서 변경하도록 코드를 편집했습니다.
  • 의사 변경

아래 방법을 살펴보십시오.

/**
 * Return pseudo unique ID
 * @return ID
 */
public static String getUniquePsuedoID() {
    // If all else fails, if the user does have lower than API 9 (lower
    // than Gingerbread), has reset their device or 'Secure.ANDROID_ID'
    // returns 'null', then simply the ID returned will be solely based
    // off their Android device information. This is where the collisions
    // can happen.
    // Thanks http://www.pocketmagic.net/?p=1662!
    // Try not to use DISPLAY, HOST or ID - these items could change.
    // If there are collisions, there will be overlapping data
    String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);

    // Thanks to @Roman SL!
    // https://stackoverflow.com/a/4789483/950427
    // Only devices with API >= 9 have android.os.Build.SERIAL
    // http://developer.android.com/reference/android/os/Build.html#SERIAL
    // If a user upgrades software or roots their device, there will be a duplicate entry
    String serial = null;
    try {
        serial = android.os.Build.class.getField("SERIAL").get(null).toString();

        // Go ahead and return the serial for api => 9
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
    } catch (Exception exception) {
        // String needs to be initialized
        serial = "serial"; // some value
    }

    // Thanks @Joe!
    // https://stackoverflow.com/a/2853253/950427
    // Finally, combine the values we have found by using the UUID class to create a unique identifier
    return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}

새로운 기능 (광고 및 Google Play 서비스가있는 앱) :

Google Play 개발자 콘솔에서 :

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

정보를 얻은 모든 링크를 참조하려고했습니다. 누락되어 포함해야 할 경우 의견을 말하십시오!

Google 플레이어 서비스 인스턴스 ID

https://developers.google.com/instance-id/


그러나 BuildOS 업데이트시 클래스가 변경 되지 않습니까? 특히 API가 업데이트 된 경우? 그렇다면 이것이 고유하다는 것을 어떻게 보증합니까? (당신이 쓴 방법에 대해 말하기)
LuckyMe

2
의견을 보내기 위해 내 앱에서 방법을 사용했습니다. 나쁜 소식이 있습니다. 불행히도 PsuedoID는 완전히 고유하지 않습니다. 내 서버는 5 개의 ID로 100 이상, 거의 30 개의 ID로 30 이상을 기록했습니다. 가장 많이 반복되는 ID는 'ffffffff-fc8f-6093-ffff-ffffd8'(159 레코드) 및 'ffffffff-fe99-b334-ffff-ffffef'(154 번)입니다. 또한 시간과 의견을 바탕으로 다른 사람들이 있음이 분명합니다. 지금까지의 총 레코드 수는 10,000입니다. 왜 이런 일이 있었는지 알려주십시오. 탱크.
hojjat reyhane 2016 년

1
나는 1.5 년 전에 이것을 썼다. 왜 이것이 당신에게 고유하지 않은지 잘 모르겠습니다. 광고 ID를 사용해보십시오. 그렇지 않은 경우 자신의 솔루션을 생각해 낼 수 있습니다.
Jared Burrows

2
sorta .. 당신이 질문을 통해 이것에 대해 당신의 생각을한다면 정말 감사하겠습니다
Durai Amuthan.H

1
감사합니다. 나는 이것을 모두에게 최신 상태로 유지하려고합니다. 이 질문은 하드웨어 대 소프트웨어 및 크로스 플랫폼과 관련하여 까다 롭습니다.
Jared Burrows

340

Dave Webb가 언급했듯이 Android 개발자 블로그에는 이를 다루는 기사 가 있습니다. 그들이 선호하는 솔루션은 기기가 아닌 앱 설치를 추적하는 것이며 대부분의 사용 사례에서 잘 작동합니다. 블로그 게시물에는 해당 코드를 작동시키는 데 필요한 코드가 표시되어 있으므로 확인하시기 바랍니다.

그러나 앱 설치 식별자가 아닌 장치 식별자가 필요한 경우 블로그 게시물에서 솔루션에 대해 논의합니다. Google 담당자에게 문의하여 필요한 경우 몇 가지 항목에 대한 추가 설명을 얻었습니다. 위에서 언급 한 블로그 게시물에서 언급하지 않은 장치 식별자에 대해 발견 한 내용은 다음과 같습니다.

  • ANDROID_ID는 기본 장치 식별자입니다. ANDROID_ID는 Android <= 2.1 또는> = 2.3 버전에서 완벽하게 신뢰할 수 있습니다. 게시물에 언급 된 문제는 2.2 만입니다.
  • 여러 제조업체의 여러 장치가 2.2의 ANDROID_ID 버그의 영향을받습니다.
  • 내가 결정할 수있는 한, 영향을받는 모든 장치는 동일한 ANDROID_ID9774d56d682e549c 입니다. 에뮬레이터에서보고 한 것과 동일한 장치 ID 인 btw도 있습니다.
  • Google은 OEM이 대부분 또는 대부분의 기기에서이 문제를 해결했다고 생각하지만, 2011 년 4 월 초부터 ANDROID_ID가 손상된 기기를 찾는 것이 여전히 쉬운 지 확인할 수있었습니다.

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;
    }
}

6
다양한 ID를 모두 해시하여 크기가 모두 같아야합니까? 또한 개인 정보가 실수로 노출되지 않도록 장치 ID를 해싱해야합니다.
Steve Pomeroy

2
좋은 지적이야, 스티브 항상 UUID를 반환하도록 코드를 업데이트했습니다. 이를 통해 a) 생성 된 ID의 크기가 항상 같아야하며 b) 실수로 개인 정보가 노출되지 않도록 반환하기 전에 Android 및 장치 ID가 해시됩니다. 또한 기기 ID가 공장 초기화시에도 유지되며 일부 사용자에게는 바람직하지 않을 수 있도록 설명을 업데이트했습니다.
emmby

1
나는 당신이 틀렸다고 믿습니다. 선호되는 솔루션은 장치 식별자가 아닌 설치를 추적하는 것입니다. 귀하의 코드는 블로그 게시물의 코드보다 훨씬 길고 복잡하며 어떤 가치를 추가하는지 명확하지 않습니다.
팀 브레이

7
좋은 지적은 사용자가 장치 ID 대신 앱 설치 ID를 사용하도록 강력하게 제안하는 주석을 업데이트 한 것입니다. 그러나이 솔루션은 여전히 ​​설치 ID가 아닌 장치가 필요한 사람들에게 유용하다고 생각합니다.
emmby

8
ANDROID_ID는 초기화시 변경 될 수 있으므로 장치도 식별 할 수 없습니다.
Samuel

180

다음은 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가 있어야합니다. 앞으로 분석에서 (즉, 아직 그 일을하지 않았습니다 :).


고유 ID에 현재 시간이 추가 된 @Lenn Dolling의 방법을 사용했습니다. 그러나 이것은 더 간단하고 신뢰할 수있는 방법처럼 보입니다. 감사합니다 Reto Meier와 Antony Nolan
Gökhan Barış Aker

훌륭하지만 루팅 된 기기는 어떻습니까? 그들은 이것에 접근하여 uid를 다른 것으로 쉽게 바꿀 수 있습니다.
tasomaniac

3
제거 후 다시 설치 한 후에도 고유 ID가 필요하지 않은 경우 훌륭한 옵션입니다 (예 : 3 번의 기회를 얻는 프로모션 이벤트 / 게임, 기간).
Kyle Clegg 21

2
Meier 프리젠 테이션은 Android Backup Manager 사용에 의존하며,이 기능은 사용자가 해당 기능을 켜도록 선택합니다. 사용자가 해당 옵션을 선택하지 않으면 백업 된 옵션을 얻지 못하기 때문에 앱 사용자 기본 설정 (Meier의 사용)에 적합합니다. 그러나 원래 질문은 장치에 대한 고유 ID를 생성하는 것에 관한 것이며,이 ID는 장치 별로는 물론 설치 당하지 않고 앱별로 생성되며 사용자가 백업 옵션을 선택하는 데 의존하기 때문에 사용자 이외의 용도로 사용됩니다 선호도 (예 : 시간 제한 시험)는 제한적입니다.
Carl

12
데이터를 제거하거나 지우면 작동하지 않습니다.
John Shelley

106

또한 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


8
이 필요android.permission.ACCESS_WIFI_STATE
ohhorob

5
WiFi가 꺼져 있으면 거의 모든 안드로이드 장치에서 사용할 수 없다는 것을 알게 될 것입니다. WiFi를 끄면 커널 수준에서 장치가 제거됩니다.
chrisdowney

12
@Sanandrea-루팅 된 기기에서 모든 것을 스푸핑 할 수 있습니다.
ocodo

5
WiFi MAC 주소 액세스가 Android M에서 차단되었습니다 : stackoverflow.com/questions/31329733/…
breez

6
안드로이드 6.x부터는 일관된 가짜 맥 주소를 반환합니다 :02:00:00:00:00:00
Behrouz.M

87

여기에 유용한 정보가 있습니다 .

다섯 가지 ID 유형이 있습니다.

  1. IMEI (전화를 사용하는 Android 기기에만 해당, 필요 android.permission.READ_PHONE_STATE)
  2. 의사 고유 ID (모든 Android 장치 용)
  3. Android ID (null 일 수 있음, 초기화시 변경 가능, 루팅 된 전화에서 변경 가능)
  4. WLAN MAC 주소 문자열 (필요 android.permission.ACCESS_WIFI_STATE)
  5. BT MAC 주소 문자열 (Bluetooth가있는 장치, 필요 android.permission.BLUETOOTH)

2
생략 된 중요 사항 (여기 및 기사에서) : WLAN 또는 BT MAC이 켜져 있지 않으면 얻을 수 없습니다! 그렇지 않으면 WLAN MAC이 완벽한 식별자라고 생각합니다. 사용자가 Wi-Fi를 켤 것이라는 보장은 없으며 실제로 직접 켜는 것이 '적절한 것'이라고 생각하지 않습니다.
Tom

1
@Tom 당신이 틀렸다. WLAN 또는 BT MAC이 꺼져 있어도 계속 읽을 수 있습니다. 그러나 장치에 사용 가능한 WLAN 또는 BT 모듈이 있다는 보장은 없습니다.
Marqs

2
로컬 WiFi 및 Bluetooth MAC 주소는 더 이상 사용할 수 없습니다. aWifiInfo 객체의 getMacAddress () 메소드와 BluetoothAdapter.getDefaultAdapter (). getAddress () 메소드는 지금부터 02 : 00 : 00 : 00 : 00 : 00을 반환합니다.
sarika kate

4
@sarikakate 6.0 Marshmallow 이상에서만 적용됩니다. 6.0 Marshmallow 이하에서도 여전히 작동합니다.
Smeet

@Smeet 그래 맞아. 6.0 이하에서 작동한다는 것을 잊어 버렸다
sarika kate

51

공식 Android 개발자 블로그에는 이제 바로이 주제에 대한 전체 기사 인 앱 설치 식별 이 있습니다.


4
그리고이 주장의 핵심은 하드웨어에서 고유 한 ID를 얻으려고한다면 실수를 저지른 것입니다.
팀 브레이

3
또한 공장 초기화를 통해 장치 잠금을 재설정 할 수 있다면 시험판 모델은 죽은 것입니다.
Seva Alekseyev

43

에서 구글 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을 참조하십시오 .

백업 에이전트를 구현하는 방법에 대한 자세한 내용은 데이터 백업을 참조하십시오 . 백업이 즉시 수행되지 않으므로 테스트를 수행하려면 백업 맨 아래 섹션을 사용하는 것이 좋습니다.


5
사용자가 여러 장치를 사용할 때 동일한 ID를 가진 여러 장치로 연결되지 않습니까? 예를 들어 태블릿과 전화.
Tosa

이를 위해서는 최소 목표 8이 필요합니다.
halxinate

인앱 구매를 할 때 확인 페이로드를 만드는 데 선호되는 방법입니까? 인앱 결제 예제 코드에서 다음과 같이 설명합니다. "좋은 개발자 페이로드에는 다음과 같은 특징이 있습니다. 1. 두 명의 다른 사용자가 아이템을 구매하는 경우 한 사용자의 구매를 다른 사용자에게 재생할 수 없도록 해당 아이템간에 페이로드가 다릅니다. 2. 페이로드는 앱이 구매 흐름을 시작한 사람이 아닌 경우에도이를 확인할 수 있도록해야합니다 (따라서 한 장치에서 사용자가 구매 한 항목이 사용자가 소유 한 다른 장치에서 작동하도록해야합니다. "
TouchBoarder

@ 토사 나는 같은 질문을했다. 그러나이 같은 기술을 다시 사용하여 가상 장치 ID를 만들 수없고 같은 방식으로 백업 할 수 없었습니까? 장치 ID는 삭제 또는 재설치 후에도 지속되지 않지만 영구 사용자 ID가 있으면 장치 ID에서 그다지 필요하지 않을 수 있습니다.
jwehrle

39

나는 이것이 독특한 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 의 기사에서 복사 한 것 입니다.)


7
재미있는 해결책. 이것은 자신의 "해시"기능을 사용하는 대신 연결된 모든 데이터를 해시해야하는 상황 인 것 같습니다. 각 값마다 다른 데이터가 있어도 충돌이 발생하는 경우가 많습니다. 내 권장 사항 : 해시 함수를 사용한 다음 이진 결과를 10 진수로 변환하고 필요에 따라 자릅니다. 제대로하려면 UUID 또는 전체 해시 문자열을 사용해야합니다.
스티브 포메 로이

21
당신은 당신의 출처를 인정해야합니다 ... 이것은 다음 기사에서 바로 해제되었습니다 : pocketmagic.net/?p=1662
Steve Haley

8
이 ID는 무엇을 모르는 것처럼 충돌에 노출됩니다. 실제로 동일한 캐리어의 동일한 장치에서 동일하게 보장됩니다.
Seva Alekseyev

7
장치가 업그레이드 된 경우에도 변경 될 수 있습니다.
David 주어진

8
아주 나쁜 해결책입니다. 두 개의 Nexus 5에서 테스트되었습니다. 같은 숫자를 반환합니다.
Sinan Dizdarević 2016 년

38

다음 코드는 숨겨진 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) {

}

방금 xda 개발자에서 ro.serialno를 생성하는 데 사용 된다는 것을 읽었습니다 Settings.Secure.ANDROID_ID. 그래서 그들은 기본적으로 같은 가치의 다른 표현입니다.
Martin

@Martin : 장치를 재설정해도 일련 번호는 변경되지 않습니다. 그렇지 않습니까? 단지 새로운 가치 ANDROID_ID가 그것에서 파생됩니다.
Ronnie

실제로 모든 장치에서 동일한 곳에서 테스트했습니다. 또는 적어도 동일한 해시 값 (개인 정보 보호를 위해 로그 파일에 실제 값을 쓰지 않습니다).
Martin

이 값은android.os.Build.SERIAL
eugeneek

android.os.Build.SERIAL는 안드로이드 O에서 더 이상 사용되지 않습니다, android-developers.googleblog.com/2017/04/…를
EpicPandaForce

32

간단한 대답이없는 간단한 질문입니다.

또한 기존의 모든 답변은 오래되었거나 신뢰할 수 없습니다.

따라서 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-식별자 및 유사한 규정 에 따라 법으로해야합니다 .


3
지금, 이것은 최선의 대답이며, 첫 번째 문장 최선을 요약 한 것입니다 : "그것은 간단한 질문은의 간단한 대답없이 - 그냥 사랑 해요.
b2mob

해당 사용 사례를 해결하는 방법에 대한 답변이없는 "지불 사기 방지 및 전화 제외"의견에 감사해야합니다.
Eran Boudjnah

@EranBoudjnah 공식 참조의 인용문은 답변과 관련이 있습니다. 이 유스 케이스를 처리하려고 시도 할 수는 있지만 OP 질문에만 국한되지 않으므로 스택 오버 플로우 정책에 따라 별도의 전용 질문으로 수행해야합니다.
니키타 쿠 르틴

나는 그 대답이 불완전하다는 것을 말하고 있습니다. 그러나 나는 그것이 당신의 잘못이 아니라는 것을 알고 있습니다.
Eran Boudjnah

1
@ M.UsmanKhan, 그 대답은 그 직후에 쓰여졌습니다. " 오늘 그것은 최상의 방법 일뿐만 아니라, 실제로 GDPR-식별자 및 유사한 규정에 따라 법으로해야합니다. "
Nikita Kurtin

27

아래 코드를 사용하면 Android OS 기기의 고유 기기 ID를 문자열로 얻을 수 있습니다.

deviceId = Secure.getString(getApplicationContext().getContentResolver(), Secure.ANDROID_ID); 

21

직렬 필드는 가하고Build (- 진저 브레드 안드로이드 2.3) API 레벨 9 클래스입니다. 설명서에 하드웨어 일련 번호가 표시되어 있습니다. 따라서 장치에 존재하는 경우 고유해야합니다.

API 수준이 9보다 큰 모든 장치에서 실제로 지원되는지 (= null이 아닌지) 모르겠습니다.


2
불행히도 "알 수 없음"입니다.
m0skit0

18

내가 추가 할 한 가지-나는 독특한 상황 중 하나를 가지고 있습니다.

사용 :

deviceId = Secure.getString(this.getContext().getContentResolver(), Secure.ANDROID_ID);

Viewsonic G Tablet이 Null이 아닌 DeviceID를보고하더라도 모든 G Tablet은 모두 같은 숫자를보고합니다.

"고유 한"DeviceID를 기반으로 누군가의 계정에 즉시 액세스 할 수있는 "Pocket Empires"를 재생하는 것이 재미있어집니다.

내 장치에는 셀 라디오가 없습니다.


아이디는 무엇입니까? 그것은 perchance 9774d56d682e549c입니까?
Mr_and_Mrs_D

와우 너무 오래 전에 나는 그 태블릿을 던진 지 오래되었습니다. 말할 수 없었습니다.
Tony Maro

공장. 또한 임의의 UUID에서 얻은 ID보다 훨씬 컴팩트합니다.
Treewallie

1
@Treewallie 작동합니까? 다른 앱에서 동일한 기기 ID를 얻을 수 있습니까?
아놀드 브라운

@ArnoldBrown 예. 철저히 테스트하십시오. 좋은 하루 되세요 : D
Treewallie 1

16

애플리케이션이 설치된 각 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();
    }
}

앱 설치를 추적하려면 완벽합니다. 추적 장치는 훨씬 까다 롭고 완벽한 밀폐 솔루션은 아닙니다.
Luca Spiller

루팅 된 기기는 어떻습니까? 이 설치 ID를 쉽게 변경할 수 있습니다.
tasomaniac

물론. 루트는 설치 ID를 변경할 수 있습니다. 이 코드 블록을 사용하여 루트를 확인할 수 있습니다. stackoverflow.com/questions/1101380/…
Kevin Parker

공장을 재설정하면 파일이 삭제됩니까?
Jamshid

/ data 파티션을 초기화하고 삭제하거나 포맷하면 UUID가 다릅니다.
케빈 파커

12

클래스 파일에 아래 코드를 추가하십시오.

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" />

10

TelephonyManagerand를 사용하여 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에서 제안한 방법을 강력히 권장합니다 ( 앱 설치 식별 참조) .


9

장단점을 가지고 이러한 ANDROID_ID문제를 해결하는 방법에는 여러 가지 가 있습니다 ( null종종 또는 특정 모델의 장치가 항상 동일한 ID를 반환 할 수도 있음 ).

  • 사용자 정의 ID 생성 알고리즘 구현 (정적이며 변경되지 않아야하는 장치 속성을 기반으로-> 아는 사람)
  • IMEI , 일련 번호, Wi-Fi / Bluetooth-MAC 주소 와 같은 다른 ID 남용 (일부 장치에 존재하지 않거나 추가 권한이 필요함)

본인 은 Android 용 기존 OpenUDID 구현 ( https://github.com/ylechelle/OpenUDID 참조 )을 선호합니다 ( https://github.com/vieux/OpenUDID 참조 ). ANDROID_ID위에서 언급 한 문제에 대해 통합이 쉽고 대체 기능을 사용합니다 .


8

IMEI 는 어떻습니까 . 이는 Android 또는 다른 모바일 장치에 고유합니다.


9
내 이동 통신사에 연결되지 않아 IMEI가없는 태블릿에는 적합하지 않습니다.
Brill Pappin

2
IMEI 대신 ESN이있는 CDMA 장치는 말할 것도 없습니다.
David 주어진

@David 주어진 안드로이드와 CDMA가 있습니까?
Elzo Valugi 2012 년

1
그것은 단지 그것을하다 할 것 입니다 전화 : 태블릿이되지 않을 수도 있습니다.
Brill Pappin

3
@ElzoValugi 이미 "현재"이지만 모든 태블릿에 SIM 카드가있는 것은 아닙니다.
Matthew Quiros

8

고유 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;
}

런타임 버전을 요구하는 6.0 버전의 ReadPhoneState를 사용하는 경우
Harsha

8

내 두 센트-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);
        }
    }
}

8

여기에 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 "";
}

1
UUID 를 추가하고 longId파일에 저장하면 가장 고유 한 식별자가됩니다.String uuid = UUID.randomUUID().toString();
Mousa Alfhaily

1
다른 모든 방법이 실패하면 사용자가 API 9보다 낮 으면 (Gingerbread보다 낮음) 휴대 전화 또는 'Secure.ANDROID_ID'를 재설정 한 것입니다. 'null'을 반환하면 반환 된 ID는 전적으로 Android 기기 정보를 기반으로합니다. 충돌이 발생할 수있는 곳입니다. DISPLAY, HOST 또는 ID를 사용하지 마십시오. 이러한 항목은 변경 될 수 있습니다. 충돌이 있으면 중복되는 데이터가 있습니다. 출처 : gist.github.com/pedja1/fe69e8a80ed505500caa
Mousa Alfhaily

이 코드 라인으로 고유 번호를 얻으려고하면 고유 ID이며 다른 장치와 충돌하지 않는다고 말할 수 있습니까?
닌자

1
@Ninja BLE mac 주소는 고유하기 때문에 생성 된 ID는 항상 고유합니다. 그러나 확실하게 확인하려면에 UUID를 추가하는 것이 좋습니다 longId. 다음과 같이 한 줄을 변경하십시오. String longId = pseudoId + androidId + btId + UUID.randomUUID().toString();이렇게하면 생성 된 ID가 고유합니다.
ᴛʜᴇᴘᴀᴛᴇʟ

@ ᴛʜᴇᴘᴀᴛᴇʟ이 큰 도움에 감사드립니다. 실제로 내 응용 프로그램은 매우 민감한 데이터이므로 확인해야합니다.
닌자

7

다른 방법은 /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 파일을 열고 문자를 읽으십시오. 모든 장치에이 파일이있는 것은 아니기 때문에 예외 처리기로 래핑해야합니다.

최소한 다음 장치는이 파일을 세계에서 읽을 수있는 것으로 알려져 있습니다.

  • 갤럭시 넥서스
  • 넥서스 S
  • 모토로라 Xoom 3G
  • 도시바 AT300
  • HTC 하나 V
  • 미니 MK802
  • 삼성 갤럭시 S II

또한 내 블로그 게시물 권한이없는 앱에 Android 하드웨어 일련 번호 누출 정보를 제공하는 다른 파일에 대해 논의 할 수 있습니다.


방금 블로그 게시물을 읽었습니다. 나는 이것이 고유하지 않다고 생각합니다. Build.SERIAL은 모든 권한없이 사용할 수 있으며 이론상 고유 하드웨어 일련 번호입니다.
Tom

1
네가 옳아. 기기를 추적 할 수있는 또 하나의 방법 일 뿐이며이 두 가지 방법 모두 앱 권한이 필요하지 않습니다.
insitusec

7

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;
}

최근에 SM-G928F / Galaxy S6 edge + 유형의 클라이언트 기기가 Android ID에 16 진수 대신 16 자리 만 제공한다는 것을 발견했습니다.
Holger Jakobs 2016 년

7

특정 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가 포함 된 목록에서 얻을 수있는 다른 인터페이스들입니다. 그러나 이것은 전화기마다 바뀔 수 있습니다.


7

다음 코드를 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);

7

보다 구체적으로 Settings.Secure.ANDROID_ID. 이것은 장치가 처음 부팅 될 때 생성 및 저장되는 64 비트 수량입니다. 장치를 닦으면 재설정됩니다.

ANDROID_ID고유 한 기기 식별자에 적합합니다. 단점 : 첫째, 2.2 이전의 Android 릴리스에서는 100 % 신뢰할 수 없음 (“Froyo”).또한 주요 제조업체의 인기있는 핸드셋에서 모든 인스턴스가 동일한 ANDROID_ID를 갖는 버그가 하나 이상 발견되었습니다.


1
이 답변은 이전 Google 블로그 android-developers.googleblog.com/2011/03/…의 사본 입니다. 버그가 이미 해결 되었습니까?
Sergii


6

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 초가 번거로울 수 있다고 생각합니다 (물론 앱에 따라 다름).


1
instanceID의 또 다른 중요한 단점은 사용자가 앱의 데이터를 지우면 새 instanceID가 생성된다는 것입니다.
idanakav

흥미롭지 만 실제로 잠재적 인 사용 사례를 변경하지는 않는다고 생각합니다. android_id와 같은 인스턴스 ID는 장치를 식별하는 데 적합하지 않습니다. 따라서 서버는 사용자가 앱을 제거했다가 다시 설치하는 것처럼 데이터를 지우는 것을 볼 수 있습니다.
Tom
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.