From a22f2cf0f12a29a6d29c50d7c11f35e872e31070 Mon Sep 17 00:00:00 2001 From: Jonathan Trowbridge Date: Mon, 8 Jul 2019 22:46:35 -0400 Subject: [PATCH 1/3] master - Fix tcsPurchase Bugs This commit fixes bugs surrounding tcsPurchase in the Andorid InAppBillingImplementation class. 1. HandleActivityResult uses null-conditional operator to access tcsPurcahse to avoid NullReferenceException. 2. HandleActivityResult now invokes TrySetException rather than SetException to avoid InvalidOperationException. 3. ConnectAsync() now invokes TryCancel() and sets tcsPurcahse to null. This was done to prevent tcsPurcahse from being stuck in a WaitingForActivation TaskStatus state when the activity is destroyed in recreated while the purchase modal is being presented to the user. This was causing all future purchase attempts to return null when invoking PurchaseAsync(). --- .../InAppBillingImplementation.cs | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs b/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs index cf1a609..307b8fc 100644 --- a/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs +++ b/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs @@ -365,6 +365,8 @@ async Task PurchaseAsync(string productSku, string itemType, string pa public override Task ConnectAsync(ItemType itemType = ItemType.InAppPurchase) { serviceConnection = new InAppBillingServiceConnection(Context, itemType); + tcsPurchase?.TrySetCanceled(); + tcsPurchase = null; return serviceConnection.ConnectAsync(); } @@ -522,38 +524,38 @@ public static void HandleActivityResult(int requestCode, Result resultCode, Inte switch (responseCode) { - case 0: - //Reponse returned OK - var purchaseData = data.GetStringExtra(RESPONSE_IAP_DATA); - var dataSignature = data.GetStringExtra(RESPONSE_IAP_DATA_SIGNATURE); - - tcsPurchase?.TrySetResult(new PurchaseResponse - { - PurchaseData = purchaseData, - DataSignature = dataSignature - }); - break; - case RESPONSE_CODE_RESULT_USER_CANCELED: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.UserCancelled)); - break; - case RESPONSE_CODE_RESULT_SERVICE_UNAVAILABLE: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.ServiceUnavailable)); - break; - case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.ItemUnavailable)); - break; - case BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.DeveloperError)); - break; - case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.AlreadyOwned)); - break; - case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.NotOwned)); - break; - default: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.GeneralError, responseCode.ToString())); - break; + case 0: + //Reponse returned OK + var purchaseData = data.GetStringExtra(RESPONSE_IAP_DATA); + var dataSignature = data.GetStringExtra(RESPONSE_IAP_DATA_SIGNATURE); + + tcsPurchase?.TrySetResult(new PurchaseResponse + { + PurchaseData = purchaseData, DataSignature = dataSignature + }); + break; + case RESPONSE_CODE_RESULT_USER_CANCELED: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.UserCancelled)); + break; + case RESPONSE_CODE_RESULT_SERVICE_UNAVAILABLE: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.ServiceUnavailable)); + break; + case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.ItemUnavailable)); + break; + case BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.DeveloperError)); + break; + case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.AlreadyOwned)); + break; + case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.NotOwned)); + break; + default: + tcsPurchase?.TrySetException( + new InAppBillingPurchaseException(PurchaseError.GeneralError, responseCode.ToString())); + break; } } From 9978ab99368e91ec5d9b746c2ef90e72196008f9 Mon Sep 17 00:00:00 2001 From: Jonathan Trowbridge Date: Mon, 8 Jul 2019 22:53:35 -0400 Subject: [PATCH 2/3] Revert "master - Fix tcsPurchase Bugs" This reverts commit a22f2cf0f12a29a6d29c50d7c11f35e872e31070. --- .../InAppBillingImplementation.cs | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs b/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs index 307b8fc..cf1a609 100644 --- a/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs +++ b/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs @@ -365,8 +365,6 @@ async Task PurchaseAsync(string productSku, string itemType, string pa public override Task ConnectAsync(ItemType itemType = ItemType.InAppPurchase) { serviceConnection = new InAppBillingServiceConnection(Context, itemType); - tcsPurchase?.TrySetCanceled(); - tcsPurchase = null; return serviceConnection.ConnectAsync(); } @@ -524,38 +522,38 @@ public static void HandleActivityResult(int requestCode, Result resultCode, Inte switch (responseCode) { - case 0: - //Reponse returned OK - var purchaseData = data.GetStringExtra(RESPONSE_IAP_DATA); - var dataSignature = data.GetStringExtra(RESPONSE_IAP_DATA_SIGNATURE); - - tcsPurchase?.TrySetResult(new PurchaseResponse - { - PurchaseData = purchaseData, DataSignature = dataSignature - }); - break; - case RESPONSE_CODE_RESULT_USER_CANCELED: - tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.UserCancelled)); - break; - case RESPONSE_CODE_RESULT_SERVICE_UNAVAILABLE: - tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.ServiceUnavailable)); - break; - case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: - tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.ItemUnavailable)); - break; - case BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: - tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.DeveloperError)); - break; - case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: - tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.AlreadyOwned)); - break; - case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: - tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.NotOwned)); - break; - default: - tcsPurchase?.TrySetException( - new InAppBillingPurchaseException(PurchaseError.GeneralError, responseCode.ToString())); - break; + case 0: + //Reponse returned OK + var purchaseData = data.GetStringExtra(RESPONSE_IAP_DATA); + var dataSignature = data.GetStringExtra(RESPONSE_IAP_DATA_SIGNATURE); + + tcsPurchase?.TrySetResult(new PurchaseResponse + { + PurchaseData = purchaseData, + DataSignature = dataSignature + }); + break; + case RESPONSE_CODE_RESULT_USER_CANCELED: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.UserCancelled)); + break; + case RESPONSE_CODE_RESULT_SERVICE_UNAVAILABLE: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.ServiceUnavailable)); + break; + case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.ItemUnavailable)); + break; + case BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.DeveloperError)); + break; + case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.AlreadyOwned)); + break; + case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.NotOwned)); + break; + default: + tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.GeneralError, responseCode.ToString())); + break; } } From 40051a3d91f2885394860337fe6b2057a8a8512e Mon Sep 17 00:00:00 2001 From: Jonathan Trowbridge Date: Mon, 8 Jul 2019 22:46:35 -0400 Subject: [PATCH 3/3] master - Fix tcsPurchase Bugs This commit fixes bugs surrounding tcsPurchase in the Andorid InAppBillingImplementation class. 1. HandleActivityResult uses null-conditional operator to access tcsPurcahse to avoid NullReferenceException. 2. HandleActivityResult now invokes TrySetException rather than SetException to avoid InvalidOperationException. 3. ConnectAsync() now invokes TryCancel() and sets tcsPurcahse to null. This was done to prevent tcsPurcahse from being stuck in a WaitingForActivation TaskStatus state when the activity is destroyed in recreated while the purchase modal is being presented to the user. This was causing all future purchase attempts to return null when invoking PurchaseAsync(). --- .../InAppBillingImplementation.cs | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs b/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs index cf1a609..307b8fc 100644 --- a/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs +++ b/src/Plugin.InAppBilling.Android/InAppBillingImplementation.cs @@ -365,6 +365,8 @@ async Task PurchaseAsync(string productSku, string itemType, string pa public override Task ConnectAsync(ItemType itemType = ItemType.InAppPurchase) { serviceConnection = new InAppBillingServiceConnection(Context, itemType); + tcsPurchase?.TrySetCanceled(); + tcsPurchase = null; return serviceConnection.ConnectAsync(); } @@ -522,38 +524,38 @@ public static void HandleActivityResult(int requestCode, Result resultCode, Inte switch (responseCode) { - case 0: - //Reponse returned OK - var purchaseData = data.GetStringExtra(RESPONSE_IAP_DATA); - var dataSignature = data.GetStringExtra(RESPONSE_IAP_DATA_SIGNATURE); - - tcsPurchase?.TrySetResult(new PurchaseResponse - { - PurchaseData = purchaseData, - DataSignature = dataSignature - }); - break; - case RESPONSE_CODE_RESULT_USER_CANCELED: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.UserCancelled)); - break; - case RESPONSE_CODE_RESULT_SERVICE_UNAVAILABLE: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.ServiceUnavailable)); - break; - case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.ItemUnavailable)); - break; - case BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.DeveloperError)); - break; - case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.AlreadyOwned)); - break; - case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.NotOwned)); - break; - default: - tcsPurchase.SetException(new InAppBillingPurchaseException(PurchaseError.GeneralError, responseCode.ToString())); - break; + case 0: + //Reponse returned OK + var purchaseData = data.GetStringExtra(RESPONSE_IAP_DATA); + var dataSignature = data.GetStringExtra(RESPONSE_IAP_DATA_SIGNATURE); + + tcsPurchase?.TrySetResult(new PurchaseResponse + { + PurchaseData = purchaseData, DataSignature = dataSignature + }); + break; + case RESPONSE_CODE_RESULT_USER_CANCELED: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.UserCancelled)); + break; + case RESPONSE_CODE_RESULT_SERVICE_UNAVAILABLE: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.ServiceUnavailable)); + break; + case BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.ItemUnavailable)); + break; + case BILLING_RESPONSE_RESULT_DEVELOPER_ERROR: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.DeveloperError)); + break; + case BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.AlreadyOwned)); + break; + case BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED: + tcsPurchase?.TrySetException(new InAppBillingPurchaseException(PurchaseError.NotOwned)); + break; + default: + tcsPurchase?.TrySetException( + new InAppBillingPurchaseException(PurchaseError.GeneralError, responseCode.ToString())); + break; } }