Protecting the content

When an app is published on Google Play, anyone can download and install it. This is great when the user has purchased the app. However, we need a way to prevent users from accessing the app if they have managed to get hold of the app without purchasing it.

Getting ready

Before we can add licensing to our app, we will need the app's public key from the Play Store app listing, which is created on the Google Play Developer Console (https://play.google.com/apps/publish).

How to do it...

One of the ways to prevent unauthorized access to our app's content is by adding Google Play licensing to our app. Then, when the app is launched, we can verify the user with Google Play.

The first thing that we will need to do before adding licensing to our app is to get the app's public key from Google Play:

  1. We need to copy the Base64-encoded public key from the console under the app's Services & APIs section:
    How to do it...

    The public app license key

  2. Then, we will paste that key into our app. For testing purposes, we can just store it in a field, but we should always obfuscate this key:
    private const string PublicKey = "<app-license-key>";

Now that we have the public key, we set up our app so that we can perform the license check once the app has been launched:

  1. First, we will need to install the Android License Verification Library NuGet.
  2. Next, we need to add the licensing check permission to the app:
    [assembly: UsesPermission(
      "com.android.vending.CHECK_LICENSE")]
  3. Then, we implement the ILicenseCheckerCallback interface:
    public class MainActivity : Activity,
      ILicenseCheckerCallback {
      public void Allow(PolicyServerResponse response) {
      }
      public void DontAllow(PolicyServerResponse response) {
      }
      public void ApplicationError(CallbackErrorCode errorCode) {
      }
    }

The next thing that we need to do is to set up the license checker requirements:

  1. First, we collect the unique information about the device, which we require when we will encrypt the server responses:
    string deviceId = Settings.Secure.GetString(
      ContentResolver, Settings.Secure.AndroidId);
    string appId = this.PackageName;
    byte[] salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  2. Next, we create an instance of AesObfuscator, which will be used to encrypt the response:
    var obfuscator = new AesObfuscator(salt, appId, deviceId);
  3. Then, we create an instance of ServerManagedPolicy, which will process the server response to determine whether the user is authorized to use our app:
    var policy = new ServerManagedPolicy(this, obfuscator);
  4. Finally, we create an instance of LicenseChecker, making use of the public key:
    checker = new LicenseChecker(this, policy, PublicKey);

Now, after the app is launched and we want to ensure that the user is authorized to access the app, we initiate the license check:

  1. To start a check, or to retry a failed check, we invoke the CheckAccess() method of the license checker:
    checker.CheckAccess(this);
  2. When a result comes back from Google Play, we are notified through the various methods of the ILicenseCheckerCallback interface. The Allow() method will be called if the user is licensed:
    public void Allow(PolicyServerResponse response) {
      // we are licensed
    }
  3. If the user is not licensed, or there is a recoverable error, the DontAllow() method is invoked:
    public void DontAllow(PolicyServerResponse response) {
      if (response == PolicyServerResponse.Retry) {
        // we can retry the license check
      }
      else {
        // confirmed unlicensed user
      }
    }
  4. If there is an app error, the ApplicationError() method is invoked:
    public void ApplicationError(CallbackErrorCode errorCode) {
      // handle the specific error code
    }
  5. When we are finished with the checks, we clean up the resources using the OnDestroy() method:
    checker.OnDestroy();

As the publishing process usually takes a few days, we can test our implementation by specifying the server response manually:

  1. First, we navigate to the Account details section of the developer console settings page.
  2. Then, at the bottom of the page, we select LICENSED from the Test License Response dropdown, and click on the Save button:
    How to do it...

    The steps to set the server response to Licensed

  3. We will now receive the Licensed response in our app, and the Allow() method will be invoked.

How it works...

Some apps contain contents that we would like only legitimate users to be able to access. Although this is usually not the case with free apps, we almost always want to prevent access if the user has not purchase the app.

Tip

Usually, only paid apps require licensing to be implemented.

Google Play provides a licensing service, which we can use to verify that the user did actually download the app from Google Play, either after purchasing the app or because the app was free. We use this service by querying Google Play at runtime to obtain a licensing status. Then we can determine, based on the response, whether the user is allowed to continue using the app, or should download the app.

Note

Google Play provides a licensing service only to apps distributed via Google Play. Other stores use other licensing mechanisms.

The only thing we need in order to get started is the public key from the Google Play Developer Console. We can either create a new app listing or use the existing app listing from a previous release. This key is also used when handling in-app billing, so we should always keep this key secure. One way to secure the key would be to obfuscate or encrypt the key.

Once we have the key, we start with the licensing implementation. We first need to install the Android License Verification Library NuGet, which contains the required assemblies. Once installed, we will need to add the com.android.vending.CHECK_LICENSE permission to our app.

Tip

Even though the license check requires Internet access, the only permission that is required is com.android.vending.CHECK_LICENSE.

In order to handle the responses from the licensing sever, we need to implement the ILicenseCheckerCallback interface. This interface has three methods, one of which is invoked depending on what the response from the server is. The Allow() method corresponds to a confirmed licensed user. The DontAllow() method corresponds to a confirmed unlicensed user, or if there is a problem and we should retry. In the case of a retry, the PolicyServerResponse parameter value will be Retry. Also, the ApplicationError() method provides us with the CallbackErrorCode parameter in the case of an error when retrieving the license status.

Before we can initiate the license check, we will need an implementation of IPolicy, such as ServerManagedPolicy. Policies are used to determine whether the user with a given license is allowed to use the app. They contain the logic for allowing or disallowing user access based on the result of the license check. The ServerManagedPolicy instance uses server-provided settings and cached server responses to manage access to the app. Because this policy caches responses, the user can use the app even if the network goes down.

As the ServerManagedPolicy instance caches the server responses on the device, we need to encrypt the response to prevent unauthorized access to this response. To do this, we need to provide the policy an implementation of IObfuscator, such as AesObfuscator. This obfuscator uses the device ID, the app ID, and a random salt to encrypt the responses.

Note

Not all policies cache the response, such as StrictPolicy, thus, they do not require encryption or an obfuscator.

To actually perform the check, we create an instance of LicenseChecker, providing the policy and the public key from Google Play. Then, we invoke the CheckAccess() method, passing in the ILicenseCheckerCallback instance. As soon as the server responds, the provided policy will determine whether the user should be able to access the app, and invoke one of the methods of the ILicenseCheckerCallback instance.

Once we have determined whether or not the user can access the app, we must clean up any resources used by the license checker. This we do through the OnDestroy() method of the license checker.

Note

Once the license checker has been cleaned up, a new instance needs to be created to perform the check again.

To test our implementation against the various responses from the server, we will need to ensure that the app has been published, and is not just in the saved draft state. We need to publish our app even though it is not yet complete; this is because when we do the license check, we will need Google Play to know about our package name. The app only needs to be published to the Alpha channel, and does not even have to be a complete app. We should add a (see Uploading the app package recipe) bit here so that we don't have to go into detail until much later. We can just create an empty app, with our package name, and signed with the keystore that we will use for the final app.

Note

In order to test licensing, an app with the same package name, which is signed with the same keystore, must be published to Google Play.

After we have published our app to one of the channels, we do have to wait for a few days before the app can be tested with actual responses. But, we can also ask Google Play to respond with simulated responses. To simulate the various responses that we might receive, we select each response from the console settings. On the Settings page, under the Account details section and at the bottom of the page, we select the response from the License Test Response dropdown.

There's more...

There is also the StrictPolicy instance that we can use instead of ServerManagedPolicy. This policy does not persist the result from the server but instead just keeps it in memory. This results in the app checking with the server each time the app is launched. We might want to do this if we want to ensure that the user is licensed at the time of use. However, this comes at a cost in which the user has to be connected to the Internet each time the app is launched.

In the case where we need greater flexibility or control over the license checking, we can also create our own policy entirely. As the license checker only requires that the IPolicy interface be implemented, we can implement our own instance of that interface:

public class MyPolicy : IPolicy {
  public bool AllowAccess() {
  }
  public void ProcessServerResponse(
  PolicyServerResponse response, ResponseData rawData) {
  }
}

We can also create our own obfuscator for an existing policy, such as the ServerManagedPolicy instance, by implementing the IObfuscator interface:

public interface IObfuscator {
  string Obfuscate (string original, string key) {
  }
  string Unobfuscate (string obfuscated, string key) {
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.54.6