Android Tutorial Protect your Android application with the Licensing library

The licensing library allows you to use Android market licensing service to verify that the user is allowed to access your application.

Your applications sends a request to the local market application. The market application contacts the market server and returns the result. The result is cached based on the market rules.

It is recommended to go over Google's documentation related to the licensing method: Application Licensing | Android Developers

Configuring the licensing library is simple. You should first have a publisher account in the market.
The license key is available in Google Play developer console under Development tools - Services & APIs.

The licensing library and service will not prevent a dedicated hacker from hacking your application. It will however make it more difficult.

The first step is to initialize a LicenseChecker object:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   Dim lc As LicenseChecker
   Dim p As PhoneId
   lc.Initialize("lc", p.GetDeviceId, publicKey, "kljdflkf".GetBytes("UTF8"))
   lc.SetVariableAndValue("test_1", "some secret value")
   lc.CheckAccess
End Sub
The result of the licensing check is cached locally. The cache is encrypted with AES algorithm. In order to avoid users from tampering with the cache and copying the cache to different devices, the device id is used together with the package name as the password.

Note that the same user will be able to download your application to other devices running with the same user account.

PhoneId (from the Phone library) requires the READ_STATE permission. The protection will still work if you pass an arbitrary string. It will be weaker however.
The Salt parameter should be an array of bytes with some random values (the values should be the same on each run).

Edit: It is recommended to use the alternative id method as described here: http://www.b4x.com/forum/basic4andr...oid-device-unique-id-alternative-phoneid.html

The next step is to call lc.CheckAccess. This in turn calls the market application or the local cache and checks whether the user is allowed to access the program.
One of the following events will be raised when the result arrives: Allow, DontAllow or Error (ErrorCode As String).
It is up to you to handle the event subs as required.

LicenseChecker.SetVariableAndValue
A simple way to hack an application is to "jump over" the checking code. For example a hacker might remove the call to CheckAccess and instead call your Allow event sub.
In order to make it a bit more complicated you can call LicenseChecker.SetVariableAndValue.
For example:
B4X:
lc.SetVariableAndValue("test_1", "some secret value")
The above code will set the value of a process global string value in the main activity named test1 to "some secret value" if the check was successful. You should not use or test the value of test1 in the Allow event sub as it will be too obvious. Instead you should use it later in your program.
You can be creative and pass the name of the variable or the value by using BytesToString or some other way.
As this variable is accessed in a dynamic way it will fail when the code is obfuscated. Therefore you need to include an underscore in the variable name to prevent it from being obfuscated. For example: test_1.
Note that SetVariableAndValue method will fail when running in rapid debug mode as the variable is part of the "debugger engine".

A more complete example:
B4X:
Sub Process_Globals
   Dim publicKey As String
   publicKey = "MIIBIjANBgkqhAADSFEFEFkiG9w0BfW/cGhTbtIs6QIDAQAB..."
   Dim test_1 As String
End Sub
Sub Globals

End Sub
Sub Activity_Create(FirstTime As Boolean)
   Dim lc As LicenseChecker
   Dim p As PhoneId
   lc.Initialize("lc", p.GetDeviceId, publicKey, "kljdflkf".GetBytes("UTF8"))
   lc.SetVariableAndValue("test1", "some secret value")
   lc.CheckAccess
End Sub
Sub lc_Allow
   Log("Allow")
End Sub
Sub lc_DontAllow
   Log("DontAllow")
   ToastMessageShow("Closing application.", True)
   Activity.Finish
End Sub
Sub lc_Error (ErrorCode As String)
   Log("error: " & ErrorCode)
   ToastMessageShow("Closing application.", True)
   Activity.Finish
End Sub
Sub Activity_Pause(UserClosed As Boolean)
 
End Sub
Sub Activity_Resume

End Sub

The library is available here: http://www.b4x.com/forum/additional-libraries-official-updates/11430-licensing-library.html
 
Last edited:

Highwinder

Active Member
Licensed User
Longtime User
Mucho Thanko

Erel,

Again, a monster thanks for providing this library. I uploaded an app to the Market and sure enough, it works. Was nice to see it in action through the testing. Works great.
 

ondesic

Active Member
Licensed User
Longtime User
I integrated the license and have been testing it. I know the info is cached locally. However, it doesn't seem to query the local info.
I allow a license on a phone and all is well. When I turn the phone to airplane mode, it rejects the license and closes the program.

Do I need to do something special to use the local cache?
 

Kevin

Well-Known Member
Licensed User
Longtime User
I integrated the license and have been testing it. I know the info is cached locally. However, it doesn't seem to query the local info.
I allow a license on a phone and all is well. When I turn the phone to airplane mode, it rejects the license and closes the program.

Do I need to do something special to use the local cache?

The local cache should be used automatically according to Google policy.

Try running your program with an internet connection for a day or two and then disable internet. It should work.

ondesic: Just curious what you have found out. I'd like to add this to my app as well but I wouldn't want my customers to have any issues like this (even though due to its nature, my app would be worthless in Airplane mode as it required WiFi to be enabled).
 

ondesic

Active Member
Licensed User
Longtime User
Erel,

I've tested this license a lot. One thing I noticed is that 2 Error messages get sent directly to the lc_DontAllow sub instead of the lc_Error sub.

Those errors are ERROR_SERVER_FEALURE and ERROR_CONTACTING_SERVER.

lc_DontAllow needs parameters to catch these 2 errors. Like, lc_DontAllow (Errors As String). I would think that when these 2 errors occur, then the license uses a cache.

Anyway my problem still persists when I change to airplane mode. I licensed my app, then played with it for a few days like you suggested. The market is still not using a cache. It is just sending the response to lc_DontAllow.

Perhaps a cache is not created on the device automatically. Perhaps we must create one manually? If it is being created, it isn't being used when a connection is unavailable.
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
The default policy which was implemented by Google uses the server responses to decide whether the cached license is valid or not.
You can read more about it here: Application Licensing | Android Developers
You can try looking for the cache file. It should be located in the internal folder and named: com.android.vending.licensing.ServerManagedPolicy
 

bluejay

Active Member
Licensed User
Longtime User
Hi Erel,

I am looking at using the Licensing library in a Widget, preferably doing the license check in the Preferences activity.

For SetVariableAndValue the documentation refers to setting a variable in the Main activity. Is this the only restriction?

What is the best way to use this library in a Widget where the Main Activity may never run?

thanks,

bluejay
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
For SetVariableAndValue the documentation refers to setting a variable in the Main activity. Is this the only restriction
The variable should be declared in the main activity. However there is no problem with calling this method from a service.

What is the best way to use this library in a Widget where the Main Activity may never run?
You should do the check in Service_Create.
 

palmzac

Active Member
Licensed User
Longtime User
Hi,

The problem is that my app was "force quit" & display "The application Market (process com.android.vending) has stopped unexpectedly. Please try again.". Everything is OK before Market 3.3.11 was updated. Would you have any solution ? Thanks !

My phone is Nexus S 2.3.6 . Market is 3.3.11.

palmzac
 

palmzac

Active Member
Licensed User
Longtime User
Hi Erel,

You're right. It's my fault. Everything is OK after I reset my phone. ( reset to factory setting ). But, I don't know why. Anyway, Thank for your help !

palmzac
 

Inman

Well-Known Member
Licensed User
Longtime User
I am facing a strange issue with licensing library. I gave the following code in the lc_Error sub:

B4X:
   Dim res As Int, bm As Bitmap
   bm.Initialize(File.DirAssets,"android-market-icon.png")
   res=Msgbox2("Error checking license. Please check Android Market manually","Licence Check","Check Market","Exit","",Null)
   Log("why not")
   If res=DialogResponse.POSITIVE Then
         Try
            Log("in here")
            Dim market As Intent, uri As String
            uri="market://search?q=pname:com.mysite.myapp"
            market.Initialize(market.ACTION_VIEW,uri)
            StartActivity(market)
         Catch
            Dim ph As PhoneIntents
            StartActivity(ph.OpenBrowser("https://market.android.com/developer?pub=myname"))
         End Try
   End If
   Activity.Finish

Now the issue here is that the app won't execute anything below the res=msgbox2 line. I can see the message box. But once I click the Positive or Cancel response button, the message box simply goes while the app stays there. That Log("why not") line given just below the res=msgbox2 line, will not show up in the log.

Any idea why this is happening?
 

agraham

Expert
Licensed User
Longtime User
Always look in the unfiltered logs when something odd is happening. I suspect that the lc_Error Sub is not running on your main thread but on one belonging to the licensing library and this is causing an exception on returning from the Msgbox. From the source code comments.
The callback does not occur in the original checking thread. Your application should post to the appropriate handling thread
 
Last edited:

Inman

Well-Known Member
Licensed User
Longtime User
I tried looking into the unfiltered log but couldn't find (or rather understand) anything relevant to the issue. For the time being I used a timer to solve the issue, although I am not sure if it is a good practice on my part.
 

slowtime

Active Member
Licensed User
Longtime User
Hi,

I inserted my account in Test Accounts.

I have got the Public Key.
I put Public Key in publicKey var.
Now how I can test if my application is protect ?

Maybe, I must launch the application from a device connected with another gmail account or I must run the application ( not downloaded from market) in a device with a gmail test account ?

Sorry but my English is not good enought.

Thank you
 

slowtime

Active Member
Licensed User
Longtime User
Hi Erel,

I tried but there is something wrong in my code.

This is my code:

HTML:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.
Dim PhoneId As PhoneId  

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim edittext1 As EditText
   Dim button1 As Button
   Dim ImageView1 As ImageView
   Dim Label1 As Label
   Dim Label2 As Label
   Dim Label3 As Label
   Dim Panel1 As Panel
   
   Dim publicKey As String
   publicKey = "MIIB......" '.. checked the same as in Google
   Dim test1 As String
   
   Dim EditText2 As EditText
End Sub
Sub Activity_Create(FirstTime As Boolean)
If FirstTime Then
      Dim lc As LicenseChecker
      Dim p As PhoneId
      lc.Initialize("lc", p.GetDeviceId, publicKey, "kljdflkf".GetBytes("UTF8"))
      lc.SetVariableAndValue("test1", "ok")
      lc.CheckAccess
   End If
      
      activity.LoadLayout("imei.bal")
      edittext2.Visible=False
      edittext2.Enabled=False
      
   edittext1.Text=PhoneId.GetDeviceId


End Sub
Sub lc_Allow
   Log("Allow")
   Log(test1)
   ToastMessageShow("App registred" & TEST1,True)
End Sub
Sub lc_DontAllow
   Log("DontAllow")
   Log(test1)
   ToastMessageShow("Closing application.", True)
   edittext2.Visible=True
edittext2.Enabled=True
edittext2.Text="test1 = " & test1
   'Activity.Finish
End Sub
Sub lc_Error (ErrorCode As String)
   Log("error: " & ErrorCode)
   ToastMessageShow("Closing application.", True)
   Activity.Finish
End Sub
Sub ImageView1_LongClick
edittext2.Visible=True
edittext2.Enabled=True
edittext2.Text="test1 = " & test1
End Sub

When I download application Toast messages is "Closing application." and test1 is empty.

Help me please.
Ciao

p.s. I did not change in SDK and I did not loaded any SDK library about licensing.
 
Top