Skip to content

Commit b077f2d

Browse files
Fix BillingResult ObjectDisposedException by creating persistent copies
Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com>
1 parent 59dfc94 commit b077f2d

File tree

1 file changed

+63
-8
lines changed
  • source/com.android.billingclient/billing/Additions

1 file changed

+63
-8
lines changed

source/com.android.billingclient/billing/Additions/Additions.cs

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,15 @@ internal class InternalAcknowledgePurchaseResponseListener : Java.Lang.Object, I
196196
public Action<BillingResult> AcknowledgePurchaseResponseHandler { get; set; }
197197

198198
public void OnAcknowledgePurchaseResponse(BillingResult result)
199-
=> AcknowledgePurchaseResponseHandler?.Invoke(result);
199+
{
200+
// Create a copy of the BillingResult to ensure it stays alive after the callback
201+
var resultCopy = BillingResult.NewBuilder()
202+
.SetResponseCode((int)result.ResponseCode)
203+
.SetDebugMessage(result.DebugMessage)
204+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
205+
.Build();
206+
AcknowledgePurchaseResponseHandler?.Invoke(resultCopy);
207+
}
200208
}
201209

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

211219
public void OnBillingSetupFinished(BillingResult result)
212-
=> BillingSetupFinishedHandler?.Invoke(result);
220+
{
221+
// Create a copy of the BillingResult to ensure it stays alive after the callback
222+
var resultCopy = BillingResult.NewBuilder()
223+
.SetResponseCode((int)result.ResponseCode)
224+
.SetDebugMessage(result.DebugMessage)
225+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
226+
.Build();
227+
BillingSetupFinishedHandler?.Invoke(resultCopy);
228+
}
213229
}
214230

215231
internal class InternalConsumeResponseListener : Java.Lang.Object, IConsumeResponseListener
216232
{
217233
public Action<BillingResult, string> ConsumeResponseHandler { get; set; }
218234
public void OnConsumeResponse(BillingResult result, string str)
219-
=> ConsumeResponseHandler?.Invoke(result, str);
235+
{
236+
// Create a copy of the BillingResult to ensure it stays alive after the callback
237+
var resultCopy = BillingResult.NewBuilder()
238+
.SetResponseCode((int)result.ResponseCode)
239+
.SetDebugMessage(result.DebugMessage)
240+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
241+
.Build();
242+
ConsumeResponseHandler?.Invoke(resultCopy, str);
243+
}
220244
}
221245

222246
internal class InternalPriceChangeConfirmationListener : Java.Lang.Object //, IPriceChangeConfirmationListener
223247
{
224248
public Action<BillingResult> PriceChangeConfirmationHandler { get; set; }
225249
public void OnPriceChangeConfirmationResult(BillingResult result)
226-
=> PriceChangeConfirmationHandler?.Invoke(result);
250+
{
251+
// Create a copy of the BillingResult to ensure it stays alive after the callback
252+
var resultCopy = BillingResult.NewBuilder()
253+
.SetResponseCode((int)result.ResponseCode)
254+
.SetDebugMessage(result.DebugMessage)
255+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
256+
.Build();
257+
PriceChangeConfirmationHandler?.Invoke(resultCopy);
258+
}
227259
}
228260

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

233265
public void OnPurchaseHistoryResponse(BillingResult result, IList<PurchaseHistoryRecord> history)
234-
=> PurchaseHistoryResponseHandler?.Invoke(result, history);
266+
{
267+
// Create a copy of the BillingResult to ensure it stays alive after the callback
268+
var resultCopy = BillingResult.NewBuilder()
269+
.SetResponseCode((int)result.ResponseCode)
270+
.SetDebugMessage(result.DebugMessage)
271+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
272+
.Build();
273+
PurchaseHistoryResponseHandler?.Invoke(resultCopy, history);
274+
}
235275
}
236276

237277
internal class InternalPurchasesUpdatedListener : Java.Lang.Object, IPurchasesUpdatedListener
238278
{
239279
public Action<BillingResult, IList<Purchase>> PurchasesUpdatedHandler { get; set; }
240280
public void OnPurchasesUpdated(BillingResult result, IList<Purchase> purchases)
241-
=> PurchasesUpdatedHandler?.Invoke(result, purchases);
281+
{
282+
// Create a copy of the BillingResult to ensure it stays alive after the callback
283+
var resultCopy = BillingResult.NewBuilder()
284+
.SetResponseCode((int)result.ResponseCode)
285+
.SetDebugMessage(result.DebugMessage)
286+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
287+
.Build();
288+
PurchasesUpdatedHandler?.Invoke(resultCopy, purchases);
289+
}
242290
}
243291

244292
[Obsolete("Use QueryProductDetailsAsync(QueryProductDetailsParams) instead")]
@@ -257,8 +305,15 @@ internal class InternalProductDetailsResponseListener : Java.Lang.Object, IProdu
257305
public void OnProductDetailsResponse(BillingResult result, QueryProductDetailsResult queryResult)
258306
{
259307
queryResult ??= new();
260-
queryResult.Result = result;
261-
ProductDetailsResponseHandler?.Invoke(result, queryResult);
308+
// Create a copy of the BillingResult to ensure it stays alive after the callback
309+
// The original result may be disposed by the native side, causing ObjectDisposedException
310+
var resultCopy = BillingResult.NewBuilder()
311+
.SetResponseCode((int)result.ResponseCode)
312+
.SetDebugMessage(result.DebugMessage)
313+
.SetOnPurchasesUpdatedSubResponseCode(result.OnPurchasesUpdatedSubResponseCode)
314+
.Build();
315+
queryResult.Result = resultCopy;
316+
ProductDetailsResponseHandler?.Invoke(resultCopy, queryResult);
262317
}
263318
}
264319

0 commit comments

Comments
 (0)