Skip to content

Commit eb238bf

Browse files
committed
feat: only allow sending SMS messages once per second to prevent incurring significant charges
1 parent 4a6512e commit eb238bf

File tree

4 files changed

+25
-1
lines changed

4 files changed

+25
-1
lines changed

base/src/main/java/io/github/sds100/keymapper/base/utils/ErrorUtils.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ fun KMError.getFullMessage(resourceProvider: ResourceProvider): String {
249249
PurchasingError.PurchasingNotImplemented -> resourceProvider.getString(R.string.purchasing_error_not_implemented)
250250

251251
is KMError.KeyEventActionError -> resourceProvider.getString(R.string.error_fix_key_event_action)
252+
is KMError.KeyMapperSmsRateLimit -> resourceProvider.getString(R.string.error_sms_rate_limit)
252253

253254
else -> this.toString()
254255
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,7 @@
866866
<string name="error_ui_element_not_found">UI element not found!</string>
867867
<string name="error_shell_command_timeout">Command timed out after %1$d seconds</string>
868868
<string name="error_system_bridge_disconnected">PRO Mode needs starting</string>
869+
<string name="error_sms_rate_limit">Rate limit reached. You can only send once per second.</string>
869870
<!-- Error messages -->
870871

871872
<!-- action labels -->

common/src/main/java/io/github/sds100/keymapper/common/utils/KMResult.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ abstract class KMError : KMResult<Nothing>() {
6060
data object NoAppToPhoneCall : KMError()
6161
data object NoAppToSendSms : KMError()
6262
data class SendSmsError(val resultCode: Int) : KMError()
63+
data object KeyMapperSmsRateLimit : KMError()
6364

6465
data class NotAFile(val uri: String) : KMError()
6566
data class NotADirectory(val uri: String) : KMError()

system/src/main/java/io/github/sds100/keymapper/system/phone/AndroidPhoneAdapter.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.content.Intent
1111
import android.content.IntentFilter
1212
import android.content.pm.PackageManager
1313
import android.os.Build
14+
import android.os.SystemClock
1415
import android.telecom.TelecomManager
1516
import android.telephony.PhoneStateListener
1617
import android.telephony.SmsManager
@@ -29,6 +30,7 @@ import kotlinx.coroutines.channels.Channel
2930
import kotlinx.coroutines.flow.MutableSharedFlow
3031
import kotlinx.coroutines.launch
3132
import kotlinx.coroutines.withTimeout
33+
import timber.log.Timber
3234
import javax.inject.Inject
3335
import javax.inject.Singleton
3436

@@ -39,6 +41,11 @@ class AndroidPhoneAdapter @Inject constructor(
3941
) : PhoneAdapter {
4042
companion object {
4143
private const val ACTION_SMS_SENT_RESULT = "io.github.sds100.keymapper.SMS_SENT_RESULT"
44+
45+
/**
46+
* The minimum frequency that SMS messages can be sent.
47+
*/
48+
private const val SMS_MIN_RATE_MILLIS = 1000L
4249
}
4350

4451
private val ctx: Context = context.applicationContext
@@ -57,9 +64,17 @@ class AndroidPhoneAdapter @Inject constructor(
5764

5865
override val callStateFlow: MutableSharedFlow<CallState> = MutableSharedFlow()
5966

60-
// Emits the result code in SmsManager
67+
/**
68+
* Emits the result code in SmsManager
69+
*/
6170
private val smsSentResultFlow = Channel<Int>()
6271

72+
/**
73+
* The time the last SMS was sent. This is used to prevent someone accidentally incurring
74+
* significant charges.
75+
*/
76+
private var lastSmsTime: Long = -1
77+
6378
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
6479
override fun onReceive(context: Context?, intent: Intent?) {
6580
context ?: return
@@ -170,7 +185,13 @@ class AndroidPhoneAdapter @Inject constructor(
170185
)
171186

172187
try {
188+
if (SystemClock.uptimeMillis() - lastSmsTime < SMS_MIN_RATE_MILLIS) {
189+
Timber.d("SMS rate limit exceeded to protect against significant costs")
190+
return KMError.KeyMapperSmsRateLimit
191+
}
192+
173193
smsManager.sendTextMessage(number, null, message, sentPendingIntent, null)
194+
lastSmsTime = SystemClock.uptimeMillis()
174195
} catch (e: IllegalArgumentException) {
175196
return KMError.Exception(e)
176197
}

0 commit comments

Comments
 (0)