Skip to content

Commit

Permalink
Merge pull request #108 from fingerprintjs/fix/simplify-lazy-initiali…
Browse files Browse the repository at this point in the history
…zation

Fix/simplify lazy initialization
  • Loading branch information
Sergey-Makarov committed Oct 5, 2023
2 parents 3285701 + 0edc3ab commit afc30d4
Show file tree
Hide file tree
Showing 31 changed files with 450 additions and 449 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ class HomeViewModel @Inject constructor(
stabilityLevel = stabilityLevel
)
val signals = withContext(Dispatchers.IO) {
fingerprinter.getFingerprintingSignalsProvider().getSignalsMatching(
fingerprinter.getFingerprintingSignalsProvider()?.getSignalsMatching(
version = version,
stabilityLevel = stabilityLevel
)
).orEmpty()
}
fingerprintScreenStateMutable.emit(
FingerprintScreenState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ class ReportAttachmentCreator @Inject constructor(
StabilityLevel.values().map { value -> version to value }
}.map { (version, stabilityLevel) ->
val fingerprint = fingerprinter.getFingerprint(version, stabilityLevel)
val signals = fingerprinter.getFingerprintingSignalsProvider().getSignalsMatching(
val signals = fingerprinter.getFingerprintingSignalsProvider()?.getSignalsMatching(
version, stabilityLevel
)
).orEmpty()
FingerprintInfoVo(version = version.description,
stabilityLevel = stabilityLevel.description,
fingerprint = fingerprint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.fingerprintjs.android.fingerprint

import androidx.annotation.Discouraged
import androidx.annotation.WorkerThread
import com.fingerprintjs.android.fingerprint.device_id_signals.DeviceIdSignalsProvider
import com.fingerprintjs.android.fingerprint.fingerprinting_signals.FingerprintingSignal
import com.fingerprintjs.android.fingerprint.fingerprinting_signals.FingerprintingSignalsProvider
import com.fingerprintjs.android.fingerprint.signal_providers.SignalGroupProvider
Expand All @@ -14,25 +13,27 @@ import com.fingerprintjs.android.fingerprint.signal_providers.installed_apps.Ins
import com.fingerprintjs.android.fingerprint.signal_providers.os_build.OsBuildSignalGroupProvider
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.DummyResults
import com.fingerprintjs.android.fingerprint.tools.FingerprintingLegacySchemeSupportExtensions
import com.fingerprintjs.android.fingerprint.tools.flatten
import com.fingerprintjs.android.fingerprint.tools.hashers.Hasher
import com.fingerprintjs.android.fingerprint.tools.hashers.MurMur3x64x128Hasher
import com.fingerprintjs.android.fingerprint.tools.logs.Logger
import com.fingerprintjs.android.fingerprint.tools.logs.ePleaseReport
import com.fingerprintjs.android.fingerprint.tools.safe.Safe
import com.fingerprintjs.android.fingerprint.tools.safe.safe
import com.fingerprintjs.android.fingerprint.tools.safe.safeAsync
import com.fingerprintjs.android.fingerprint.tools.threading.runOnAnotherThread
import com.fingerprintjs.android.fingerprint.tools.threading.safe.Safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe


public class Fingerprinter internal constructor(
private val legacyArgs: LegacyArgs?,
private val fpSignalsProvider: FingerprintingSignalsProvider,
private val deviceIdSignalsProvider: DeviceIdSignalsProvider,
private val implFactory: () -> FingerprinterImpl,
private val isLegacyFactory: Boolean,
) {
@Volatile
private var deviceIdResult: DeviceIdResult? = null
@Volatile
private var fingerprintResult: FingerprintResult? = null
private val impl by lazy {
// we use long timeout here, so that any single hiccup during the initialization
// does not ruin an entire operation.
// another option could be to not use timeout at all, since we have a lot of timeouts
// deep inside.
safe(timeoutMs = Safe.timeoutLong) { implFactory() }
}

/**
* Retrieve the device ID information.
Expand All @@ -50,33 +51,15 @@ public class Fingerprinter internal constructor(
""")
@Throws(IllegalStateException::class)
public fun getDeviceId(listener: (DeviceIdResult) -> Unit) {
if (legacyArgs == null) {
throw (IllegalStateException(
"To call this deprecated method, the instance" +
"must be retrieved using deprecated factory method."
))
}
checkThisLegacyMethodSupported()

deviceIdResult?.let {
listener.invoke(it)
return
}

safeAsync(
runFingerprinterImplOnAnotherThread(
onError = {
listener.invoke(DummyResults.deviceIdResult)
Logger.ePleaseReport(it)
}
) {
val deviceIdResult = DeviceIdResult(
legacyArgs.deviceIdProvider.fingerprint(),
legacyArgs.deviceIdProvider.rawData().gsfId().value,
legacyArgs.deviceIdProvider.rawData().androidId().value,
legacyArgs.deviceIdProvider.rawData().mediaDrmId().value
)
this.deviceIdResult = deviceIdResult
listener.invoke(deviceIdResult)
}
},
onSuccess = listener,
) { getDeviceId() }
}

/**
Expand All @@ -90,21 +73,13 @@ public class Fingerprinter internal constructor(
* @param listener device ID listener.
*/
public fun getDeviceId(version: Version, listener: (DeviceIdResult) -> Unit) {
safeAsync(
runFingerprinterImplOnAnotherThread(
onError = {
listener.invoke(DummyResults.deviceIdResult)
Logger.ePleaseReport(it)
}
) {
listener.invoke(
DeviceIdResult(
deviceId = deviceIdSignalsProvider.getSignalMatching(version).getIdString(),
gsfId = deviceIdSignalsProvider.gsfIdSignal.getIdString(),
androidId = deviceIdSignalsProvider.androidIdSignal.getIdString(),
mediaDrmId = deviceIdSignalsProvider.mediaDrmIdSignal.getIdString(),
)
)
}
},
onSuccess = listener,
) { getDeviceId(version) }
}

/**
Expand All @@ -130,50 +105,15 @@ public class Fingerprinter internal constructor(
stabilityLevel: StabilityLevel = StabilityLevel.OPTIMAL,
listener: (FingerprintResult) -> Unit
) {
if (legacyArgs == null) {
throw (IllegalStateException(
"To call this deprecated method, the instance" +
"must be retrieved using deprecated factory method."
))
}
checkThisLegacyMethodSupported()

fingerprintResult?.let {
listener.invoke(it)
return
}
safeAsync(
runFingerprinterImplOnAnotherThread(
onError = {
listener.invoke(DummyResults.fingerprintResult)
Logger.ePleaseReport(it)
}
) {
val fingerprintSb = StringBuilder()

fingerprintSb.apply {
append(legacyArgs.hardwareSignalProvider.fingerprint(stabilityLevel))
append(legacyArgs.osBuildSignalProvider.fingerprint(stabilityLevel))
append(legacyArgs.deviceStateSignalProvider.fingerprint(stabilityLevel))
append(legacyArgs.installedAppsSignalProvider.fingerprint(stabilityLevel))
}

val result = object : FingerprintResult {
override val fingerprint = legacyArgs.configuration.hasher.hash(fingerprintSb.toString())

@Suppress("UNCHECKED_CAST")
override fun <T : SignalGroupProvider<*>> getSignalProvider(clazz: Class<T>): T? {
return when (clazz) {
HardwareSignalGroupProvider::class.java -> legacyArgs.hardwareSignalProvider
OsBuildSignalGroupProvider::class.java -> legacyArgs.osBuildSignalProvider
DeviceStateSignalGroupProvider::class.java -> legacyArgs.deviceStateSignalProvider
InstalledAppsSignalGroupProvider::class.java -> legacyArgs.installedAppsSignalProvider
DeviceIdProvider::class.java -> legacyArgs.deviceIdProvider
else -> null
} as? T
}
}

listener.invoke(result)
}
},
onSuccess = listener,
) { getFingerprint(stabilityLevel) }
}

/**
Expand All @@ -196,32 +136,13 @@ public class Fingerprinter internal constructor(
hasher: Hasher = MurMur3x64x128Hasher(),
listener: (String) -> (Unit),
) {
safeAsync(
runFingerprinterImplOnAnotherThread(
onError = {
listener.invoke(DummyResults.fingerprint)
Logger.ePleaseReport(it)
}
) {
val result = if (version < Version.fingerprintingFlattenedSignalsFirstVersion) {
val joinedHashes = with(FingerprintingLegacySchemeSupportExtensions) {
listOf(
hasher.hash(fpSignalsProvider.getHardwareSignals(version, stabilityLevel)),
hasher.hash(fpSignalsProvider.getOsBuildSignals(version, stabilityLevel)),
hasher.hash(fpSignalsProvider.getDeviceStateSignals(version, stabilityLevel)),
hasher.hash(fpSignalsProvider.getInstalledAppsSignals(version, stabilityLevel)),
).joinToString(separator = "")
}

hasher.hash(joinedHashes)
} else {
getFingerprint(
fingerprintingSignals = fpSignalsProvider.getSignalsMatching(version, stabilityLevel),
hasher = hasher,
)
}

listener.invoke(result)
}
},
onSuccess = listener,
) { getFingerprint(version, stabilityLevel, hasher) }
}

/**
Expand All @@ -243,22 +164,46 @@ public class Fingerprinter internal constructor(
fingerprintingSignals: List<FingerprintingSignal<*>>,
hasher: Hasher = MurMur3x64x128Hasher(),
): String {
return safe(timeoutMs = Safe.timeoutLong) { hasher.hash(fingerprintingSignals) }
return impl
.map { it.getFingerprint(fingerprintingSignals, hasher) }
.flatten()
.onFailure { Logger.ePleaseReport(it) }
.getOrDefault(DummyResults.fingerprint)
}

private fun Hasher.hash(fingerprintingSignals: List<FingerprintingSignal<*>>): String {
val joinedString =
fingerprintingSignals.joinToString(separator = "") { it.getHashableString() }
return this.hash(joinedString)
}

/**
* @return [FingerprintingSignalsProvider] which is useful in conjunction with the getFingerprint(signals, hasher) method.
* @return [FingerprintingSignalsProvider] which is useful in conjunction with the getFingerprint(signals, hasher) method, or null
* if some unexpected error occurred.
*/
public fun getFingerprintingSignalsProvider(): FingerprintingSignalsProvider {
return fpSignalsProvider
@WorkerThread
public fun getFingerprintingSignalsProvider(): FingerprintingSignalsProvider? {
return impl
.map { it.getFingerprintingSignalsProvider() }
.onFailure { Logger.ePleaseReport(it) }
.getOrNull()
}

@Suppress("NOTHING_TO_INLINE")
private inline fun checkThisLegacyMethodSupported() {
if (!isLegacyFactory) {
throw (IllegalStateException(
"To call this deprecated method, the instance " +
"must be retrieved using deprecated factory method."
))
}
}

private inline fun <T> runFingerprinterImplOnAnotherThread(
crossinline onError: (Throwable) -> Unit,
crossinline onSuccess: (T) -> Unit,
crossinline call: FingerprinterImpl.() -> Result<T>,
) {
runOnAnotherThread {
impl.map { it.call() }.flatten().fold(
onSuccess = { onSuccess.invoke(it) },
onFailure = onError,
)
}.onFailure(onError)
}

/**
Expand Down
Loading

0 comments on commit afc30d4

Please sign in to comment.