Skip to content
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

Crash with null pointer exception after Billing Service gets disconnected #45

Closed
dec1 opened this issue Dec 6, 2016 · 24 comments
Closed

Comments

@dec1
Copy link

dec1 commented Dec 6, 2016

After successful setup and the establishment of a connection to the Billing Service in

IabHelper.startSetup()

The user tries to make an in-app purchase.

IabHelper.launchPurchaseFlow()

gets called which in turn calls methods on mService. But sometimes

IabHelper.onServiceDisconnected()

has been called in the meantime which sets

IabHelper.mService = null

This causes a null pointer exception.

It seems unpredictable when/why this disconnection happens. But I have some pre-launch reports in my developer console as examples.

Would be resolved by:
#46

@dec1 dec1 changed the title Billing Service gets disconnected causing null pointer exception (IabHelper.mService is null)) Crash with null pointer exception after Billing Service gets disconnects Dec 7, 2016
@dec1 dec1 changed the title Crash with null pointer exception after Billing Service gets disconnects Crash with null pointer exception after Billing Service gets disconnected Dec 7, 2016
@netomarin
Copy link

Hi @dec1

The disconnection, in general, happens due to background operations performed by Play Store app.
The correct approach should be by implementing a retry police.

I'll address this problem internally, and get back to you when we have an ETA about the fix.

Thanks

@pox822
Copy link

pox822 commented Jul 4, 2017

Hi @netomarin

Any news about this issue?

It impacts buyers to sometimes crash on their purchase.

@afehners
Copy link

I'm seeing the same issue. Any fix coming or recommended workaround?

@netomarin
Copy link

@pox822 @afehners do you have a stacktrace or any log to help?

We are investigating this bug now: No response code in PURCHASES_UPDATED Bundle

This error is generating a NullPointerException when checking the result and maybe it's disconnecting the client. I'll get back with some news ASAP.

Thanks
Neto

@pox822
Copy link

pox822 commented Aug 2, 2017

java.lang.NullPointerException:
at com.cuizconv.billingutil.IabHelper.launchPurchaseFlow (IabHelper.java:454)
at com.cuizconv.billingutil.IabHelper.launchPurchaseFlow (IabHelper.java:399)
at com.cuizconv.MainActivity.purchaseNoAds (MainActivity.java:210)
at com.cuizconv.MainActivity.onOptionsItemSelected (MainActivity.java:224)
at android.app.Activity.onMenuItemSelected (Activity.java:2983)
at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected (PhoneWindow.java:1162)
at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected (MenuBuilder.java:761)
at com.android.internal.view.menu.MenuItemImpl.invoke (MenuItemImpl.java:152)
at com.android.internal.view.menu.MenuBuilder.performItemAction (MenuBuilder.java:904)
at com.android.internal.view.menu.MenuBuilder.performItemAction (MenuBuilder.java:894)
at android.widget.ActionMenuView.invokeItem (ActionMenuView.java:595)
at com.android.internal.view.menu.ActionMenuItemView.onClick (ActionMenuItemView.java:142)
at android.view.View.performClick (View.java:4781)
at android.view.View$PerformClick.run (View.java:19907)
at android.os.Handler.handleCallback (Handler.java:739)
at android.os.Handler.dispatchMessage (Handler.java:95)
at android.os.Looper.loop (Looper.java:160)
at android.app.ActivityThread.main (ActivityThread.java:5541)
at java.lang.reflect.Method.invoke (Native Method)
at java.lang.reflect.Method.invoke (Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:964)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:759)

@afehners
Copy link

afehners commented Aug 2, 2017

I'm not sure my stack trace will help since I've made a few fixes to the sample IabHelper to make it more robust.

Fatal Exception: java.lang.NullPointerException: Attempt to invoke interface method 'android.os.Bundle com.a.a.a.a.a(int, java.lang.String, java.lang.String, java.lang.String)' on a null object reference
at com.fehnerssoftware.billingutils.IabHelper.queryPurchases(IabHelper.java:843)
at com.fehnerssoftware.billingutils.IabHelper.queryInventory(IabHelper.java:545)
at com.fehnerssoftware.billingutils.IabHelper.queryInventory(IabHelper.java:523)
at com.fehnerssoftware.billingutils.IabHelper$2.run(IabHelper.java:618)
at java.lang.Thread.run(Thread.java:762)

The failing line of code is the last line of this block

int queryPurchases(Inventory inv, String itemType) throws JSONException, RemoteException  {
        // Query purchases
        logDebug("Querying owned items, item type: " + itemType);
        logDebug("Package name: " + mContext.getPackageName());
        boolean verificationFailed = false;
        String continueToken = null;

        do {
            logDebug("Calling getPurchases with continuation token: " + continueToken);
            Bundle ownedItems = mService.getPurchases(3, mContext.getPackageName(),
                    itemType, `continueToken); <----- mService is null here

The logs show the billing service gets disconnected after a while and the crash comes when trying to query inventory:

328 | 14:58:18:226 (UTC) | D/IabHelper Billing service disconnected.
333 | 14:58:18:388 (UTC) | D/IabHelper Starting async operation: refresh inventory

To fix it in my copy I simply added to checkNotDisposed() a check that if mService is null and mSetupDone is true it throws an illegal state exception. I catch this in my code and setup IabHelper again and retry.

@Plastix
Copy link

Plastix commented Aug 3, 2017

I'm getting the same crash as @aliafshar (with a slightly modified IabHelper as well).

@aliafshar: A few questions about your fix. Don't you have to call dispose on the IabHelper instance before you call setup again? How do you ensure that you don't use the instance in the interim while it is reconnecting to the billing service?

Overall, I think that your fix will solve my crash. Thank you so much for mentioning it here!

@yccheok
Copy link

yccheok commented Aug 23, 2017

This is my error message.

java.lang.NullPointerException: 
  at org.yccheok.jstock.billing.IabHelper.querySkuDetails (IabHelper.java:1047)
  at org.yccheok.jstock.billing.IabHelper.queryInventory (IabHelper.java:630)
  at org.yccheok.jstock.billing.IabHelper$2.run (IabHelper.java:698)
  at java.lang.Thread.run (Thread.java:818)

Hope it get fixed soon.

@elmath
Copy link

elmath commented Aug 23, 2017

I have exactly the same problem as @afehners (mService is null on line 945). And based on Crashlytics, the problems occurs a lot. Unfortunately I haven't been able to reproduce it on my test devices. Almost all the time, the problem occurs while the app is not in focus (running as a foreground service).

@gorgon
Copy link
Contributor

gorgon commented Aug 23, 2017

If you want to reproduce this use case:

  • launch your app
  • return to Home
  • move Play Store shortcut to "App info" section in the top
  • Click "Force Stop"

Once you return back to your app, service should be disconnected from the Play Store. So any operation on it will throw NPE.

And the best way to solve this issue is by implementing retry-policy specific to your needs as Neto mentioned above.
We did showcased the most basic one inside the updated TrivialDrive_v2 sample.
So feel free to reuse that approach.

@yccheok
Copy link

yccheok commented Sep 12, 2017

@netomarin

Hi, may I know any ETA for this issues fixed?

Time-to-time, we will get crash report from developer console. We find it is difficult to fix the problem, without guidance from Google team.

Thanks.
Cheok

@netomarin
Copy link

Hi @yccheok and all developers in this thread

This problem is caused because your BillingClient is not maintaining the connection status.
To avoid this problem you need to implement your own connection retry-police.

Override the onBillingServiceDisconnected() callback method and implement your own retry policy to handle lost connections to Google Play in the event the client loses connection.

The Play Billing Library client must call the startConnection() method to restart the connection before making further requests.

As mentioned by @gorgon, you can check the open-sourced sample Trivial Drive v2 and use the same approach.

I'm closing this issue, since it's not an issue related to the sample.
If you have a bug report to the library, please fill a bug in our public bug tracker.

Thanks,
Neto

@yccheok
Copy link

yccheok commented Sep 12, 2017

@netomarin

Thanks. I didn't notice that TrivialDrive had been updated using recent https://developer.android.com/google/play/billing/billing_library.html

Guess we need to refactor our previous code which is using old IabHelper, and move it to https://developer.android.com/google/play/billing/billing_library.html

@d-schmidt
Copy link

The "bug" @gorgon and @netomarin missed and failed to mention directly is: IabHelper is obsolete und should no longer be used. Their advise is only valid for the new billingclient.

@dec1
Copy link
Author

dec1 commented Oct 17, 2017

IabHelper is obsolete

Does it say this somewhere clearly? Presumably it should.
Its amazing how confusing the documentation is.

billing_integrate.html still describes using the old IabHelper and IInAppBillingService.aidl

Whereas for example
billling_library.html describes using the new BillingClient.

The github example both reference, contains both a "TrivialDrive" and "TrivialDrive_v2" subdir, each of which simply describe themselves as

Sample for In-App Billing version 3

Even though billing/versions.html shows version 5 to be current since Feb 2015
TrivialDrive_v2 doesn't state how/why it differs from TrivialDrive, and why TrivialDrive is still there at all (is TrivialDrive obsolete? Are there valid reasons to still use TrivialDrive? Is TrivialDrive_v2 not finished yet?)

And this on top of how hard it is to get the sample working anyways (setting up your own in-app products in developer console, modifying the app to reflect this, only working with signed release etc). A bit more clarity here could save many 1000s of developers many frustrating hours

@d-schmidt
Copy link

billling_library.html feels unfinished as well.

Why does the chapter 'Retrieve existing purchases' describe and show 'Google Play calls the onPurchasesUpdated() method to deliver the result of the purchase operation'? How is this relevant? Is it called on service connect?
Only in the last sentence it states: 'You can also query Google Play to quickly retrieve the list of purchases and subscriptions that the user made.' It fails to mention how.

Three chapters down we learn you should use queryPurchases() and could use queryPurchaseHistoryAsync() but neither triggers above callback.
The difference between these two methods is also not explained in depth. The first uses the local Play Service cache, the second does some kind of net call but is only able to retrieve the very last transaction. When is the cache refreshed? Is there a use case for the seconds method? Is the first unsafe?

I tried to re-implement billing because of the NPEs. I hate trial and error, and not just the annoying, impossible to really test billing. The docs suck. I'm irritated.

@Aperico-com
Copy link

@netomarin why do you close the issue when there still is a big problem with non reproducible NPEs to production builds? Following the documentation for using IABHelper to a tee still cause these problems, but you think its is OK to close the issue? Can I at least get an answer to if IABHelper is valid to use? Is the BillingClient approach what we are supposed to use now? If so why do you not give that info anywhere? The only thing I can do now is catch the internal exceptions to prevent crashes, getting the purchase flow to work... just forget it, at least using IABHelper.

@netomarin
Copy link

@dec1 the page billing_integrate.html is a "generic" guide, provided for developers that doesn't want to use Play Billing Library, or can't use due to a platform incompatibility (like developing using NDK or C++). So, we decided to keep this page with generic instructions about how to use AIDL interface.
And you can still using the AIDL interface if you want, it's not deprecated. So, we recommend to use the new Play Billing Library, but you are not obligated to and we need to keep the instructions about how to do it. It's not confusing or ambiguous, they are different guides and we explain this at the top of the page:

"Note: This guide provides general instructions to help you implement the In-app Billing API in your app. The Play Billing Library provides a wrapper around the In-app Billing API and is designed to help you when integrating the API. To see a complete implementation using the library, visit the Play Billing Library training class. The training class provides a complete sample In-app Billing app, which demonstrates how to start a purchase flow, handle purchase result, list your products, consume an item, and much more."

@dec1 about the samples descriptions, you are right, we will improve it ASAP. Thanks.
Trivial Drive v1 still on GitHub because, as I mentioned earlier, it demonstrates how to use the AIDL interface instead of Play Billing Library.
Trivial Drive v2 is the sample for Play Billing Library implementation, and is finished. But, we will keep improving and adding new features as they are launched on Play Billing Library, we are actively improving it.
Since the sample is a reference implementation, and in-app billing API have many configurations required to assure the platform security, the sample requires additional steps to have it running in you account. These are steps required by Google Play for your own security.

@d-schmidt Play Billing Library guide is finished and up-to-date to our current development state. If the documentation is missing something or you feel that is not correct, please open a bug in our public bug tracker. This channel is for issues related to the TrivialDrive samples.

@d-schmidt we mention the onPurchasesUpdated method because it's called when a purchase is updated, even it's not started from the current device, so it's important to know how to handle it to keep you inventory up to date.

@d-schmidt Google Play's cache has it owns update policy, but it happens on a very short amount of time and it's totally safe. And other difference between queryPurchases() and queryPurchaseHistoryAsync() is that queryPurchasesHistoryAsync returns the most recent purchase made by the user for each SKU, even if that purchase is expired, canceled, or consumed, and queryPurchases() only the valid managed purchases.
As mentioned on documentation, we recommend to use the queryPurchases(), because it's enough for almost all cases:

"Use the queryPurchases() method whenever possible as it uses the local cache, in preference to the queryPurchaseHistoryAsync() method."

@Aperico-com The issue is closed because it's not a issue caused by the sample code. The samples are fine and running, and if you are facing a NPE in your modified code, as we mentioned earlier, you need to check the connection status and implement your own retry policy.

Hope I could address all open questions.
Thanks

@d-schmidt
Copy link

d-schmidt commented Oct 19, 2017

@netomarin

  1. Thanks for the clarifications. Much appreciated. I still think the title of the introduction chapter 'Retrieve existing purchases' doesn't fit the content. I will create a ticket in the appropriate tracker.

  2. The NPE mentioned in the first post is still in the Trivaldrive v1 example.
    This line sets mService = null without any checks or flags: IabHelper.java#L227
    I call queryInventoryAsync(listener) for an NPE here: IabHelper.java#L945. A similar NPE happens in IabHelper.java#L453.

@Aperico-com
Copy link

@netomarin I implemented a reconnect policy. But my question and the original question was about IABHelper and your online docs for it, you keep referencing to Billing Client, that is why I ask again, should devs not use IABHelper anymore, is it obsolete and if so why does the docs not say that?

@dec1
Copy link
Author

dec1 commented Feb 6, 2018

@netomarin
@d-schmidt

@dec1 the page billing_integrate.html is a "generic" guide, provided for developers that doesn't want to use Play Billing Library, or can't use due to a platform incompatibility (like developing using NDK or C++).

If by "generic" you mean a "low level confusing and buggy example", then I suppose you're right.
When I hear the term generic I generally think "high level clear explanation and of the main ideas with a helpful example"

I didn't know there was an api NDK/C++ developers could use for in-app billing? Can you point me to the documentation? And if a developer did want to do so using C++, did they could just use JNI to call the corresponding methods of the newer less buggy JAVA classes, just as easily as the could the older JAVA example.

@netomarin
Copy link

@Aperico-com Our recommendation is to use the Play Billing Library. We have no more plans to update the Trivial Drive v1, that uses the IABHelper.
Our docs are no more referring to the v1, just to the new version.

@dec1 I'm sorry if you feel that way, but the Trivial Drive v2 covers how to use Play Billing Library, including how to list products, retrieve purchases and start flows for in-app purchases and subscriptions.
On our docs you can find an explanation about the API methods, how in-app billing works and all other details.
When you say buggy, please could you point a bug that still blocking you to use Google Play Billing?
We want to help all developers to implement in-app billing easily.

For C/C++ you can use the AIDL interface to communicate with the Google Play service, similar to what you can do for any service. For all methods on AIDL service, please check In-app Billing Reference. If you want to see the AIDL file, please check IInAppBillingService.aidl.

And for a better support for C++ developers, we are already working on a C++ library and sample, and I hope we will have news soon.

Also, we are working on a Kotlin version for Trivial Drive v2.

@yccheok
Copy link

yccheok commented Feb 8, 2018

Recently, we refactor our code, from IabHelper to Play Billing Library.

The outcome is that, we see much less crash report related to billing.

Thank you for providing Play Billing Library.

@ifdhotqq
Copy link

@netomarin "please could you point a bug that still blocking you to use Google Play Billing?" - Sure, how about the bug where queryPurchases() returns an empty list, but any attempt to purchase an IAP returns an error code 7 - item already in inventory. That is a pretty big bug with billing library. Sure, it only seems to happen for my users who had already purchased items with the old IabHelper system before I upgraded to billing library, but because of this bug I had to go back to the old IabHelper system. Worst, there are many people who have been having this bug for months according to your issue tracker. Do you even look at the issue tracker that you have pointed us to?

"We want to help all developers to implement in-app billing easily." - You're joking, right? Google's actions seem to indicate the exact opposite of this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests