Skip to content

Commit 13118cd

Browse files
committed
[MOB-11167] Add timeout for crypto operations to solve ANRs
1 parent 03f1b01 commit 13118cd

1 file changed

Lines changed: 26 additions & 3 deletions

File tree

iterableapi/src/main/java/com/iterable/iterableapi/IterableKeychain.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ package com.iterable.iterableapi
22

33
import android.content.Context
44
import android.content.SharedPreferences
5+
import android.os.Handler
6+
import android.os.Looper
7+
import java.util.concurrent.Callable
8+
import java.util.concurrent.Executors
9+
import java.util.concurrent.TimeUnit
10+
import java.util.concurrent.TimeoutException
511

612
class IterableKeychain {
713
companion object {
@@ -10,6 +16,9 @@ class IterableKeychain {
1016
const val KEY_USER_ID = "iterable-user-id"
1117
const val KEY_AUTH_TOKEN = "iterable-auth-token"
1218
private const val PLAINTEXT_SUFFIX = "_plaintext"
19+
private const val CRYPTO_OPERATION_TIMEOUT_MS = 500L
20+
21+
private val cryptoExecutor = Executors.newSingleThreadExecutor()
1322
}
1423

1524
private var sharedPrefs: SharedPreferences
@@ -48,6 +57,10 @@ class IterableKeychain {
4857
}
4958
}
5059

60+
private fun <T> runWithTimeout(callable: Callable<T>): T {
61+
return cryptoExecutor.submit(callable).get(CRYPTO_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS)
62+
}
63+
5164
private fun handleDecryptionError(e: Exception? = null) {
5265
IterableLogger.w(TAG, "Decryption failed, clearing all data and regenerating key")
5366
sharedPrefs.edit()
@@ -56,7 +69,14 @@ class IterableKeychain {
5669
.remove(KEY_AUTH_TOKEN)
5770
.apply()
5871

59-
encryptor.resetKeys()
72+
try {
73+
// Use timeout for key reset operation
74+
runWithTimeout { encryptor.resetKeys(); Unit }
75+
} catch (ex: Exception) {
76+
// Just log if resetKeys times out or fails - don't recurse
77+
IterableLogger.e(TAG, "Failed to reset keys with timeout", ex)
78+
}
79+
6080
decryptionFailureHandler?.let { handler ->
6181
val exception = e ?: Exception("Unknown decryption error")
6282
try {
@@ -80,8 +100,10 @@ class IterableKeychain {
80100
return sharedPrefs.getString(key, null)
81101
}
82102

103+
val encryptedValue = sharedPrefs.getString(key, null) ?: return null
104+
83105
return try {
84-
sharedPrefs.getString(key, null)?.let { encryptor.decrypt(it) }
106+
runWithTimeout { encryptor.decrypt(encryptedValue) }
85107
} catch (e: Exception) {
86108
handleDecryptionError(e)
87109
null
@@ -96,7 +118,8 @@ class IterableKeychain {
96118
}
97119

98120
try {
99-
editor.putString(key, encryptor.encrypt(value))
121+
val encrypted = runWithTimeout { encryptor.encrypt(value) }
122+
editor.putString(key, encrypted)
100123
.remove(key + PLAINTEXT_SUFFIX)
101124
.apply()
102125
} catch (e: Exception) {

0 commit comments

Comments
 (0)