Skip to content

Commit

Permalink
Remove nested safe calls with timeouts, since it makes the code less …
Browse files Browse the repository at this point in the history
…predictable, as "structured concurrency" is not supported by safe call for now.
  • Loading branch information
Sergey-Makarov committed Oct 6, 2023
1 parent 67212ef commit 187dc1a
Show file tree
Hide file tree
Showing 27 changed files with 245 additions and 130 deletions.
10 changes: 9 additions & 1 deletion fingerprint/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ android {
}
}

androidComponents {
onVariants {
it.androidTest?.packaging?.resources?.excludes?.add("META-INF/*")
}
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java) {
if (!this.name.contains("Test")) {
kotlinOptions.freeCompilerArgs += "-Xexplicit-api=warning"
Expand All @@ -94,7 +100,9 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:${Constants.kotlinVersion}")
implementation("androidx.appcompat:appcompat:1.6.1")
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.12.7")
testImplementation("io.mockk:mockk:1.12.8")
androidTestImplementation("io.mockk:mockk:1.12.8")
androidTestImplementation ("io.mockk:mockk-android:1.12.8")
androidTestImplementation("androidx.test.ext:junit-ktx:1.1.5")
androidTestImplementation("androidx.test:runner:1.5.2")
}
3 changes: 3 additions & 0 deletions fingerprint/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<manifest xmlns:tools="http://schemas.android.com/tools">
<uses-sdk tools:overrideLibrary="io.mockk, io.mockk.proxy.android"/>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package com.fingerprintjs.android.playground
package com.fingerprintjs.android.fingerprint

import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.fingerprintjs.android.fingerprint.Configuration
import com.fingerprintjs.android.fingerprint.Fingerprinter
import com.fingerprintjs.android.fingerprint.FingerprinterFactory
import com.fingerprintjs.android.fingerprint.signal_providers.StabilityLevel
import com.fingerprintjs.android.fingerprint.tools.FingerprintingLegacySchemeSupportExtensions.getDeviceStateSignals
import com.fingerprintjs.android.fingerprint.tools.FingerprintingLegacySchemeSupportExtensions.getHardwareSignals
import com.fingerprintjs.android.fingerprint.tools.FingerprintingLegacySchemeSupportExtensions.getInstalledAppsSignals
import com.fingerprintjs.android.fingerprint.tools.FingerprintingLegacySchemeSupportExtensions.getOsBuildSignals
import com.fingerprintjs.android.playground.utils.callbackToSync
import com.fingerprintjs.android.fingerprint.tools.threading.safe.Safe
import com.fingerprintjs.android.fingerprint.utils.callbackToSync
import io.mockk.every
import io.mockk.mockkObject
import junit.framework.TestCase
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -182,4 +183,25 @@ class InstrumentedTests {
}
}
}

@Test
fun nestedSafeCallNeverHappens() {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.P)
return // object mocks are not supported

var logCalled = false
mockkObject(Safe)
every { Safe.logIllegalSafeWithTimeoutUsage() } answers { logCalled = true }

Fingerprinter.Version.values().forEach { version ->
val fingerprinter = FingerprinterFactory.create(context)
val deviceId = callbackToSync { fingerprinter.getDeviceId(version = version) { emit(it) } }
StabilityLevel.values().forEach { stabilityLevel ->
val fingerprint = callbackToSync { fingerprinter.getFingerprint(version, stabilityLevel) { emit(it) } }
}
val fingerprintingSignalsProvider = fingerprinter.getFingerprintingSignalsProvider()!!
}

TestCase.assertEquals(false, logCalled)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.fingerprintjs.android.playground.utils
package com.fingerprintjs.android.fingerprint.utils

import java.util.concurrent.CountDownLatch

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ 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.threading.runOnAnotherThread
import com.fingerprintjs.android.fingerprint.tools.threading.safe.Safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe


Expand All @@ -32,7 +31,7 @@ public class Fingerprinter internal constructor(
// 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() }
safe { implFactory() }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ 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.hashers.Hasher
import com.fingerprintjs.android.fingerprint.tools.hashers.MurMur3x64x128Hasher
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout


/**
Expand Down Expand Up @@ -191,9 +191,9 @@ public object FingerprinterFactory {

private fun createMemoryInfoProvider(context: Context): MemInfoProvider {
return MemInfoProviderImpl(
activityManager = safe { context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }.getOrNull(),
internalStorageStats = safe { StatFs(Environment.getRootDirectory()!!.absolutePath!!) }.getOrNull(),
externalStorageStats = safe {
activityManager = safeWithTimeout { context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }.getOrNull(),
internalStorageStats = safeWithTimeout { StatFs(Environment.getRootDirectory()!!.absolutePath!!) }.getOrNull(),
externalStorageStats = safeWithTimeout {
context.getExternalFilesDir(null)
?.takeIf { it.canRead() }
?.let { StatFs(it.absolutePath!!) }!!
Expand All @@ -204,59 +204,59 @@ public object FingerprinterFactory {
private fun createOsBuildInfoProvider() = OsBuildInfoProviderImpl()

private fun createGsfIdProvider(context: Context) = GsfIdProvider(
safe { context.contentResolver!! }.getOrDefault(null)
safeWithTimeout { context.contentResolver!! }.getOrDefault(null)
)

private fun createMediaDrmProvider() = MediaDrmIdProvider()

private fun createAndroidIdProvider(context: Context) = AndroidIdProvider(
safe { context.contentResolver!! }.getOrDefault(null)
safeWithTimeout { context.contentResolver!! }.getOrDefault(null)
)

private fun createSensorDataSource(context: Context) = SensorDataSourceImpl(
safe { context.getSystemService(Context.SENSOR_SERVICE) as SensorManager }.getOrNull()
safeWithTimeout { context.getSystemService(Context.SENSOR_SERVICE) as SensorManager }.getOrNull()
)

private fun createInputDevicesDataSource(context: Context) = InputDevicesDataSourceImpl(
safe { context.getSystemService(Context.INPUT_SERVICE) as InputManager }.getOrNull()
safeWithTimeout { context.getSystemService(Context.INPUT_SERVICE) as InputManager }.getOrNull()
)

private fun createPackageManagerDataSource(context: Context) = PackageManagerDataSourceImpl(
safe { context.packageManager!! }.getOrNull()
safeWithTimeout { context.packageManager!! }.getOrNull()
)

private fun createSettingsDataSource(context: Context) = SettingsDataSourceImpl(
safe { context.contentResolver!! }.getOrNull()
safeWithTimeout { context.contentResolver!! }.getOrNull()
)


private fun createDevicePersonalizationDataSource(context: Context) =
DevicePersonalizationInfoProviderImpl(
ringtoneManager = safe { RingtoneManager(context) }.getOrNull(),
assetManager = safe { context.assets!! }.getOrNull(),
configuration = safe { context.resources!!.configuration!! }.getOrNull(),
ringtoneManager = safeWithTimeout { RingtoneManager(context) }.getOrNull(),
assetManager = safeWithTimeout { context.assets!! }.getOrNull(),
configuration = safeWithTimeout { context.resources!!.configuration!! }.getOrNull(),
)

private fun createFingerprintSensorStatusProvider(context: Context) =
FingerprintSensorInfoProviderImpl(
safe { FingerprintManagerCompat.from(context)!! }.getOrNull()
safeWithTimeout { FingerprintManagerCompat.from(context)!! }.getOrNull()
)

private fun createDeviceSecurityProvider(context: Context) = DeviceSecurityInfoProviderImpl(
safe { context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager }.getOrNull(),
safe { context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager }.getOrNull(),
safeWithTimeout { context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager }.getOrNull(),
safeWithTimeout { context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager }.getOrNull(),
)

private fun createCodecInfoProvider() = CodecInfoProviderImpl(
safe { MediaCodecList(MediaCodecList.ALL_CODECS) }.getOrNull()
safeWithTimeout { MediaCodecList(MediaCodecList.ALL_CODECS) }.getOrNull()
)

private fun createBatteryInfoDataSource(context: Context) = BatteryInfoProviderImpl(context)

private fun createCameraInfoProvider(): CameraInfoProvider = CameraInfoProviderImpl()

private fun createGpuInfoProvider(context: Context) =
GpuInfoProviderImpl(safe { context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }.getOrNull())
GpuInfoProviderImpl(safeWithTimeout { context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }.getOrNull())

//endregion

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.fingerprintjs.android.fingerprint.signal_providers.os_build.OsBuildSi
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.FingerprintingLegacySchemeSupportExtensions
import com.fingerprintjs.android.fingerprint.tools.hashers.Hasher
import com.fingerprintjs.android.fingerprint.tools.threading.safe.Safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe


Expand All @@ -25,6 +24,7 @@ internal class FingerprinterImpl internal constructor(
) {
@Volatile
private var deviceIdResult: DeviceIdResult? = null

@Volatile
private var fingerprintResult: FingerprintResult? = null

Expand All @@ -33,7 +33,7 @@ internal class FingerprinterImpl internal constructor(
fun getDeviceId(): Result<DeviceIdResult> {
require(legacyArgs != null)

return safe(timeoutMs = Safe.timeoutLong) {
return safe {
deviceIdResult?.let { return@safe it }
val deviceIdResult = DeviceIdResult(
legacyArgs.deviceIdProvider.fingerprint(),
Expand All @@ -48,7 +48,7 @@ internal class FingerprinterImpl internal constructor(

@WorkerThread
fun getDeviceId(version: Fingerprinter.Version): Result<DeviceIdResult> {
return safe(timeoutMs = Safe.timeoutLong) {
return safe {
DeviceIdResult(
deviceId = deviceIdSignalsProvider.getSignalMatching(version).getIdString(),
gsfId = deviceIdSignalsProvider.gsfIdSignal.getIdString(),
Expand All @@ -65,7 +65,7 @@ internal class FingerprinterImpl internal constructor(
): Result<FingerprintResult> {
require(legacyArgs != null)

return safe(timeoutMs = Safe.timeoutLong) {
return safe {
fingerprintResult?.let { return@safe it }
val fingerprintSb = StringBuilder()

Expand Down Expand Up @@ -101,7 +101,7 @@ internal class FingerprinterImpl internal constructor(
hasher: Hasher,
): Result<String> {
return if (version < Fingerprinter.Version.fingerprintingFlattenedSignalsFirstVersion) {
safe(timeoutMs = Safe.timeoutLong) {
safe {
val joinedHashes = with(FingerprintingLegacySchemeSupportExtensions) {
listOf(
hasher.hash(fpSignalsProvider.getHardwareSignals(version, stabilityLevel)),
Expand All @@ -126,7 +126,7 @@ internal class FingerprinterImpl internal constructor(
fingerprintingSignals: List<FingerprintingSignal<*>>,
hasher: Hasher,
): Result<String> {
return safe(timeoutMs = Safe.timeoutLong) { hasher.hash(fingerprintingSignals) }
return safe { hasher.hash(fingerprintingSignals) }
}

internal fun getFingerprintingSignalsProvider(): FingerprintingSignalsProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import android.annotation.SuppressLint
import android.content.ContentResolver
import android.provider.Settings
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout


@Deprecated(message = DeprecationMessages.UNREACHABLE_SYMBOL_UNINTENDED_PUBLIC_API)
Expand All @@ -14,7 +14,7 @@ public class AndroidIdProvider(
) {
@SuppressLint("HardwareIds")
public fun getAndroidId(): String {
return safe {
return safeWithTimeout {
Settings.Secure.getString(
contentResolver!!,
Settings.Secure.ANDROID_ID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package com.fingerprintjs.android.fingerprint.device_id_providers
import android.content.ContentResolver
import android.net.Uri
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout


@Deprecated(message = DeprecationMessages.UNREACHABLE_SYMBOL_UNINTENDED_PUBLIC_API)
Expand All @@ -13,7 +13,7 @@ public class GsfIdProvider(
) {

public fun getGsfAndroidId(): String? {
return safe {
return safeWithTimeout {
getGsfId()
}.getOrDefault("")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ package com.fingerprintjs.android.fingerprint.device_id_providers
import android.media.MediaDrm
import android.os.Build
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout
import java.security.MessageDigest
import java.util.UUID


@Deprecated(message = DeprecationMessages.UNREACHABLE_SYMBOL_UNINTENDED_PUBLIC_API)
public class MediaDrmIdProvider {
public fun getMediaDrmId(): String? = safe {
public fun getMediaDrmId(): String? = safeWithTimeout {
mediaDrmId()
}.getOrDefault(null)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout


@Deprecated(message = DeprecationMessages.UNREACHABLE_SYMBOL_UNINTENDED_PUBLIC_API)
Expand All @@ -20,7 +20,7 @@ internal class BatteryInfoProviderImpl(
private val applicationContext: Context
) : BatteryInfoProvider {
override fun batteryHealth(): String {
return safe {
return safeWithTimeout {
val intent = applicationContext
.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))!!

Expand All @@ -36,7 +36,7 @@ internal class BatteryInfoProviderImpl(

@SuppressLint("PrivateApi")
override fun batteryTotalCapacity(): String {
return safe {
return safeWithTimeout {
val mPowerProfile = Class.forName(POWER_PROFILE_CLASS_NAME)
.getConstructor(Context::class.java)
.newInstance(applicationContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.fingerprintjs.android.fingerprint.info_providers

import android.hardware.Camera
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout
import java.util.LinkedList


Expand All @@ -22,7 +22,7 @@ public interface CameraInfoProvider {
internal class CameraInfoProviderImpl(
) : CameraInfoProvider {
override fun getCameraInfo(): List<CameraInfo> {
return safe {
return safeWithTimeout {
extractInfo()
}.getOrDefault(emptyList())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.fingerprintjs.android.fingerprint.info_providers

import android.media.MediaCodecList
import com.fingerprintjs.android.fingerprint.tools.DeprecationMessages
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safe
import com.fingerprintjs.android.fingerprint.tools.threading.safe.safeWithTimeout


public data class MediaCodecInfo(
Expand All @@ -20,7 +20,7 @@ internal class CodecInfoProviderImpl(
private val codecList: MediaCodecList?,
) : CodecInfoProvider {
override fun codecsList(): List<MediaCodecInfo> {
return safe {
return safeWithTimeout {
extractCodecInfo()
}.getOrDefault(emptyList())
}
Expand Down
Loading

0 comments on commit 187dc1a

Please sign in to comment.