Google Play License Checking

824 views
Skip to first unread message

Tim O'Brien

unread,
Jun 24, 2013, 3:17:59 PM6/24/13
to pl...@googlegroups.com
Hi Michael,

I'm having an issue with Google Play Licensing and PlayN.  I've used it before and started with that working code base.  

Normally, I create a Handler in the android activity and thread off the call to create the license checker. I think the issue is handler making a callback when the GL thread has already started.  Any idea?

        public void main(){
                mHandler = new Handler();
new Thread(new Runnable() {    
public void run() {  CreateLicenseCheck();  } }).start();
                PlayN.run(game);
        }

public void CreateLicenseCheck() {
//License Check
String deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);  
        
mLicenseCheckerCallback = new KyghtLicenceChecker();        
// Construct the LicenseChecker with a Policy.
mChecker = new LicenseChecker( this, new ServerManagedPolicy(this, 
new AESObfuscator(SALT, getPackageName(), deviceId)), BASE64_PUBLIC_KEY);
mChecker.checkAccess(mLicenseCheckerCallback);
       }


private class KyghtLicenceChecker implements LicenseCheckerCallback {

   private void displayResult(final String result, final boolean allowusage) {
       mHandler.post(new Runnable() {
           public void run() {
            mLicenseValid = allowusage;
               if (!allowusage) {
                LicenseNotValid(result);                                
               }
           }
       });
   }


I created a LicenseServer interface and passed it to my game to make calls on and stopped using mHandler and tried to use a callback pass in from the game and I got the following error. 


06-24 15:05:17.435 E/AndroidRuntime(19246): FATAL EXCEPTION: background thread
06-24 15:05:17.435 E/AndroidRuntime(19246): java.lang.NullPointerException
06-24 15:05:17.435 E/AndroidRuntime(19246): at kyght.mygame.android.ShowcaseActivity$KyghtLicenceChecker.displayResult(ShowcaseActivity.java:100)
06-24 15:05:17.435 E/AndroidRuntime(19246): at kyght.mygame.android.ShowcaseActivity$KyghtLicenceChecker.applicationError(ShowcaseActivity.java:123)
06-24 15:05:17.435 E/AndroidRuntime(19246): at com.google.android.vending.licensing.LicenseValidator.handleApplicationError(LicenseValidator.java:218)
06-24 15:05:17.435 E/AndroidRuntime(19246): at com.google.android.vending.licensing.LicenseValidator.verify(LicenseValidator.java:190)
06-24 15:05:17.435 E/AndroidRuntime(19246): at com.google.android.vending.licensing.LicenseChecker$ResultListener$2.run(LicenseChecker.java:228)
06-24 15:05:17.435 E/AndroidRuntime(19246): at android.os.Handler.handleCallback(Handler.java:587)
06-24 15:05:17.435 E/AndroidRuntime(19246): at android.os.Handler.dispatchMessage(Handler.java:92)
06-24 15:05:17.435 E/AndroidRuntime(19246): at android.os.Looper.loop(Looper.java:123)
06-24 15:05:17.435 E/AndroidRuntime(19246): at android.os.HandlerThread.run(HandlerThread.java:60)
06-24 15:05:22.765 E/ActivityThread(19246): Activity kyght.mygame.android.ShowcaseActivity has leaked ServiceConnection com.google.android.vending.licensing.LicenseChecker@405612d0 that was originally bound here
06-24 15:05:22.765 E/ActivityThread(19246): android.app.ServiceConnectionLeaked: Activity kyght.mygame.android.ShowcaseActivity has leaked ServiceConnection com.google.android.vending.licensing.LicenseChecker@405612d0 that was originally bound here
06-24 15:05:22.765 E/ActivityThread(19246): at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:938)
06-24 15:05:22.765 E/ActivityThread(19246): at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:833)
06-24 15:05:22.765 E/ActivityThread(19246): at android.app.ContextImpl.bindService(ContextImpl.java:927)
06-24 15:05:22.765 E/ActivityThread(19246): at android.content.ContextWrapper.bindService(ContextWrapper.java:347)
06-24 15:05:22.765 E/ActivityThread(19246): at com.google.android.vending.licensing.LicenseChecker.checkAccess(LicenseChecker.java:149)
06-24 15:05:22.765 E/ActivityThread(19246): at kyght.mygame.android.ShowcaseActivity.doLicenseCheck(ShowcaseActivity.java:85)
06-24 15:05:22.765 E/ActivityThread(19246): at kyght.mygame.android.ShowcaseActivity.CreateLicenseCheck(ShowcaseActivity.java:80)
06-24 15:05:22.765 E/ActivityThread(19246): at kyght.mygame.android.ShowcaseActivity$1.run(ShowcaseActivity.java:55)
06-24 15:05:22.765 E/ActivityThread(19246): at java.lang.Thread.run(Thread.java:1019)
06-24 15:13:40.190 E/AndroidRuntime(20822): FATAL EXCEPTION: GLThread 12
06-24 15:13:40.190 E/AndroidRuntime(20822): java.lang.NullPointerException
06-24 15:13:40.190 E/AndroidRuntime(20822): at playn.android.GameLoop.start(GameLoop.java:50)
06-24 15:13:40.190 E/AndroidRuntime(20822): at playn.android.GameViewGL$3.run(GameViewGL.java:98)
06-24 15:13:40.190 E/AndroidRuntime(20822): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1346)
06-24 15:13:40.190 E/AndroidRuntime(20822): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1138)
06-24 15:20:52.810 E/AndroidRuntime(21719): FATAL EXCEPTION: GLThread 12
06-24 15:20:52.810 E/AndroidRuntime(21719): java.lang.NullPointerException
06-24 15:20:52.810 E/AndroidRuntime(21719): at playn.android.GameLoop.start(GameLoop.java:50)
06-24 15:20:52.810 E/AndroidRuntime(21719): at playn.android.GameViewGL$3.run(GameViewGL.java:98)
06-24 15:20:52.810 E/AndroidRuntime(21719): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1346)
06-24 15:20:52.810 E/AndroidRuntime(21719): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1138)
06-24 15:33:30.400 E/AndroidRuntime(23016): FATAL EXCEPTION: GLThread 12
06-24 15:33:30.400 E/AndroidRuntime(23016): java.lang.NullPointerException
06-24 15:33:30.400 E/AndroidRuntime(23016): at playn.android.GameLoop.start(GameLoop.java:50)
06-24 15:33:30.400 E/AndroidRuntime(23016): at playn.android.GameViewGL$3.run(GameViewGL.java:98)
06-24 15:33:30.400 E/AndroidRuntime(23016): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1346)
06-24 15:33:30.400 E/AndroidRuntime(23016): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1138)

Daniel Gerson

unread,
Jun 24, 2013, 7:06:37 PM6/24/13
to pl...@googlegroups.com

06-24 15:05:17.435 E/AndroidRuntime(19246): FATAL EXCEPTION: background thread
06-24 15:05:17.435 E/AndroidRuntime(19246): java.lang.NullPointerException
06-24 15:05:17.435 E/AndroidRuntime(19246): at kyght.mygame.android.ShowcaseActivity$KyghtLicenceChecker.displayResult(ShowcaseActivity.java:100)
06-24 15:05:17.435 E/AndroidRuntime(19246): at kyght.mygame.android.ShowcaseActivity$KyghtLicenceChecker.applicationError(ShowcaseActivity.java:123)
06-24 15:05:17.435 E/AndroidRuntime(19246): at com.google.android.vending.licensing.LicenseValidator.handleApplicationError(LicenseValidator.java:218)
06-24 15:05:17.435 E/AndroidRuntime(19246): at com.google.android.vending.licensing.LicenseValidator.verify(LicenseValidator.java:190)
06-24 15:05:17.435 E/AndroidRuntime(19246): at com.google.android.vending.licensing.LicenseChecker$ResultListener$2.run(LicenseChecker.java:228)


Not that I've had any experience with this. Not as far along as you are... but take a look at the LicenseValidator.java  
https://code.google.com/p/marketlicensing/source/browse/library/src/com/android/vending/licensing/LicenseValidator.java

The version I'm looking at is probably a little different to yours, as there isn't anything specific on line 190, but the handleApplicationError(...) call suggests you haven't set it up correctly.
If I were you, I'd breakpoint this file to see where and how it fails.

My two cents from the data presented suggests it's more likely to do with setup, than concurrency... of course I could be wrong.

Good luck :-)
DMG

Tim O'Brien

unread,
Jun 24, 2013, 7:20:34 PM6/24/13
to pl...@googlegroups.com

Thanks Daniel,

Your right about the error occuring in my handling code, but I think that is a result of a call from my GL thread to Google code with callback to Android created class and then calling back into GL thread.  The usual android handler seems to be interferring with the GL thread, might be something to do with the default looper. 

I'm sure Michael has already had to deal with it in SpellBound. I just took his pom profile for signed apk's but didn't see any other threads on the subject. This is the last piece of the puzzle. 

Tim

--
 
---
You received this message because you are subscribed to a topic in the Google Groups "PlayN" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/playn/ctbQzF0RUGc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to playn+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Daniel Gerson

unread,
Jun 24, 2013, 7:37:59 PM6/24/13
to pl...@googlegroups.com
Aah... I was obviously lazy looking all the way down the stacktrace.
Just an observation. GameLoop.java doesn't appear to be in 1.7.1 or later from a quick search.
Are you using 1.6? Might be worth testing to see if the later versions of PlayN solve your problems.

DMG

Daniel Gerson

unread,
Jun 24, 2013, 7:49:05 PM6/24/13
to pl...@googlegroups.com

06-24 15:05:22.765 E/ActivityThread(19246): Activity kyght.mygame.android.ShowcaseActivity has leaked ServiceConnection com.google.android.vending.licensing.LicenseChecker@405612d0 that was originally bound here
06-24 15:05:22.765 E/ActivityThread(19246): android.app.ServiceConnectionLeaked: Activity kyght.mygame.android.ShowcaseActivity has leaked ServiceConnection com.google.android.vending.licensing.LicenseChecker@405612d0 that was originally bound here
06-24 15:05:22.765 E/ActivityThread(19246): at android.app.LoadedApk$ServiceDispatcher.<init>(LoadedApk.java:938)
06-24 15:05:22.765 E/ActivityThread(19246): at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:833)
06-24 15:05:22.765 E/ActivityThread(19246): at android.app.ContextImpl.bindService(ContextImpl.java:927)
06-24 15:05:22.765 E/ActivityThread(19246): at android.content.ContextWrapper.bindService(ContextWrapper.java:347)
06-24 15:05:22.765 E/ActivityThread(19246): at com.google.android.vending.licensing.LicenseChecker.checkAccess(LicenseChecker.java:149)
06-24 15:05:22.765 E/ActivityThread(19246): at kyght.mygame.android.ShowcaseActivity.doLicenseCheck(ShowcaseActivity.java:85)
06-24 15:05:22.765 E/ActivityThread(19246): at kyght.mygame.android.ShowcaseActivity.CreateLicenseCheck(ShowcaseActivity.java:80)
06-24 15:05:22.765 E/ActivityThread(19246): at kyght.mygame.android.ShowcaseActivity$1.run(ShowcaseActivity.java:55)

Another lead that might be worth following is the ServiceConnectionLeaked exception.

http://stackoverflow.com/questions/12308400/what-does-it-mean-to-leak-a-service-connection
Seems it has something to do with the Activity lifecycle... all those things seem to require special care... :-/

Anyway, you're probably right that Michael will be able to help. :-)

I'd still wager that the GL errors themselves are a red-herring. 

DMG

Tim O'Brien

unread,
Jun 24, 2013, 10:06:33 PM6/24/13
to pl...@googlegroups.com

Thanks Daniel,

Yes, I'm still using 1.6. Waiting until after version one of game to move to 1.7.  I have to fix all my box 2d calls to work with native box and I don't really want to get into it yet.

I will double check my calls. Maybe your right and the activity state or lifecycle is messed up.

Tim

--

Michael Bayne

unread,
Jun 25, 2013, 9:34:59 AM6/25/13
to pl...@googlegroups.com
On Mon, Jun 24, 2013 at 12:17 PM, Tim O'Brien <obrie...@gmail.com> wrote:
I'm having an issue with Google Play Licensing and PlayN.  I've used it before and started with that working code base. 

I have not used  the Google Play Licensing stuff, so this is all just based on general proper practice.
 
        public void main(){
                mHandler = new Handler();
new Thread(new Runnable() {    
public void run() {  CreateLicenseCheck();  } }).start();
                PlayN.run(game);
        }

You shouldn't use "new Thread(new Runnable() ..." to do async stuff. It will work, but a better approach is to use:

PlayN.invokeAsync(new Runnable() { ... });

which will use AsyncTask, which is recommended on Android for asynchronous tasks.

   private void displayResult(final String result, final boolean allowusage) {
       mHandler.post(new Runnable() {
           public void run() {
            mLicenseValid = allowusage;
               if (!allowusage) {
                LicenseNotValid(result);                                
               }
           }
       });
   }

Assuming LicenseNotValid is some PlayN code, then you should not be using mHandler.post() to invoke this code, you should use:

PlayN.invokeLater(new Runnable() { ... });

which will ensure the code is run on the PlayN thread.

-- m...@samskivert.com

Michael Bayne

unread,
Jun 25, 2013, 9:35:56 AM6/25/13
to pl...@googlegroups.com

On Mon, Jun 24, 2013 at 7:06 PM, Tim O'Brien <obrie...@gmail.com> wrote:

Yes, I'm still using 1.6. Waiting until after version one of game to move to 1.7.  I have to fix all my box 2d calls to work with native box and I don't really want to get into it yet.

PlayN 1.7 does not use native box2d. It does use the latest version of box2d, which has some API changes, but it's still just the pure-Java box2d.

-- m...@samskivert.com

Daniel Gerson

unread,
Jun 25, 2013, 9:41:45 AM6/25/13
to pl...@googlegroups.com

Just out of curiosity, what's the speed differential between native box2d and the java version? Tim, have you done any tests? Is Libgdx onto something with their approach, or are the gains marginal? 

Michael Bayne

unread,
Jun 25, 2013, 9:45:15 AM6/25/13
to pl...@googlegroups.com
On Tue, Jun 25, 2013 at 6:41 AM, Daniel Gerson <daniel...@gmail.com> wrote:
Just out of curiosity, what's the speed differential between native box2d and the java version? Tim, have you done any tests? Is Libgdx onto something with their approach, or are the gains marginal?

I have not done any scientific tests, but the native box2d appears to be modestly faster on Android and about the same or slower on iOS, because emulating JNI via MonoTouch makes every native call quite slow. We looked into ways to reduce the number of JNI calls, but it's difficult due to the way that box2d is designed (it requires a JNI call from native to managed code for every contact callback).
 
-- m...@samskivert.com

Tim O'Brien

unread,
Jun 25, 2013, 10:05:40 AM6/25/13
to pl...@googlegroups.com
I believe it working now, I will verify first and post my solution.

Tim O'Brien

unread,
Jun 25, 2013, 10:31:12 AM6/25/13
to pl...@googlegroups.com
Ok, I finally got it working by overriding the OnCreate method and initializing the Licence checking stuff there. 

Inside main() I pass the game a reference to the LicenseServer, the game makes calls to CheckLicense and disables game actions.  I have attached the code below, I didn't clean it up yet.


//***************** ANDROID CLASS ******************
public class GameActivity extends GameActivity {

//Vars for licensing
private static final String BASE64_PUBLIC_KEY = "..."
private static final byte[] SALT = new byte[] { ... };

private LicenseCheckerCallback mLicenseCheckerCallback;
private LicenseChecker mChecker;
private boolean mLicenseValid = false;
private Callback<Boolean> GameCallback;
private GameLicense LicenseServ;
private MyGame game;
private Handler mHandler;
private Context MyActivity;
private String packageName;
private String deviceId;


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

LicenseServ = new GameLicense();
mHandler = new Handler();
MyActivity = this;
packageName = getPackageName();
deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
CreateLicenseCheck();
}

  @Override
  public void main(){
try {
   game = new MyGame();
 game.setLicenseServer(LicenseServ);
   PlayN.run(game);

} catch(Exception err) {
PlayN.log().error("StartUp", err);
this.finish();
}

  }

public void CreateLicenseCheck() {
try
{
mLicenseCheckerCallback = new KyghtLicenceChecker();

ServerManagedPolicy policy = new ServerManagedPolicy(MyActivity, new AESObfuscator(SALT, packageName, deviceId));

mChecker = new LicenseChecker( MyActivity, policy, BASE64_PUBLIC_KEY);

}catch(Exception err) {
PlayN.log().error("License Check Error", err);
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if (mChecker!=null) mChecker.onDestroy();
}

public void doLicenseCheck() {
mChecker.checkAccess(mLicenseCheckerCallback);
}

//Game License Server
public class GameLicense implements LicenseServer {

@Override
public void CheckLicense(final Callback<Boolean> handler) {
mHandler.post(new Runnable() {
@Override
public void run() {
GameCallback = handler;
if (mLicenseCheckerCallback!=null)
doLicenseCheck();
else
GameCallback.onFailure(new Exception("License Service not available yet"));
}});
}
}

private class KyghtLicenceChecker implements LicenseCheckerCallback {
   private void displayResult(final String result, final boolean allowusage) {
    try{
       mHandler.post(new Runnable() {
           public void run() {
            if (GameCallback!=null) {
            if (allowusage)
            GameCallback.onSuccess(allowusage);
            else {
            if (result == null) {
            //We need to retry here
            GameCallback.onSuccess(allowusage);
            } else {
            //Total Failure
            GameCallback.onFailure(new Exception(result));
            }
            }
            }
           }
       });

    } catch(Exception err) {
    PlayN.log().error("Callback Error", err);
    }
   }

public void allow(int reason) {
displayResult("", true);
}

public void dontAllow(int reason) {
if (reason == Policy.RETRY)
displayResult(null, false); //must retry later
else {
String errortext = "Unknown";
if (reason == Policy.NOT_LICENSED) errortext = "Invalid License";
displayResult(errortext, false);
}
}

public void applicationError(int errorCode) {
PlayN.log().error("License App Error " + errorCode);
String errortext = "Unknown";
switch(errorCode) {
case ERROR_NOT_MARKET_MANAGED:
errortext = "Not Market Game";
break;
case ERROR_INVALID_PACKAGE_NAME:
errortext = "Invalid Game Package";
break;
case ERROR_INVALID_PUBLIC_KEY:
errortext = "Invalid Public Key";
break;
case ERROR_MISSING_PERMISSION:
errortext = "Invalid Permission";
break;
case ERROR_NON_MATCHING_UID:
errortext = "Invalid UID Match";
break;
case ERROR_CHECK_IN_PROGRESS:
errortext = "Invalid CheckIn";
break;
}
displayResult(errortext, false);
}
}
}



//************ INSIDE GAME CLASS **************
  public void DoLicenseCheck() {
//Did we check the license
if (licenseserv!=null && !licchecked) {
ShowMessage("Checking License");
licenseserv.CheckLicense(new Callback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
if (result) {
ShowMessage("Valid License");
licchecked = true;
} else {
ShowMessage("Retry Later");
}
_isValidLicense = result;
}

@Override
public void onFailure(Throwable cause) {
ShowMessage(cause.getMessage());
_isValidLicense = false;
}

});
}
  }
  
  
  
//************* ANDROID MANIFEST PERMISSIONS *************


<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />


Tim
Reply all
Reply to author
Forward
0 new messages