Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit f2616aa

Browse files
authored
fix: refactor getDeviceID to use DRM, remove AndroidID (segmentio#567)
1 parent d1b6af1 commit f2616aa

File tree

1 file changed

+96
-65
lines changed

1 file changed

+96
-65
lines changed

packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt

Lines changed: 96 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@ import android.content.Context
66
import android.content.Intent
77
import android.content.pm.PackageInfo
88
import android.content.res.Resources
9+
import android.media.MediaDrm
910
import android.net.ConnectivityManager
1011
import android.net.NetworkCapabilities
1112
import android.net.Uri
1213
import android.os.Build
13-
import android.provider.Settings
14-
import android.provider.Settings.Secure.getString
1514
import android.util.Log
1615
import androidx.core.content.pm.PackageInfoCompat
1716
import com.facebook.react.ReactActivity
@@ -21,6 +20,8 @@ import com.facebook.react.module.annotations.ReactModule
2120
import com.sovranreactnative.SovranModule
2221
import java.lang.Exception
2322
import java.util.*
23+
import java.security.MessageDigest
24+
import java.util.UUID
2425

2526

2627
enum class ConnectionType {
@@ -54,88 +55,118 @@ class AnalyticsReactNativeModule : ReactContextBaseJavaModule, ActivityEventList
5455
return PackageInfoCompat.getLongVersionCode(pInfo).toString()
5556
}
5657

57-
@SuppressLint("HardwareIds")
58-
private fun getUniqueId(collectDeviceId : Boolean): String? {
59-
if (collectDeviceId) {
60-
return getString(reactApplicationContext.contentResolver, Settings.Secure.ANDROID_ID)
58+
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
59+
60+
/**
61+
* Workaround for not able to get device id on Android 10 or above using DRM API
62+
* {@see https://stackoverflow.com/questions/58103580/android-10-imei-no-longer-available-on-api-29-looking-for-alternatives}
63+
* {@see https://developer.android.com/training/articles/user-data-ids}
64+
*/
65+
private fun getUniqueId(collectDeviceId : Boolean): String? {
66+
if (collectDeviceId) {
67+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
68+
return null
69+
70+
val WIDEVINE_UUID = UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L)
71+
var wvDrm: MediaDrm? = null
72+
try {
73+
wvDrm = MediaDrm(WIDEVINE_UUID)
74+
val wideVineId = wvDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID)
75+
val md = MessageDigest.getInstance("SHA-256")
76+
md.update(wideVineId)
77+
return md.digest().toHexString()
78+
} catch (e: Exception) {
79+
return null
80+
} finally {
81+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
82+
wvDrm?.close()
83+
} else {
84+
wvDrm?.release()
85+
}
6186
}
62-
return null
6387
}
88+
return null
89+
}
6490

65-
private fun getConnectionType(context: Context): ConnectionType {
66-
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
67-
var result: ConnectionType = ConnectionType.Unknown
68-
69-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
70-
cm?.run {
71-
cm.getNetworkCapabilities(cm.activeNetwork)?.run {
72-
if (hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
73-
result = ConnectionType.Wifi
74-
} else if (hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
75-
result = ConnectionType.Cellular
76-
} else {
77-
result = ConnectionType.Unknown
78-
}
91+
private fun getConnectionType(context: Context): ConnectionType {
92+
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
93+
var result: ConnectionType = ConnectionType.Unknown
94+
95+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
96+
cm?.run {
97+
cm.getNetworkCapabilities(cm.activeNetwork)?.run {
98+
if (hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
99+
result = ConnectionType.Wifi
100+
} else if (hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
101+
result = ConnectionType.Cellular
102+
} else {
103+
result = ConnectionType.Unknown
79104
}
80105
}
81-
} else {
82-
cm?.run {
83-
cm.activeNetworkInfo?.run {
84-
if (type == ConnectivityManager.TYPE_WIFI) {
85-
result = ConnectionType.Wifi
86-
} else if (type == ConnectivityManager.TYPE_MOBILE) {
87-
result = ConnectionType.Cellular
88-
} else {
89-
result = ConnectionType.Unknown
90-
}
106+
}
107+
} else {
108+
cm?.run {
109+
cm.activeNetworkInfo?.run {
110+
if (type == ConnectivityManager.TYPE_WIFI) {
111+
result = ConnectionType.Wifi
112+
} else if (type == ConnectivityManager.TYPE_MOBILE) {
113+
result = ConnectionType.Cellular
114+
} else {
115+
result = ConnectionType.Unknown
91116
}
92117
}
93118
}
94-
return result
95119
}
120+
return result
121+
}
96122

97-
@ReactMethod
98-
fun getContextInfo(config: ReadableMap, promise: Promise) {
99-
val appName: String = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString()
100-
val appVersion: String = pInfo.versionName
101-
val buildNumber = getBuildNumber()
102-
val bundleId = reactApplicationContext.packageName
123+
@ReactMethod
124+
fun getContextInfo(config: ReadableMap, promise: Promise) {
125+
val appName: String = reactApplicationContext.applicationInfo.loadLabel(reactApplicationContext.packageManager).toString()
126+
val appVersion: String = pInfo.versionName
127+
val buildNumber = getBuildNumber()
128+
val bundleId = reactApplicationContext.packageName
103129

104-
val connectionType: ConnectionType = getConnectionType(reactApplicationContext)
105-
val timezone: TimeZone = TimeZone.getDefault()
106-
val currentLocale: Locale = Locale.getDefault()
107-
val locale: String = "${currentLocale.language}-${currentLocale.country}"
130+
val connectionType: ConnectionType = getConnectionType(reactApplicationContext)
131+
val timezone: TimeZone = TimeZone.getDefault()
132+
val currentLocale: Locale = Locale.getDefault()
133+
val locale: String = "${currentLocale.language}-${currentLocale.country}"
108134

109-
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
110-
val screenHeight = Resources.getSystem().displayMetrics.heightPixels
135+
val screenWidth = Resources.getSystem().displayMetrics.widthPixels
136+
val screenHeight = Resources.getSystem().displayMetrics.heightPixels
111137

112-
val screenDensity = Resources.getSystem().displayMetrics.density;
138+
val screenDensity = Resources.getSystem().displayMetrics.density;
113139

114-
val contextInfo: WritableMap = Arguments.createMap()
140+
val contextInfo: WritableMap = Arguments.createMap()
115141

116-
contextInfo.putString("appName", appName)
117-
contextInfo.putString("appVersion", appVersion)
118-
contextInfo.putString("buildNumber", buildNumber)
119-
contextInfo.putString("bundleId", bundleId)
120-
contextInfo.putString("deviceId", getUniqueId(config.hasKey("collectDeviceId") && config.getBoolean("collectDeviceId")))
121-
contextInfo.putString("deviceName", Build.DEVICE)
122-
contextInfo.putString("deviceType", "android")
123-
contextInfo.putString("manufacturer", Build.MANUFACTURER)
124-
contextInfo.putString("model", Build.MODEL)
142+
// generate random identifier that does not persist across installations
143+
// use it as the fallback in case DRM API failed to generate one.
144+
val fallbackDeviceId = UUID.randomUUID().toString()
145+
val deviceId = getUniqueId(config.hasKey("collectDeviceId") && config.getBoolean("collectDeviceId"))?: fallbackDeviceId
125146

126-
contextInfo.putString("timezone", timezone.id)
127-
contextInfo.putString("locale", locale)
128-
contextInfo.putString("networkType", connectionType.toString().toLowerCase(currentLocale))
147+
contextInfo.putString("appName", appName)
148+
contextInfo.putString("appVersion", appVersion)
149+
contextInfo.putString("buildNumber", buildNumber)
150+
contextInfo.putString("bundleId", bundleId)
151+
contextInfo.putString("deviceId", deviceId)
152+
contextInfo.putString("deviceName", Build.DEVICE)
153+
contextInfo.putString("deviceType", "android")
154+
contextInfo.putString("manufacturer", Build.MANUFACTURER)
155+
contextInfo.putString("model", Build.MODEL)
129156

130-
contextInfo.putString("osName", "Android")
131-
contextInfo.putString("osVersion", Build.VERSION.RELEASE)
157+
contextInfo.putString("timezone", timezone.id)
158+
contextInfo.putString("locale", locale)
159+
contextInfo.putString("networkType", connectionType.toString().toLowerCase(currentLocale))
132160

133-
contextInfo.putInt("screenWidth", screenWidth)
134-
contextInfo.putInt("screenHeight", screenHeight)
135-
contextInfo.putDouble("screenDensity", screenDensity.toDouble())
161+
contextInfo.putString("osName", "Android")
162+
contextInfo.putString("osVersion", Build.VERSION.RELEASE)
136163

137-
promise.resolve(contextInfo)
138-
}
164+
contextInfo.putInt("screenWidth", screenWidth)
165+
contextInfo.putInt("screenHeight", screenHeight)
166+
contextInfo.putDouble("screenDensity", screenDensity.toDouble())
167+
168+
promise.resolve(contextInfo)
169+
}
139170

140171
fun getReferrer(activity: Activity): Uri? {
141172
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {

0 commit comments

Comments
 (0)