Asset - Extension Google Play Licensing: modification

Discussion in 'Marketplace' started by Andrey, Sep 28, 2018.

  1. Andrey

    Andrey Member

    Joined:
    Jun 21, 2016
    Posts:
    281
    Hello!
    The Marketplace has an extension from YoYo Google Play Licensing. However, it has one significant limitation — interruption of the game in case of problems with the license.
    Because of the interruption, we have trouble getting into the Designed for Families program.
    Also, the extension does not give any choice in actions. It's inflexible.
     
    Last edited: Oct 16, 2019 at 5:33 PM
  2. Andrey

    Andrey Member

    Joined:
    Jun 21, 2016
    Posts:
    281
    [!] This modification allows you to abandon the dialog box and get through GML server response (or from the cache), when you want it yourself.

    The function GM_LicenseCheck_Init(1);
    installed in the game makes a request and receives a response asynchronously:

    - the time of the request;
    - response code;
    - error code (if any).



    1. To install the modification you need to replace all the contents of the file ../extensions/GooglePlayLicensingAsExt/AndroidSource/Java/GooglePlayLicensingAsExt.java
    on this code:
    Code:
    package ${YYAndroidPackageName};
    
    import ${YYAndroidPackageName}.R;
    import ${YYAndroidPackageName}.RunnerActivity;
    import com.yoyogames.runner.RunnerJNILib;
    
    import android.util.Log;
    import android.os.Handler;
    
    import com.google.android.vending.licensing.LicenseChecker;
    import com.google.android.vending.licensing.LicenseCheckerCallback;
    import com.google.android.vending.licensing.Obfuscator;
    import com.google.android.vending.licensing.ServerManagedPolicy;
    import com.google.android.vending.licensing.AESObfuscator;
    import com.google.android.vending.licensing.Policy;
    
    import android.net.Uri;
    import android.provider.Settings.Secure;
    import android.app.AlertDialog;
    import android.app.Dialog;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    
    public class GooglePlayLicensingAsExt extends RunnerSocial
    {
    
        final int EVENT_OTHER_SOCIAL = 70;
        final int GM_LICENSECHECK_INIT = 1000;
        final int GM_LICENSECHECK_REASON = 2000;
        final int GM_LICENSECHECK_ERROR = 3000;
        private int GM_licensecheck_start = 0;
     
     
        // License checking
        private LicenseCheckerCallback mLicenseCheckerCallback;
        private LicenseChecker mChecker; 
        private Handler mLicenseHandler = new Handler();
     
     
     
        private class MyLicenseCheckerCallback implements LicenseCheckerCallback
        {
            public void allow(int reason)
            {
             
                if (RunnerActivity.CurrentActivity.isFinishing()) {
                    // Don't update UI if Activity is finishing.
                    return;
                }
                Log.i("yoyo", "!!!!##### Successful license check #####!!!!!! ");
             
                GM_LicenseCheck_Reason(reason);
            }
    
         
         
            public void dontAllow(int reason)
            {
                if (RunnerActivity.CurrentActivity.isFinishing()) {
                    // Don't update UI if Activity is finishing.
                    return;
                  }
               
                Log.i("yoyo", "!!!!##### failed license check reason=" + reason + " #####!!!!!! ");
             
                GM_LicenseCheck_Reason(reason);
             
            }
         
         
            public void applicationError( int _error )
            {
                // log the error
                Log.i("yoyo", "License Error - " + _error);
             
                // then call dontAllow
                  dontAllow(0);
             
                GM_LicenseCheck_Error(_error);
            }
        }
     
    
    
     
     
        @Override
        public void Init()
        {
            Log.i("yoyo", "Google Play Licensing extension initialising" );
        }
     
     
     
        /* Checks the security for the application */
        public void checkLicensing()
        {
            if(GM_licensecheck_start == 1)
            {
                if(RunnerActivity.CurrentActivity.checkCallingOrSelfPermission("com.android.vending.CHECK_LICENSE")==PackageManager.PERMISSION_GRANTED)
                {
                    mLicenseCheckerCallback = new MyLicenseCheckerCallback();
                    String deviceId = Secure.getString(RunnerActivity.CurrentActivity.getContentResolver(), Secure.ANDROID_ID);
                    Log.i("yoyo", "deviceId = "+deviceId);
                    ServerManagedPolicy policy = new ServerManagedPolicy( RunnerActivity.CurrentActivity, new AESObfuscator( RunnerActivity.CurrentActivity.SALT, RunnerActivity.CurrentActivity.getPackageName(), deviceId));
                 
                    if( RunnerActivity.BASE64_PUBLIC_KEY == null || RunnerActivity.BASE64_PUBLIC_KEY == "")
                    {
                        Log.i("yoyo", "Invalid license key found");
                    }
                    try
                    {
                        mChecker = new LicenseChecker( RunnerActivity.CurrentActivity, policy, RunnerActivity.BASE64_PUBLIC_KEY);
                        mChecker.checkAccess( mLicenseCheckerCallback );
                    }
                    catch ( IllegalArgumentException _ex )
                    {
                        Log.i("yoyo", "exception while doing license check! invalid license key????"+ _ex);
                    }
                }
                else
                {
                    Log.i("yoyo", "@@@@@@ Google Licensing permission not set" );
                }
             
                GM_licensecheck_start = 0;
            }
        }
     
     
     
     
     
     
     
    
        // INIT
        public void GM_LicenseCheck_Init(double arg0)
        {
            long GM_licensecheck_time = System.currentTimeMillis();
            if(arg0 == 1)
            {
                arg0 = 0;
                GM_licensecheck_start = 1;
                Log.i("yoyo", "GM_LicenseCheck_Init: YES");
                Log.i("yoyo", "GM_licensecheck_time: " + GM_licensecheck_time);
                         
                int dsMapIndex = RunnerJNILib.jCreateDsMap(null, null, null);
                RunnerJNILib.DsMapAddDouble( dsMapIndex, "type", GM_LICENSECHECK_INIT);
                RunnerJNILib.DsMapAddDouble( dsMapIndex, "time", GM_licensecheck_time);
                RunnerJNILib.CreateAsynEventWithDSMap(dsMapIndex, EVENT_OTHER_SOCIAL);
             
                checkLicensing();
            }
        }
     
        // REASON
        public void GM_LicenseCheck_Reason(double res0)
        {
            Log.i("yoyo", "REASON: " + res0);
         
            int dsMapIndex = RunnerJNILib.jCreateDsMap(null, null, null);
            RunnerJNILib.DsMapAddDouble( dsMapIndex, "type", GM_LICENSECHECK_REASON);
            RunnerJNILib.DsMapAddDouble( dsMapIndex, "reason", res0);
            RunnerJNILib.CreateAsynEventWithDSMap(dsMapIndex, EVENT_OTHER_SOCIAL);
        }
     
        // ERROR
        public void GM_LicenseCheck_Error(double err0)
        {
            Log.i("yoyo", "ERROR: " + err0);
         
            int dsMapIndex = RunnerJNILib.jCreateDsMap(null, null, null);
            RunnerJNILib.DsMapAddDouble( dsMapIndex, "type", GM_LICENSECHECK_ERROR);
            RunnerJNILib.DsMapAddDouble( dsMapIndex, "error", err0);
            RunnerJNILib.CreateAsynEventWithDSMap(dsMapIndex, EVENT_OTHER_SOCIAL);
        }
    
    
     
    
    }
    
    


    2. Next, configure the extension in GMS as in the picture (see attachment file at the bottom of the page):
    [​IMG]


    3. Now we can send a request via the GM_LicenseCheck_Init(1); function whenever we want; and wait for a response:
    Code:
    var type = async_load[? "type"];
    
    switch(type)
    {
       // LICENSED
       case 2000:
           // LICENSED = 256 (LICENSED means that the server returned back a valid license response)
           // LICENSED_OLD_KEY = 256 (LICENSED means that the server returned back a valid license response)
           // NOT_LICENSED = 561 (NOT_LICENSED means that the server returned back a valid license response that indicated that the user definitively is not licensed)
           // RETRY = 291 (RETRY means that the license response was unable to be determined - perhaps as a result of faulty networking)
    
    var licensing_reason = async_load[? "reason"];
    
       break;
     
     
       // ERRORS
       case 3000:
           // ERROR_NOT_MARKET_MANAGED = 3
           // ERROR_SERVER_FAILURE = 4
           // ERROR_OVER_QUOTA = 5
           // ERROR_CONTACTING_SERVER = 257
           // ERROR_INVALID_PACKAGE_NAME = 258
           // ERROR_NON_MATCHING_UID = 259
    
           var licensing_error = async_load[? "error"];
       break;
     
     
       default:
       break;
    }
    
    


    4. Now you can decide what you want to do with this answer.

    5. See implementation example >>>
     

    Attached Files:

    Last edited: Oct 16, 2019 at 5:39 PM
  3. Mert

    Mert Member

    Joined:
    Jul 20, 2016
    Posts:
    388
    Need a magnifier to read this one :D
     
  4. Andrey

    Andrey Member

    Joined:
    Jun 21, 2016
    Posts:
    281
    It's the truth.
    But if there is no magnifying glass, then attached file pictures at the bottom of the post. ;)
     
    Mert likes this.
  5. Adriano_ppaula

    Adriano_ppaula Member

    Joined:
    Jun 22, 2017
    Posts:
    24
    Hello Andrey!
    Is it possible to make such a modification to google play service extension for GM 1.4?

    Currently after the google + API has been disabled, no google play service functionality works any more.
     
  6. Andrey

    Andrey Member

    Joined:
    Jun 21, 2016
    Posts:
    281
    Hello!
    I'm not using 1.4 anymore, so I can't check.
    This modification only removes the interrupt caused by the pop-up window. Instead of an interrupt, a response is sent.
     
  7. Dan

    Dan YoYo Games Staff YYG Staff

    Joined:
    Apr 15, 2016
    Posts:
    339
    We are going to review this change and see if it needs to be included in the extension, as I can see the comment in the 2.2.4 beta thread which says this still blocks Designed For Families submission. In future, please do report the issue to us directly via the ticket system, though - we could probably have fixed this months ago if we'd known about it ;)


    1.4 does not support Android API 26+ anyway, so Google won't accept any updates or new app submissions. You would have to use GMS2 to update that project, using the current versions of Android Studio / Android APIs and the Google Play Services extension at that time.
     
  8. Andrey

    Andrey Member

    Joined:
    Jun 21, 2016
    Posts:
    281
    Thank You, Dan!
    And yes, of course I sent report about this a year ago ( #149955 )
     
  9. SnortySnoopy

    SnortySnoopy Member

    Joined:
    Apr 25, 2018
    Posts:
    213
    @Andrey ,
    1. could you please tell me where to do step no. 3? I can't seems to locate the location of "GM_LicenseCheck_Init(1);" to add the codes to
    2. also an example for no. 4 regarding "decide what you want to do with this answer"
    Thank you.
     
  10. Andrey

    Andrey Member

    Joined:
    Jun 21, 2016
    Posts:
    281
    This modification gives You the right to decide when to make request license verification.
    Function "GM_LicenseCheck_Init(1);" You can use 1 time in the code Create some object.
    Can use she in your code Step if you want to make requests under certain conditions.
    Item 3 is the sample code asynchronous events through which You will get the answers after the feature request GM_LicenseCheck_Init(1);
    After receiving the answer (256, 561 or 291), You decide what to do next (to block game or not).

    Here is an example implementation:

    Create event:
    Code:
    global.licensing_reason = 0;
    Step event:
    Code:
    if(global.licensing_reason == 0)
    {
        GM_LicenseCheck_Init(1); // the request for verification of license
        global.licensing_reason = 1;
    }
    else if(global.licensing_reason == 256)
    {
        // LICENSED means that the server returned back a valid license response
        //show_message_async("LICENSED");
    }
    else if(global.licensing_reason == 291)
    {
        // RETRY means that the license response was unable to be determined - perhaps as a result of faulty networking
        //show_message_async("RETRY");
    }
    else if(global.licensing_reason == 561)
    {
        // NOT_LICENSED
        //show_message_async("NOT_LICENSED");
    }
    Async – Social event:
    Code:
    // RECEIVING RESPONSES FROM THE SERVER DURING LICENSE CHECKING
    
    var type = async_load[? "type"];
    
    
    switch(type)
    {
        // RESPONSE TIME CHECK OF THE LICENSE
        case 1000:
            var game_licensing_lasttime = date_current_datetime();
        break;
        
        
        
        // RESPONSE TO THE LICENSE
        case 2000:
            // LICENSED = 256 (LICENSED means that the server returned back a valid license response)
            // LICENSED_OLD_KEY = 256 (LICENSED means that the server returned back a valid license response)
            // NOT_LICENSED = 561 (NOT_LICENSED means that the server returned back a valid license response that indicated that the user definitively is not licensed)
            // RETRY = 291 (RETRY means that the license response was unable to be determined - perhaps as a result of faulty networking)
            global.licensing_reason = async_load[? "reason"];
        break;
        
        
        
        // ERRORS
        case 3000:
            // ERROR_NOT_MARKET_MANAGED = 3
            // ERROR_SERVER_FAILURE = 4
            // ERROR_OVER_QUOTA = 5
            // ERROR_CONTACTING_SERVER = 257
            // ERROR_INVALID_PACKAGE_NAME = 258
            // ERROR_NON_MATCHING_UID = 259
            var check_licensing_error = async_load[? "error"];
        break;
        
        
        default:
        break;
        
        
    }
    
     
  11. SnortySnoopy

    SnortySnoopy Member

    Joined:
    Apr 25, 2018
    Posts:
    213
    Thanks @Andrey , I'll try doing the above and report back. I think I'll try place the above into the creation code of the splash screen object since that's the only object found in the first room the game run. Alternatively I might add it to the title screen object of the 2nd room (main menu) in the game.

    Anyway I just received feedback from GP, they rejected all my app updates to .aab using beta 2.2.4 for the same reason as the first rejection. Something is seriously wrong which I haven't experience before 2.2.4 & GPLicense Ext 2.3.2. I've only until Oct 1st to fix the issues before they decide what they'll do with my apps. Joining the family program is a huge mistake. :confused:
     
  12. Dan

    Dan YoYo Games Staff YYG Staff

    Joined:
    Apr 15, 2016
    Posts:
    339
    Could you send us a helpdesk ticket with all your rejection info from Google, please? We'll see if there's anything we need to address there.
     
  13. SnortySnoopy

    SnortySnoopy Member

    Joined:
    Apr 25, 2018
    Posts:
    213
    @Dan , I just got reply from GP, they decide to approve the first app that I report as rejected (which I had fix yesterday) after I chooses to change my target market and opt out of the family program. I haven't resubmit the apps that was rejected today. I'm going to try the same route, see if they'll approve it once I remove them from the family program regardless the warning messages related to licensing.

    Note: The now approved (rejected app) still contain the 2 warnings related to the Licensing screen - I had only fixed the Access_Network_State, which they decide to let through after I remove my app from the family program. You need not look into the case with Access_Network_State, since as I mentioned in earlier posts it was a mistake on my end. The case which Andrey reported is what's relevant.

    PS: I'll send you the report tonight.

    Edit:

    @Dan ,
    I've decided to post the report in the Feedback topic. I can't decide which category to choose in the helpdesk. Sorry and thanks.
     
    Last edited: Sep 17, 2019

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice