Skip to content

Fix BillingResult ObjectDisposedException in Billing 8.0.0.1 #1232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
71 changes: 63 additions & 8 deletions source/com.android.billingclient/billing/Additions/Additions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,15 @@ internal class InternalAcknowledgePurchaseResponseListener : Java.Lang.Object, I
public Action<BillingResult> AcknowledgePurchaseResponseHandler { get; set; }

public void OnAcknowledgePurchaseResponse(BillingResult result)
=> AcknowledgePurchaseResponseHandler?.Invoke(result);
{
// Create a copy of the BillingResult to ensure it stays alive after the callback
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
AcknowledgePurchaseResponseHandler?.Invoke(resultCopy);
}
}

internal class InternalBillingClientStateListener : Java.Lang.Object, IBillingClientStateListener
Expand All @@ -209,36 +217,76 @@ public void OnBillingServiceDisconnected()
=> BillingServiceDisconnectedHandler?.Invoke();

public void OnBillingSetupFinished(BillingResult result)
=> BillingSetupFinishedHandler?.Invoke(result);
{
// Create a copy of the BillingResult to ensure it stays alive after the callback
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
BillingSetupFinishedHandler?.Invoke(resultCopy);
}
}

internal class InternalConsumeResponseListener : Java.Lang.Object, IConsumeResponseListener
{
public Action<BillingResult, string> ConsumeResponseHandler { get; set; }
public void OnConsumeResponse(BillingResult result, string str)
=> ConsumeResponseHandler?.Invoke(result, str);
{
// Create a copy of the BillingResult to ensure it stays alive after the callback
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
ConsumeResponseHandler?.Invoke(resultCopy, str);
}
}

internal class InternalPriceChangeConfirmationListener : Java.Lang.Object //, IPriceChangeConfirmationListener
{
public Action<BillingResult> PriceChangeConfirmationHandler { get; set; }
public void OnPriceChangeConfirmationResult(BillingResult result)
=> PriceChangeConfirmationHandler?.Invoke(result);
{
// Create a copy of the BillingResult to ensure it stays alive after the callback
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
PriceChangeConfirmationHandler?.Invoke(resultCopy);
}
}

internal class InternalPurchaseHistoryResponseListener : Java.Lang.Object, IPurchaseHistoryResponseListener
{
public Action<BillingResult, IList<PurchaseHistoryRecord>> PurchaseHistoryResponseHandler { get; set; }

public void OnPurchaseHistoryResponse(BillingResult result, IList<PurchaseHistoryRecord> history)
=> PurchaseHistoryResponseHandler?.Invoke(result, history);
{
// Create a copy of the BillingResult to ensure it stays alive after the callback
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
PurchaseHistoryResponseHandler?.Invoke(resultCopy, history);
}
}

internal class InternalPurchasesUpdatedListener : Java.Lang.Object, IPurchasesUpdatedListener
{
public Action<BillingResult, IList<Purchase>> PurchasesUpdatedHandler { get; set; }
public void OnPurchasesUpdated(BillingResult result, IList<Purchase> purchases)
=> PurchasesUpdatedHandler?.Invoke(result, purchases);
{
// Create a copy of the BillingResult to ensure it stays alive after the callback
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
PurchasesUpdatedHandler?.Invoke(resultCopy, purchases);
}
}

[Obsolete("Use QueryProductDetailsAsync(QueryProductDetailsParams) instead")]
Expand All @@ -257,8 +305,15 @@ internal class InternalProductDetailsResponseListener : Java.Lang.Object, IProdu
public void OnProductDetailsResponse(BillingResult result, QueryProductDetailsResult queryResult)
{
queryResult ??= new();
queryResult.Result = result;
ProductDetailsResponseHandler?.Invoke(result, queryResult);
// Create a copy of the BillingResult to ensure it stays alive after the callback
// The original result may be disposed by the native side, causing ObjectDisposedException
var resultCopy = BillingResult.NewBuilder()
.SetResponseCode((int)result.ResponseCode)
.SetDebugMessage(result.DebugMessage)
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
.Build();
queryResult.Result = resultCopy;
ProductDetailsResponseHandler?.Invoke(resultCopy, queryResult);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,12 @@ Android.BillingClient.Api.QueryProductDetailsParams.Product.Zza() -> string!
Android.BillingClient.Api.QueryProductDetailsParams.Product.Zzb() -> string!
Android.BillingClient.Api.QueryProductDetailsParams.Zzb() -> string!
Android.BillingClient.Api.QueryProductDetailsResult
Android.BillingClient.Api.QueryProductDetailsResult.ProductDetails.get -> System.Collections.Generic.IList<Android.BillingClient.Api.ProductDetails!>!
Android.BillingClient.Api.QueryProductDetailsResult.ProductDetails.set -> void
Android.BillingClient.Api.QueryProductDetailsResult.ProductDetailsList.get -> System.Collections.Generic.IList<Android.BillingClient.Api.ProductDetails!>!
Android.BillingClient.Api.QueryProductDetailsResult.QueryProductDetailsResult() -> void
Android.BillingClient.Api.QueryProductDetailsResult.Result.get -> Android.BillingClient.Api.BillingResult!
Android.BillingClient.Api.QueryProductDetailsResult.Result.set -> void
Android.BillingClient.Api.QueryProductDetailsResult.UnfetchedProductList.get -> System.Collections.Generic.IList<Android.BillingClient.Api.UnfetchedProduct!>!
Android.BillingClient.Api.QueryPurchaseHistoryParams
Android.BillingClient.Api.QueryPurchaseHistoryParams.Builder
Expand Down
Loading