mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Utilize re-entrant locking for in app payments instead of synchronized blocks.
This commit is contained in:
committed by
Greyson Parrelli
parent
a79b4c3ba0
commit
ed24fd0c4b
@@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.rx.RxStore
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import java.util.Locale
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
class InternalDonorErrorConfigurationViewModel : ViewModel() {
|
||||
|
||||
@@ -101,7 +102,7 @@ class InternalDonorErrorConfigurationViewModel : ViewModel() {
|
||||
fun save(): Completable {
|
||||
val snapshot = store.state
|
||||
val saveState = Completable.fromAction {
|
||||
synchronized(InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
InAppPaymentSubscriberRecord.Type.DONATION.lock.withLock {
|
||||
when {
|
||||
snapshot.selectedBadge?.isGift() == true -> handleGiftExpiration(snapshot)
|
||||
snapshot.selectedBadge?.isBoost() == true -> handleBoostExpiration(snapshot)
|
||||
@@ -116,7 +117,7 @@ class InternalDonorErrorConfigurationViewModel : ViewModel() {
|
||||
|
||||
fun clearErrorState(): Completable {
|
||||
return Completable.fromAction {
|
||||
synchronized(InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
InAppPaymentSubscriberRecord.Type.DONATION.lock.withLock {
|
||||
SignalStore.inAppPayments.setExpiredBadge(null)
|
||||
SignalStore.inAppPayments.setExpiredGiftBadge(null)
|
||||
SignalStore.inAppPayments.unexpectedSubscriptionCancelationReason = null
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.whispersystems.signalservice.internal.push.exceptions.InAppPaymentPro
|
||||
import java.security.SecureRandom
|
||||
import java.util.Currency
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.locks.Lock
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
@@ -231,10 +232,11 @@ object InAppPaymentsRepository {
|
||||
/**
|
||||
* Returns the object to utilize as a mutex for recurring subscriptions.
|
||||
*/
|
||||
fun resolveMutex(inAppPaymentId: InAppPaymentTable.InAppPaymentId): Any {
|
||||
@WorkerThread
|
||||
fun resolveLock(inAppPaymentId: InAppPaymentTable.InAppPaymentId): Lock {
|
||||
val payment = SignalDatabase.inAppPayments.getById(inAppPaymentId) ?: error("Not found")
|
||||
|
||||
return payment.type.requireSubscriberType()
|
||||
return payment.type.requireSubscriberType().lock
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -214,7 +214,7 @@ object RecurringInAppPaymentRepository {
|
||||
subscriptionLevel,
|
||||
subscriber.currency.currencyCode,
|
||||
levelUpdateOperation.idempotencyKey.serialize(),
|
||||
subscriberType
|
||||
subscriberType.lock
|
||||
)
|
||||
}
|
||||
.flatMapCompletable {
|
||||
|
||||
@@ -9,6 +9,8 @@ import org.signal.donations.InAppPaymentType
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
import java.util.Currency
|
||||
import java.util.concurrent.locks.Lock
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
|
||||
/**
|
||||
* Represents a SubscriberId and metadata that can be used for a recurring
|
||||
@@ -24,7 +26,7 @@ data class InAppPaymentSubscriberRecord(
|
||||
/**
|
||||
* Serves as the mutex by which to perform mutations to subscriptions.
|
||||
*/
|
||||
enum class Type(val code: Int, val jobQueue: String, val inAppPaymentType: InAppPaymentType) {
|
||||
enum class Type(val code: Int, val jobQueue: String, val inAppPaymentType: InAppPaymentType, val lock: Lock = ReentrantLock()) {
|
||||
/**
|
||||
* A recurring donation
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.RemoteConfig
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
/**
|
||||
* Checks and rectifies state pertaining to backups subscriptions.
|
||||
@@ -85,7 +86,7 @@ class BackupSubscriptionCheckJob private constructor(parameters: Parameters) : C
|
||||
val purchase: BillingPurchaseResult = AppDependencies.billingApi.queryPurchases()
|
||||
val hasActivePurchase = purchase is BillingPurchaseResult.Success && purchase.isAcknowledged && purchase.isWithinTheLastMonth()
|
||||
|
||||
synchronized(InAppPaymentSubscriberRecord.Type.BACKUP) {
|
||||
InAppPaymentSubscriberRecord.Type.BACKUP.lock.withLock {
|
||||
val inAppPayment = SignalDatabase.inAppPayments.getLatestInAppPaymentByType(InAppPaymentType.RECURRING_BACKUP)
|
||||
|
||||
if (inAppPayment?.state == InAppPaymentTable.State.PENDING) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
/**
|
||||
* Job to redeem a verified donation receipt. It is up to the Job prior in the chain to specify a valid
|
||||
@@ -157,8 +158,12 @@ public class DonationReceiptRedemptionJob extends BaseJob {
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
if (isForSubscription()) {
|
||||
synchronized (InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
Lock lock = InAppPaymentSubscriberRecord.Type.DONATION.getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
doRun();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
doRun();
|
||||
|
||||
@@ -167,7 +167,7 @@ class ExternalLaunchDonationJob private constructor(
|
||||
subscriptionLevel,
|
||||
subscriber.currency.currencyCode,
|
||||
levelUpdateOperation.idempotencyKey.serialize(),
|
||||
subscriber.type
|
||||
subscriber.type.lock
|
||||
)
|
||||
|
||||
getResultOrThrow(updateSubscriptionLevelResponse, doOnApplicationError = {
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||
import org.thoughtcrime.securesms.util.Environment
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
/**
|
||||
@@ -77,7 +78,7 @@ class InAppPaymentAuthCheckJob private constructor(parameters: Parameters) : Bas
|
||||
var hasRetry = false
|
||||
for (payment in unauthorizedInAppPayments) {
|
||||
val verificationStatus: CheckResult<Unit> = if (payment.type.recurring) {
|
||||
synchronized(payment.type.requireSubscriberType().inAppPaymentType) {
|
||||
payment.type.requireSubscriberType().lock.withLock {
|
||||
checkRecurringPayment(payment)
|
||||
}
|
||||
} else {
|
||||
@@ -244,7 +245,7 @@ class InAppPaymentAuthCheckJob private constructor(parameters: Parameters) : Bas
|
||||
level,
|
||||
subscriber.currency.currencyCode,
|
||||
updateOperation.idempotencyKey.serialize(),
|
||||
subscriber.type
|
||||
subscriber.type.lock
|
||||
)
|
||||
|
||||
val updateLevelResult = checkResult(updateLevelResponse)
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.internal.EmptyResponse
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import java.util.Locale
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
@@ -90,7 +91,7 @@ class InAppPaymentKeepAliveJob private constructor(
|
||||
}
|
||||
|
||||
override fun onRun() {
|
||||
synchronized(type) {
|
||||
type.lock.withLock {
|
||||
doRun()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager.Chain
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
/**
|
||||
* Submits a purchase token to the server to link it with a subscriber id.
|
||||
@@ -76,7 +77,7 @@ class InAppPaymentPurchaseTokenJob private constructor(
|
||||
}
|
||||
|
||||
override fun onRun() {
|
||||
synchronized(InAppPaymentsRepository.resolveMutex(inAppPaymentId)) {
|
||||
InAppPaymentsRepository.resolveLock(inAppPaymentId).withLock {
|
||||
doRun()
|
||||
}
|
||||
}
|
||||
@@ -87,7 +88,7 @@ class InAppPaymentPurchaseTokenJob private constructor(
|
||||
val response = AppDependencies.donationsService.linkGooglePlayBillingPurchaseTokenToSubscriberId(
|
||||
inAppPayment.subscriberId!!,
|
||||
inAppPayment.data.redemption!!.googlePlayBillingPurchaseToken!!,
|
||||
InAppPaymentSubscriberRecord.Type.BACKUP
|
||||
InAppPaymentSubscriberRecord.Type.BACKUP.lock
|
||||
)
|
||||
|
||||
if (response.applicationError.isPresent) {
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription.Sub
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import java.io.IOException
|
||||
import java.util.Currency
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
@@ -128,7 +129,7 @@ class InAppPaymentRecurringContextJob private constructor(
|
||||
}
|
||||
|
||||
override fun onRun() {
|
||||
synchronized(InAppPaymentsRepository.resolveMutex(inAppPaymentId)) {
|
||||
InAppPaymentsRepository.resolveLock(inAppPaymentId).withLock {
|
||||
doRun()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.util.hasGiftBadge
|
||||
import org.thoughtcrime.securesms.util.requireGiftBadge
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||
import java.io.IOException
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
/**
|
||||
* Takes a ReceiptCredentialResponse and submits it to the server for redemption.
|
||||
@@ -181,7 +182,7 @@ class InAppPaymentRedemptionJob private constructor(
|
||||
}
|
||||
|
||||
if (inAppPayment.type.recurring) {
|
||||
synchronized(inAppPayment.type.requireSubscriberType()) {
|
||||
inAppPayment.type.requireSubscriberType().lock.withLock {
|
||||
performInAppPaymentRedemption(inAppPayment)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
@@ -57,8 +58,12 @@ public class SubscriptionKeepAliveJob extends BaseJob {
|
||||
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
synchronized (InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
Lock lock = InAppPaymentSubscriberRecord.Type.DONATION.getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
doRun();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
@@ -140,8 +141,12 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
||||
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
synchronized (InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
Lock lock = InAppPaymentSubscriberRecord.Type.DONATION.getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
doRun();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.util.Currency
|
||||
import java.util.Locale
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
/**
|
||||
* Key-Value store for in app payment related values. Note that most of this file will be deprecated after the release of
|
||||
@@ -449,7 +450,7 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
|
||||
*/
|
||||
@WorkerThread
|
||||
fun updateLocalStateForManualCancellation(subscriberType: InAppPaymentSubscriberRecord.Type) {
|
||||
synchronized(subscriberType) {
|
||||
subscriberType.lock.withLock {
|
||||
Log.d(TAG, "[updateLocalStateForManualCancellation] Clearing donation values.")
|
||||
clearLevelOperations()
|
||||
|
||||
@@ -493,7 +494,7 @@ class InAppPaymentValues internal constructor(store: KeyValueStore) : SignalStor
|
||||
*/
|
||||
@WorkerThread
|
||||
fun updateLocalStateForLocalSubscribe(subscriberType: InAppPaymentSubscriberRecord.Type) {
|
||||
synchronized(subscriberType) {
|
||||
subscriberType.lock.withLock {
|
||||
clearLevelOperations()
|
||||
|
||||
if (subscriberType == InAppPaymentSubscriberRecord.Type.DONATION) {
|
||||
|
||||
Reference in New Issue
Block a user