@@ -2,6 +2,12 @@ package com.iterable.iterableapi
22
33import android.content.Context
44import 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
612class 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