Android 안드로이드 - 기기 단말 정보 가져오기
페이지 정보
본문
안드로이드는 휴대폰을 구분할 수 있는 변경 불가능한 식별자(이하 편의상 UID, Unique identifier)를 제공해 왔습니다.
ID의 특징
ID는 다음과 같은 특징이 있는데, 사용하는 이유와 목적에 따라 적절한 ID를 선택하는 것이 좋습니다.
Scope
안드로이드에서는 다음 3가지 경우의 Scope가 존재합니다
-. Single 앱의 ID : 다른 앱과 ID를 공유하지 않음
-. 앱 그룹별 ID : 관련된 앱끼리 미리 정의하여 공유
-. 디바이스 ID : 모든 앱이 이용할 수 있는 디바이스 고유 ID
리셋가능 vs 변경불가능
안드로이드에서는 다음 4가지 경우의 케이스가 존재합니다
-. Session-only : 앱이 재시작될 때마다 새로운 ID 발급
-. Install-reset : 앱 재설치 때마다 새로운 ID 발급(내부저장소 삭제와 동일)
-. FDR-reset : 공장초기화 때마다 새로운 ID 발급
-. FDR-persistent : 공장초기화 후에도 ID 변경없음
공식 문서에 언급된 API들은 아래와 같습니다.
Build
getSerial()
TelephonyManager
getImei()
getDeviceId()
getMeid()
getSimSerialNumber()
getSubscriberId()
WifiInfo (안드로이드 6)
getMacAddress()
Build나 TelephonyManager 클래스를 통해 제공되는 이 기능이 안드로이드 10(API29 / Q OS / Quince Tart)부터 개인정보 보호 강화 정책에 따라 추가 제약을 받습니다.
targetSdkVersion 기준 28까지는 READ_PHONE_STATE 권한을 가진 앱은 UID (Unique identifier)를 읽을 수 있으나, 29부터 READ_PRIVILEGED_PHONE_STATE 권한이 있어야 합니다.
TelephonyManager.getDeviceId() API를 각각 O OS(안드로이드 8 / 오레오 / Oreo)와 Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart)에서 호출하면
O OS(안드로이드 8 / 오레오 / Oreo) + READ_PHONE_STATE 권한이 없을 때
Caused by: java.lang.SecurityException: getDeviceId: uid 10237 does not have android.permission.READ_PHONE_STATE.
O OS(안드로이드 8 / 오레오 / Oreo) + READ_PHONE_STATE 권한이 있을 때
2019-12-31 14:59:56.528 32231-32231/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= 230064929080355
Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart) + READ_PHONE_STATE 권한이 없을 때
Caused by: java.lang.SecurityException: getDeviceId: The user 10265 does not meet the requirements to access device identifiers.
Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart) + READ_PHONE_STATE 권한이 있을 때
2019-12-31 14:54:39.741 5812-5812/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= null
Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart) + READ_PRIVILEGED_PHONE_STATE 권한이 있을 때
2019-12-31 14:59:56.528 32231-32231/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= 230064929080355
그럼 READ_PRIVILEGED_PHONE_STATE 권한은 어떻게 얻을 수 있을까?
결론만 이야기하면, 다운로드 앱(구글 플레이 스토어 등에서 다운받아 설치한 앱)은 저 권한을 얻을 방법이 없습니다.
저 퍼미션이 선언된 시스템의 AndroidManifest.xml을 보면, protectionLevel이 signatureOrSystem이기 때문입니다.
이 조건은 단말 제조사만 충족할 수 있으므로, 다운로드 앱은 targetSdkVersion 29부터 UID에 접근할 수 없습니다.
1. Unique Telephone Number ( IMEI, MEID, ESN, IMSI )
핸드폰의 경우 쉽게 유니크한 값을 얻을 수 있습니다.
android.permission.READ_PHONE_STATE 권한이 필요하기 때문에 유저에게 명시해야합니다.
안드로이드 10.0 에서 TelephonyManager에서 개인을 특정할 수 있는 정보를 가져올수 없도록 변경되었습니다.
TelephonyManager().getDeviceID() : null 반환
TelephonyManager().getlmei() : null 반환
TelephonyManager().getMeid() : null 반환
2. Mac Address
안드로이드 6.0 부터 Wifi 나 Bluetooth의 Mac Address 접근이 금지되었습니다.
( wlan0 파일을 직접 읽어오는 구멍이 있었지만, 안드로이드 7.0에서 막혔습니다 )
WifiInfo.getMacAddres() : 02:00:00:00:00:00 반환
3. Serial Number
모든 안드로이드 디바이스가 시리얼넘버를 가지고 있지 않습니다.
Android Q (10.0) 에서 하드웨어의 시리얼넘버 사용이 불가능해졌습니다.
Build.getSerial() : unknown 반환
Build.SERIAL : unknown 반환
4. Secure Android ID / SSAID / Settings.Secure.ANDROID_ID
유명한 핸드폰 제조사 제품에서 매번 동일한 Android_ID 를 가지는 알려진 버그가 있습니다.
안드로이드 8.0 부터는 앱 그룹 범위, 그 전에는 디바이스 범위 식별자입니다.
서명 키가 동일한 앱은 동일한 식별자 값을 갖습니다.
식별자는 디바이스 초기화 전까지는 유지됩니다
5. Use UUID ( google I/O에서 소개 )
추천하는 해결책은 UUID 해결책을 사용하여 유니크한 값을 얻는 것입니다.
하지만 특정 디바이스의 물리적으로 완벽한 유니크값을 사용하길 원한다면 4. Secure Android ID 솔루션을 사용합니다.
100% 보장하지는 않지만 다른 솔루션 보다는 좋습니다.
안드로이드 10.0 에서 디바이스별 사용자의 고유한 식별자가 필요할경우에 Android ID 와 WidevineId 를 대체로 사용할 수 있습니다.
Android ID는 디바이스가 공장 초기화후 최초 부팅시에 생성되는 64-bit 값입니다.
또한 초기화 전까지는 삭제 되지 않고 저장되어 있어서 디바이스 식별에 유용한 값입니다.
주의1) 디바이스를 리셋 할 경우 초기화(변경) 되지만 OTA 업데이트로 변경되지 않습니다.
주의2) 안드로이드 2.2(Proyo 프로요) 이전 버전에서는 100% 디바이스 고유 번호를 획득 한다고 보장할 수 없으며, 몇몇 Vendor 에서 출시된 디바이스에 동일한 고유번호(예: 9774d56d682e549c)가 획득 됩니다.
class DeviceInfo(val context: Context) {
// android device id 확인
// device id => 기기의 고유한 하드웨어 식별자 Android ID (SSAID)
fun getDeviceId(): String {
return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
}
// android device model 확인
// device model => 어떤 제품인지 (안드로이드 기기의 제품 모델명)
fun getDeviceModel(): String {
return Build.MODEL
}
// android device os 확인
// device os => 안드로이드 버전 몇인지
fun getDeviceOs(): String {
return Build.VERSION.RELEASE.toString()
}
// android app version 확인
// app version => 해당 앱의 버전이 몇인지
fun getAppVersion(): String {
val info: PackageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return info.versionName
}
}
안드로이드 버전과 상관없이 항상 유니크한 아이디를 반환해주는 메서드
private fun getUniqueDeviceIdentifier(): String? {
val telephonyManager: TelephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
var uniqueId: String? = null
try {
telephonyManager.deviceId?.let {
uniqueId = it
}
if (uniqueId==null && Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
telephonyManager.imei?.let { uniqueId = it }
if (uniqueId==null) {
telephonyManager.meid?.let { uniqueId = it }
}
}
} catch(e: Exception) {}
if (uniqueId==null) {
// 시리얼 번호 추출이 android 10 부터 불가능합니다.
// build 권한으로 해당 앱이 지정되면 가능할텐데, 그것에 대한 정보가 확인되지 않습니다. (update. 2023.05.01)
uniqueId = Build.SERIAL
}
if (uniqueId==null || uniqueId=="unknown") {
val wifiManager: WifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
uniqueId = wifiInfo.macAddress
}
if (uniqueId==null || uniqueId=="02:00:00:00:00:00") {
uniqueId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
}
return uniqueId
}
참고자료
https://salix97.tistory.com/225
https://dnight.tistory.com/entry/Android-고유식별자
https://developer.android.com/training/articles/user-data-ids?hl=ko#mac-11-plus
http://zolasse.blogspot.com/2012/05/blog-post.html
https://kanzler.tistory.com/64
https://wrjeoung.tistory.com/6
https://helloit.tistory.com/293
https://brunch.co.kr/@huewu/9
https://samse.tistory.com/entry/최신-Android-DeviceId-가져오기SSAID
http://sunphiz.me/wp/archives/3426
https://source.android.com/docs/core/connect/wifi-mac-randomization-behavior
https://dktfrmaster.blogspot.com/2016/11/id.html
https://source.android.com/docs/core/connect/device-identifiers?hl=ko
ID의 특징
ID는 다음과 같은 특징이 있는데, 사용하는 이유와 목적에 따라 적절한 ID를 선택하는 것이 좋습니다.
Scope
안드로이드에서는 다음 3가지 경우의 Scope가 존재합니다
-. Single 앱의 ID : 다른 앱과 ID를 공유하지 않음
-. 앱 그룹별 ID : 관련된 앱끼리 미리 정의하여 공유
-. 디바이스 ID : 모든 앱이 이용할 수 있는 디바이스 고유 ID
리셋가능 vs 변경불가능
안드로이드에서는 다음 4가지 경우의 케이스가 존재합니다
-. Session-only : 앱이 재시작될 때마다 새로운 ID 발급
-. Install-reset : 앱 재설치 때마다 새로운 ID 발급(내부저장소 삭제와 동일)
-. FDR-reset : 공장초기화 때마다 새로운 ID 발급
-. FDR-persistent : 공장초기화 후에도 ID 변경없음
공식 문서에 언급된 API들은 아래와 같습니다.
Build
getSerial()
TelephonyManager
getImei()
getDeviceId()
getMeid()
getSimSerialNumber()
getSubscriberId()
WifiInfo (안드로이드 6)
getMacAddress()
Build나 TelephonyManager 클래스를 통해 제공되는 이 기능이 안드로이드 10(API29 / Q OS / Quince Tart)부터 개인정보 보호 강화 정책에 따라 추가 제약을 받습니다.
targetSdkVersion 기준 28까지는 READ_PHONE_STATE 권한을 가진 앱은 UID (Unique identifier)를 읽을 수 있으나, 29부터 READ_PRIVILEGED_PHONE_STATE 권한이 있어야 합니다.
TelephonyManager.getDeviceId() API를 각각 O OS(안드로이드 8 / 오레오 / Oreo)와 Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart)에서 호출하면
O OS(안드로이드 8 / 오레오 / Oreo) + READ_PHONE_STATE 권한이 없을 때
Caused by: java.lang.SecurityException: getDeviceId: uid 10237 does not have android.permission.READ_PHONE_STATE.
O OS(안드로이드 8 / 오레오 / Oreo) + READ_PHONE_STATE 권한이 있을 때
2019-12-31 14:59:56.528 32231-32231/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= 230064929080355
Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart) + READ_PHONE_STATE 권한이 없을 때
Caused by: java.lang.SecurityException: getDeviceId: The user 10265 does not meet the requirements to access device identifiers.
Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart) + READ_PHONE_STATE 권한이 있을 때
2019-12-31 14:54:39.741 5812-5812/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= null
Q OS(안드로이드 10 / 퀸스 타르트 / Quince Tart) + READ_PRIVILEGED_PHONE_STATE 권한이 있을 때
2019-12-31 14:59:56.528 32231-32231/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= 230064929080355
그럼 READ_PRIVILEGED_PHONE_STATE 권한은 어떻게 얻을 수 있을까?
결론만 이야기하면, 다운로드 앱(구글 플레이 스토어 등에서 다운받아 설치한 앱)은 저 권한을 얻을 방법이 없습니다.
저 퍼미션이 선언된 시스템의 AndroidManifest.xml을 보면, protectionLevel이 signatureOrSystem이기 때문입니다.
이 조건은 단말 제조사만 충족할 수 있으므로, 다운로드 앱은 targetSdkVersion 29부터 UID에 접근할 수 없습니다.
1. Unique Telephone Number ( IMEI, MEID, ESN, IMSI )
핸드폰의 경우 쉽게 유니크한 값을 얻을 수 있습니다.
android.permission.READ_PHONE_STATE 권한이 필요하기 때문에 유저에게 명시해야합니다.
안드로이드 10.0 에서 TelephonyManager에서 개인을 특정할 수 있는 정보를 가져올수 없도록 변경되었습니다.
TelephonyManager().getDeviceID() : null 반환
TelephonyManager().getlmei() : null 반환
TelephonyManager().getMeid() : null 반환
2. Mac Address
안드로이드 6.0 부터 Wifi 나 Bluetooth의 Mac Address 접근이 금지되었습니다.
( wlan0 파일을 직접 읽어오는 구멍이 있었지만, 안드로이드 7.0에서 막혔습니다 )
WifiInfo.getMacAddres() : 02:00:00:00:00:00 반환
3. Serial Number
모든 안드로이드 디바이스가 시리얼넘버를 가지고 있지 않습니다.
Android Q (10.0) 에서 하드웨어의 시리얼넘버 사용이 불가능해졌습니다.
Build.getSerial() : unknown 반환
Build.SERIAL : unknown 반환
4. Secure Android ID / SSAID / Settings.Secure.ANDROID_ID
유명한 핸드폰 제조사 제품에서 매번 동일한 Android_ID 를 가지는 알려진 버그가 있습니다.
안드로이드 8.0 부터는 앱 그룹 범위, 그 전에는 디바이스 범위 식별자입니다.
서명 키가 동일한 앱은 동일한 식별자 값을 갖습니다.
식별자는 디바이스 초기화 전까지는 유지됩니다
5. Use UUID ( google I/O에서 소개 )
추천하는 해결책은 UUID 해결책을 사용하여 유니크한 값을 얻는 것입니다.
하지만 특정 디바이스의 물리적으로 완벽한 유니크값을 사용하길 원한다면 4. Secure Android ID 솔루션을 사용합니다.
100% 보장하지는 않지만 다른 솔루션 보다는 좋습니다.
안드로이드 10.0 에서 디바이스별 사용자의 고유한 식별자가 필요할경우에 Android ID 와 WidevineId 를 대체로 사용할 수 있습니다.
Android ID는 디바이스가 공장 초기화후 최초 부팅시에 생성되는 64-bit 값입니다.
또한 초기화 전까지는 삭제 되지 않고 저장되어 있어서 디바이스 식별에 유용한 값입니다.
주의1) 디바이스를 리셋 할 경우 초기화(변경) 되지만 OTA 업데이트로 변경되지 않습니다.
주의2) 안드로이드 2.2(Proyo 프로요) 이전 버전에서는 100% 디바이스 고유 번호를 획득 한다고 보장할 수 없으며, 몇몇 Vendor 에서 출시된 디바이스에 동일한 고유번호(예: 9774d56d682e549c)가 획득 됩니다.
class DeviceInfo(val context: Context) {
// android device id 확인
// device id => 기기의 고유한 하드웨어 식별자 Android ID (SSAID)
fun getDeviceId(): String {
return Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
}
// android device model 확인
// device model => 어떤 제품인지 (안드로이드 기기의 제품 모델명)
fun getDeviceModel(): String {
return Build.MODEL
}
// android device os 확인
// device os => 안드로이드 버전 몇인지
fun getDeviceOs(): String {
return Build.VERSION.RELEASE.toString()
}
// android app version 확인
// app version => 해당 앱의 버전이 몇인지
fun getAppVersion(): String {
val info: PackageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
return info.versionName
}
}
안드로이드 버전과 상관없이 항상 유니크한 아이디를 반환해주는 메서드
private fun getUniqueDeviceIdentifier(): String? {
val telephonyManager: TelephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
var uniqueId: String? = null
try {
telephonyManager.deviceId?.let {
uniqueId = it
}
if (uniqueId==null && Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
telephonyManager.imei?.let { uniqueId = it }
if (uniqueId==null) {
telephonyManager.meid?.let { uniqueId = it }
}
}
} catch(e: Exception) {}
if (uniqueId==null) {
// 시리얼 번호 추출이 android 10 부터 불가능합니다.
// build 권한으로 해당 앱이 지정되면 가능할텐데, 그것에 대한 정보가 확인되지 않습니다. (update. 2023.05.01)
uniqueId = Build.SERIAL
}
if (uniqueId==null || uniqueId=="unknown") {
val wifiManager: WifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
uniqueId = wifiInfo.macAddress
}
if (uniqueId==null || uniqueId=="02:00:00:00:00:00") {
uniqueId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
}
return uniqueId
}
참고자료
https://salix97.tistory.com/225
https://dnight.tistory.com/entry/Android-고유식별자
https://developer.android.com/training/articles/user-data-ids?hl=ko#mac-11-plus
http://zolasse.blogspot.com/2012/05/blog-post.html
https://kanzler.tistory.com/64
https://wrjeoung.tistory.com/6
https://helloit.tistory.com/293
https://brunch.co.kr/@huewu/9
https://samse.tistory.com/entry/최신-Android-DeviceId-가져오기SSAID
http://sunphiz.me/wp/archives/3426
https://source.android.com/docs/core/connect/wifi-mac-randomization-behavior
https://dktfrmaster.blogspot.com/2016/11/id.html
https://source.android.com/docs/core/connect/device-identifiers?hl=ko
댓글목록
등록된 댓글이 없습니다.