Skip to content

Commit 8bfbd34

Browse files
cpuinfo logic improvements: now ignoring some non-stable fields
1 parent a42d9e7 commit 8bfbd34

File tree

18 files changed

+1635
-72
lines changed

18 files changed

+1635
-72
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
package com.fingerprintjs.android.playground.fingerprinters_screen
22

33

4-
const val DEFAULT_FINGERPRINTER_VERSION = 3
4+
const val DEFAULT_FINGERPRINTER_VERSION = 4

app/src/main/java/com/fingerprintjs/android/playground/fingerprinters_screen/adapter/FingerprintItemConverter.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.fingerprintjs.android.fingerprint.signal_providers.device_state.Devic
1515
import com.fingerprintjs.android.fingerprint.signal_providers.hardware.HardwareSignalGroupProvider
1616
import com.fingerprintjs.android.fingerprint.signal_providers.installed_apps.InstalledAppsSignalGroupProvider
1717
import com.fingerprintjs.android.fingerprint.signal_providers.os_build.OsBuildSignalGroupProvider
18+
import com.fingerprintjs.android.fingerprint.info_providers.CpuInfo
1819
import java.io.File
1920
import java.util.LinkedList
2021

@@ -249,6 +250,13 @@ class FingerprintItemConverterImpl : FingerprintItemConverter {
249250
}
250251
FingerprintSectionDescription(signal.displayName, list)
251252
}
253+
is CpuInfo -> {
254+
val commonInfoList = value.commonInfo
255+
val perProcessorList = value.perProcessorInfo.mapIndexed { index, list ->
256+
listOf("processor" to index.toString()) + list
257+
}.flatten()
258+
FingerprintSectionDescription(signal.displayName, commonInfoList + perProcessorList)
259+
}
252260
else -> {
253261
FingerprintSectionDescription(
254262
signal.displayName,

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<item>v1</item>
2828
<item>v2</item>
2929
<item>v3</item>
30+
<item>v4</item>
3031
</string-array>
3132

3233
</resources>

fingerprint/src/main/java/com/fingerprintjs/android/fingerprint/info_providers/CpuInfoProvider.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,27 @@ package com.fingerprintjs.android.fingerprint.info_providers
33

44
import android.os.Build
55
import com.fingerprintjs.android.fingerprint.tools.executeSafe
6+
import com.fingerprintjs.android.fingerprint.tools.parsers.parseCpuInfo
67
import java.io.File
78
import java.util.Scanner
89

910

11+
data class CpuInfo(
12+
val commonInfo: List<Pair<String, String>>,
13+
// except processor : x pairs. index in list may be considered as an index of a processor.
14+
val perProcessorInfo: List<List<Pair<String, String>>>,
15+
) {
16+
companion object {
17+
val EMPTY = CpuInfo(
18+
commonInfo = emptyList(),
19+
perProcessorInfo = emptyList(),
20+
)
21+
}
22+
}
23+
1024
interface CpuInfoProvider {
1125
fun cpuInfo(): Map<String, String>
26+
fun cpuInfoV2(): CpuInfo
1227
fun abiType(): String
1328
fun coresCount(): Int
1429
}
@@ -19,6 +34,15 @@ internal class CpuInfoProviderImpl :
1934
return executeSafe({ getCpuInfo() }, emptyMap())
2035
}
2136

37+
override fun cpuInfoV2(): CpuInfo {
38+
return executeSafe(
39+
{
40+
getCpuInfoV2()
41+
},
42+
CpuInfo.EMPTY
43+
)
44+
}
45+
2246
@Suppress("DEPRECATION")
2347
override fun abiType(): String {
2448
return executeSafe({
@@ -47,7 +71,12 @@ internal class CpuInfoProviderImpl :
4771

4872
return map
4973
}
74+
75+
private fun getCpuInfoV2(): CpuInfo {
76+
val cpuInfoContents = File(CPU_INFO_PATH).readText()
77+
return parseCpuInfo(cpuInfoContents)
78+
}
5079
}
5180

5281
private const val CPU_INFO_PATH = "/proc/cpuinfo"
53-
private const val KEY_VALUE_DELIMITER = ": "
82+
private const val KEY_VALUE_DELIMITER = ": "

fingerprint/src/main/java/com/fingerprintjs/android/fingerprint/signal_providers/Signal.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.fingerprintjs.android.fingerprint.info_providers.CameraInfo
55
import com.fingerprintjs.android.fingerprint.info_providers.InputDeviceData
66
import com.fingerprintjs.android.fingerprint.info_providers.MediaCodecInfo
77
import com.fingerprintjs.android.fingerprint.info_providers.SensorData
8+
import com.fingerprintjs.android.fingerprint.info_providers.CpuInfo
89

910

1011
abstract class Signal<T>(
@@ -84,6 +85,25 @@ abstract class IdentificationSignal<T>(
8485
VALUE_KEY to listValue
8586
)
8687
}
88+
is CpuInfo -> {
89+
val commonProps = value.commonInfo
90+
val procCount = value.perProcessorInfo.size
91+
val perProcRepeatedProps = value.perProcessorInfo
92+
.flatten()
93+
.groupBy { it }
94+
.filter { it.value.size == procCount }
95+
.map { it.key }
96+
val perProcRepeatedPropsKeys = perProcRepeatedProps.map { it.first }.toSet()
97+
val perProcUniqueProps = value.perProcessorInfo
98+
.map { procInfo -> procInfo.filter { it.first !in perProcRepeatedPropsKeys } }
99+
mapOf(
100+
VALUE_KEY to mapOf(
101+
"commonProps" to commonProps,
102+
"repeatedProps" to perProcRepeatedProps,
103+
"uniquePerCpuProps" to perProcUniqueProps,
104+
)
105+
)
106+
}
87107
else -> {
88108
mapOf(
89109
VALUE_KEY to value.toString()

fingerprint/src/main/java/com/fingerprintjs/android/fingerprint/signal_providers/hardware/HardwareFingerprintRawData.kt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.fingerprintjs.android.fingerprint.info_providers.SensorData
77
import com.fingerprintjs.android.fingerprint.signal_providers.IdentificationSignal
88
import com.fingerprintjs.android.fingerprint.signal_providers.RawData
99
import com.fingerprintjs.android.fingerprint.signal_providers.StabilityLevel
10+
import com.fingerprintjs.android.fingerprint.info_providers.CpuInfo
1011

1112

1213
data class HardwareFingerprintRawData(
@@ -15,6 +16,7 @@ data class HardwareFingerprintRawData(
1516
val totalRAM: Long,
1617
val totalInternalStorageSpace: Long,
1718
val procCpuInfo: Map<String, String>,
19+
val procCpuInfoV2: CpuInfo,
1820
val sensors: List<SensorData>,
1921
val inputDevices: List<InputDeviceData>,
2022
val batteryHealth: String,
@@ -31,6 +33,7 @@ data class HardwareFingerprintRawData(
3133
totalRAM(),
3234
totalInternalStorageSpace(),
3335
procCpuInfo(),
36+
procCpuInfoV2(),
3437
sensors(),
3538
inputDevices(),
3639
batteryHealth(),
@@ -87,7 +90,7 @@ data class HardwareFingerprintRawData(
8790

8891
fun procCpuInfo() = object : IdentificationSignal<Map<String, String>>(
8992
1,
90-
null,
93+
4,
9194
StabilityLevel.STABLE,
9295
CPU_INFO_KEY,
9396
CPU_INFO_DISPLAY_NAME,
@@ -102,6 +105,24 @@ data class HardwareFingerprintRawData(
102105
}
103106
}
104107

108+
fun procCpuInfoV2() = object : IdentificationSignal<CpuInfo>(
109+
4,
110+
null,
111+
StabilityLevel.STABLE,
112+
CPU_INFO_KEY,
113+
CPU_INFO_DISPLAY_NAME,
114+
procCpuInfoV2.copy(
115+
commonInfo = procCpuInfoV2.commonInfo
116+
.filter { it.first.lowercase() !in CPUINFO_IGNORED_COMMON_PROPS },
117+
perProcessorInfo = procCpuInfoV2.perProcessorInfo
118+
.map { procInfo -> procInfo.filter { it.first.lowercase() !in CPUINFO_IGNORED_PER_PROC_PROPS } },
119+
),
120+
) {
121+
override fun toString(): String {
122+
return value.commonInfo.toString() + value.perProcessorInfo.toString()
123+
}
124+
}
125+
105126
fun sensors() = object : IdentificationSignal<List<SensorData>>(
106127
1,
107128
null,
@@ -210,4 +231,15 @@ data class HardwareFingerprintRawData(
210231
) {
211232
override fun toString() = coresCount.toString()
212233
}
234+
235+
companion object {
236+
private val CPUINFO_IGNORED_COMMON_PROPS = setOf<String>(
237+
// nothing here for now
238+
)
239+
240+
private val CPUINFO_IGNORED_PER_PROC_PROPS = setOf(
241+
"bogomips",
242+
"cpu mhz",
243+
)
244+
}
213245
}

fingerprint/src/main/java/com/fingerprintjs/android/fingerprint/signal_providers/hardware/HardwareSignalGroupProvider.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class HardwareSignalGroupProvider(
3535
memInfoProvider.totalRAM(),
3636
memInfoProvider.totalInternalStorageSpace(),
3737
cpuInfoProvider.cpuInfo(),
38+
cpuInfoProvider.cpuInfoV2(),
3839
sensorsDataSource.sensors(),
3940
inputDeviceDataSource.getInputDeviceData(),
4041
batteryInfoProvider.batteryHealth(),
@@ -51,8 +52,8 @@ class HardwareSignalGroupProvider(
5152
combineSignals(
5253
when (version) {
5354
1 -> v1()
54-
2 -> v2()
55-
else -> v2()
55+
2, 3 -> v2()
56+
else -> rawData.signals(version, stabilityLevel)
5657
}, stabilityLevel
5758
)
5859
)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.fingerprintjs.android.fingerprint.tools.parsers
2+
3+
import com.fingerprintjs.android.fingerprint.info_providers.CpuInfo
4+
5+
private const val SINGLE_PROCESSOR_KEY = "processor"
6+
7+
internal fun parseCpuInfo(contents: String): CpuInfo {
8+
// just for simplicity
9+
val linesStartEndBlankLine = listOf("") + contents.lines() + listOf("")
10+
11+
val repeatedBlankLinesIndices = linesStartEndBlankLine
12+
.mapIndexed { index, s -> s to index }
13+
.windowed(size = 2, step = 1, partialWindows = false) {
14+
val (firstLine, _) = it[0]
15+
val (secondLine, secondIndex) = it[1]
16+
if (firstLine.isBlank() && secondLine.isBlank())
17+
secondIndex
18+
else
19+
null
20+
}
21+
.filterNotNull()
22+
23+
// This is a list of lines that are divided to sections with single blank lines. Starts with a blank line. Ends with a
24+
// (potentially same) blank line.
25+
val linesWithSections = linesStartEndBlankLine
26+
.filterIndexed { index, s -> index !in repeatedBlankLinesIndices }
27+
28+
val sectionBreaksIndices = linesWithSections.mapIndexed { index, s -> index.takeIf { s.isBlank() } }.filterNotNull()
29+
30+
// sections (which are lists of non-blank lines). Each section is not empty.
31+
val sectionsOfLines = sectionBreaksIndices.windowed(size = 2, step = 1, partialWindows = false) {
32+
linesWithSections.slice(it[0] + 1..it[1] - 1)
33+
}
34+
35+
fun parseLine(line: String): Pair<String, String>? {
36+
return line
37+
.split(":", limit = 2)
38+
.takeIf { it.size == 2 }
39+
?.map { it.dropWhile(Char::isWhitespace).dropLastWhile(Char::isWhitespace) }
40+
?.let { it[0] to it[1] }
41+
}
42+
43+
// sections (which are key to value pairs). Each section is not empty.
44+
val sectionsOfKeyValuePairs = sectionsOfLines
45+
.map { it.mapNotNull(::parseLine) }
46+
.filter { it.isNotEmpty() }
47+
48+
fun isSingleProcessorPair(pair: Pair<String, String>): Boolean {
49+
return pair.first == SINGLE_PROCESSOR_KEY && pair.second.all(Char::isDigit)
50+
}
51+
52+
fun extractProcessorInfo(section: List<Pair<String, String>>): List<Pair<String, String>> {
53+
return section.dropWhile { !isSingleProcessorPair(it) }
54+
}
55+
56+
fun extractCommonInfo(section: List<Pair<String, String>>): List<Pair<String, String>> {
57+
return section.takeWhile { !isSingleProcessorPair(it) }
58+
}
59+
60+
val processorsInfo = sectionsOfKeyValuePairs
61+
.map(::extractProcessorInfo)
62+
.filter { it.isNotEmpty() }
63+
.map { procInfo -> procInfo.filter { !isSingleProcessorPair(it) } }
64+
65+
val commonInfo = sectionsOfKeyValuePairs
66+
.map(::extractCommonInfo)
67+
.filter { it.isNotEmpty() }
68+
.flatten()
69+
70+
return CpuInfo(
71+
commonInfo = commonInfo,
72+
perProcessorInfo = processorsInfo,
73+
)
74+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.fingerprintjs.android.fingerprint.cpu_info_parser
2+
3+
import com.fingerprintjs.android.fingerprint.info_providers.CpuInfo
4+
5+
interface CpuInfoExample {
6+
val content: String
7+
val expectedCpuInfo: CpuInfo
8+
}

0 commit comments

Comments
 (0)