440 lines
17 KiB
Plaintext
440 lines
17 KiB
Plaintext
page.title=Licensing Reference
|
|
parent.title=Application Licensing
|
|
parent.link=index.html
|
|
@jd:body
|
|
|
|
|
|
|
|
<div id="qv-wrapper">
|
|
<div id="qv">
|
|
|
|
<h2>In this document</h2>
|
|
<ol>
|
|
<li><a href="#lvl-summary">LVL Classes and Interfaces</a></li>
|
|
<li><a href="#server-response-codes">Server Response Codes</a></li>
|
|
<li><a href="#extras">Server Response Extras</a></li>
|
|
</ol>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<h2 id="lvl-summary">LVL Classes and Interfaces</h2>
|
|
|
|
<p>Table 1 lists all of the source files in the License Verification
|
|
Library (LVL) available through the Android SDK. All of the files are part of
|
|
the <code>com.android.vending.licensing</code> package.</p>
|
|
|
|
<p class="table-caption"><strong>Table 1.</strong> Summary of LVL library
|
|
classes and interfaces.</p>
|
|
|
|
<div style="width:99%">
|
|
<table width="100%">
|
|
|
|
<tr>
|
|
<th width="15%">Category</th>
|
|
<th width="20%">Name</th>
|
|
<th width="100%">Description</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td rowspan="2">License check and result</td>
|
|
<td>LicenseChecker</td>
|
|
<td>Class that you instantiate (or subclass) to initiate a license check.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><em>LicenseCheckerCallback</em></td>
|
|
<td>Interface that you implement to handle result of the license check.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td rowspan="3" width="15%">Policy</td>
|
|
<td width="20%"><em>Policy</em></td>
|
|
<td width="100%">Interface that you implement to determine whether to allow
|
|
access to the application, based on the license response. </td>
|
|
</tr>
|
|
<tr>
|
|
<td>ServerManagedPolicy</td>
|
|
<td width="100%">Default {@code Policy} implementation. Uses settings provided by the
|
|
licensing server to manage local storage of license data, license validity,
|
|
retry.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>StrictPolicy</td>
|
|
<td>Alternative {@code Policy} implementation. Enforces licensing based on a direct
|
|
license response from the server only. No caching or request retry.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td rowspan="2" width="15%">Data obfuscation <br><em>(optional)</em></td>
|
|
<td width="20%"><em>Obfuscator</em></td>
|
|
<td width="100%">Interface that you implement if you are using a {@code Policy} (such as
|
|
ServerManagedPolicy) that caches license response data in a persistent store.
|
|
Applies an obfuscation algorithm to encode and decode data being written or
|
|
read.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>AESObfuscator</td>
|
|
<td>Default Obfuscator implementation that uses AES encryption/decryption
|
|
algorithm to obfuscate/unobfuscate data.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td rowspan="2" width="15%">Device limitation<br><em>(optional)</em></td>
|
|
<td width="20%"><em>DeviceLimiter</em></td>
|
|
<td width="100%">Interface that you implement if you want to restrict use of an
|
|
application to a specific device. Called from LicenseValidator. Implementing
|
|
DeviceLimiter is not recommended for most applications because it requires a
|
|
backend server and may cause the user to lose access to licensed applications,
|
|
unless designed with care.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>NullDeviceLimiter</td>
|
|
<td>Default DeviceLimiter implementation that is a no-op (allows access to all
|
|
devices).</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td rowspan="6" width="15%">Library core, no integration needed</td>
|
|
<td width="20%">ResponseData</td>
|
|
<td width="100%">Class that holds the fields of a license response.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>LicenseValidator</td>
|
|
<td>Class that decrypts and verifies a response received from the licensing
|
|
server.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>ValidationException</td>
|
|
<td>Class that indicates errors that occur when validating the integrity of data
|
|
managed by an Obfuscator.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>PreferenceObfuscator</td>
|
|
<td>Utility class that writes/reads obfuscated data to the system's
|
|
{@link android.content.SharedPreferences} store.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><em>ILicensingService</em></td>
|
|
<td>One-way IPC interface over which a license check request is passed to the
|
|
Google Play client.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><em>ILicenseResultListener</em></td>
|
|
<td>One-way IPC callback implementation over which the application receives an
|
|
asynchronous response from the licensing server.</td>
|
|
</tr>
|
|
|
|
</table>
|
|
</div>
|
|
|
|
|
|
<h2 id="server-response-codes">Server Response Codes</h2>
|
|
|
|
<p>Table 2 lists all of the license response codes supported by the
|
|
licensing server. In general, an application should handle all of these response
|
|
codes. By default, the LicenseValidator class in the LVL provides all of the
|
|
necessary handling of these response codes for you. </p>
|
|
|
|
<p class="table-caption"><strong>Table 2.</strong> Summary of response codes
|
|
returned by the Google Play server in a license response.</p>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
<th>Response Code</th>
|
|
<th>Description</th>
|
|
<th>Signed?</th>
|
|
<th>Extras</th>
|
|
<th>Comments</th>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code LICENSED}</td>
|
|
<td>The application is licensed to the user. The user has purchased the
|
|
application or the application only exists as a draft.</td>
|
|
<td>Yes</td>
|
|
<td><code>VT</code>, <code>GT</code>, <code>GR</code></td>
|
|
<td><em>Allow access according to {@code Policy} constraints.</em></td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code LICENSED_OLD_KEY}</td>
|
|
<td>The application is licensed to the user, but there is an updated application
|
|
version available that is signed with a different key. </td>
|
|
<td>Yes </td>
|
|
<td><code>VT</code>, <code>GT</code>, <code>GR</code>, <code>UT</code></td>
|
|
<td><em>Optionally allow access according to {@code Policy} constraints.</em>
|
|
<p style="margin-top:.5em;">Can indicate that the key pair used by the installed
|
|
application version is invalid or compromised. The application can allow access
|
|
if needed or inform the user that an upgrade is available and limit further use
|
|
until upgrade.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code NOT_LICENSED}</td>
|
|
<td>The application is not licensed to the user.</td>
|
|
<td>No</td>
|
|
<td></td>
|
|
<td><em>Do not allow access.</em></td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code ERROR_CONTACTING_SERVER}</td>
|
|
<td>Local error — the Google Play application was not able to reach the
|
|
licensing server, possibly because of network availability problems. </td>
|
|
<td>No</td>
|
|
<td></td>
|
|
<td><em>Retry the license check according to {@code Policy} retry limits.</em></td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code ERROR_SERVER_FAILURE}</td>
|
|
<td>Server error — the server could not load the application's key
|
|
pair for licensing.</td>
|
|
<td>No</td>
|
|
<td></td>
|
|
<td><em>Retry the license check according to {@code Policy} retry limits.</em>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code ERROR_INVALID_PACKAGE_NAME}</td>
|
|
<td>Local error — the application requested a license check for a package
|
|
that is not installed on the device. </td>
|
|
<td>No </td>
|
|
<td></td>
|
|
<td><em>Do not retry the license check.</em>
|
|
<p style="margin-top:.5em;">Typically caused by a development error.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code ERROR_NON_MATCHING_UID}</td>
|
|
<td>Local error — the application requested a license check for a package
|
|
whose UID (package, user ID pair) does not match that of the requesting
|
|
application. </td>
|
|
<td>No </td>
|
|
<td></td>
|
|
<td><em>Do not retry the license check.</em>
|
|
<p style="margin-top:.5em;">Typically caused by a development error.</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code ERROR_NOT_MARKET_MANAGED}</td>
|
|
<td>Server error — the application (package name) was not recognized by
|
|
Google Play. </td>
|
|
<td>No</td>
|
|
<td></td>
|
|
<td><em>Do not retry the license check.</em>
|
|
<p style="margin-top:.5em;">Can indicate that the application was not published
|
|
through Google Play or that there is an development error in the licensing
|
|
implementation.</p>
|
|
</td>
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<p class="note"><strong>Note:</strong> As documented in <a
|
|
href="{@docRoot}google/play/licensing/setting-up.html#test-env">
|
|
Setting Up The Testing Environment</a>, the response code can be manually
|
|
overridden for the application developer and any registered test users via the
|
|
Google Play Developer Console.
|
|
<br/><br/>
|
|
Additionally, as noted above, applications that are in draft mode (in other
|
|
words, applications that have been uploaded but have <em>never</em> been
|
|
published) will return {@code LICENSED} for all users, even if not listed as a test
|
|
user. Since the application has never been offered for download, it is assumed
|
|
that any users running it must have obtained it from an authorized channel for
|
|
testing purposes.</p>
|
|
|
|
|
|
|
|
|
|
<h2 id="extras">Server Response Extras</h2>
|
|
|
|
<p>To assist your application in managing access to the application across the application refund
|
|
period and provide other information, The licensing server includes several pieces of
|
|
information in the license responses. Specifically, the service provides recommended values for the
|
|
application's license validity period, retry grace period, maximum allowable retry count, and other
|
|
settings. If your application uses <a href="{@docRoot}google/play/expansion-files.html">APK
|
|
expansion files</a>, the response also includes the file names, sizes, and URLs. The server appends
|
|
the settings as key-value pairs in the license response "extras" field. </p>
|
|
|
|
<p>Any {@code Policy} implementation can extract the extras settings from the license
|
|
response and use them as needed. The LVL default {@code Policy} implementation, <a
|
|
href="{@docRoot}google/play/licensing/adding-licensing.html#ServerManagedPolicy">{@code
|
|
ServerManagedPolicy}</a>, serves as a working
|
|
implementation and an illustration of how to obtain, store, and use the
|
|
settings. </p>
|
|
|
|
<p class="table-caption"><strong>Table 3.</strong> Summary of
|
|
license-management settings supplied by the Google Play server in a license
|
|
response.</p>
|
|
|
|
<table>
|
|
<tr>
|
|
<th>Extra</th><th>Description</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>{@code VT}</td>
|
|
<td>License validity timestamp. Specifies the date/time at which the current
|
|
(cached) license response expires and must be rechecked on the licensing server. See the section
|
|
below about <a href="#VT">License validity period</a>.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code GT}</td>
|
|
<td>Grace period timestamp. Specifies the end of the period during which a
|
|
Policy may allow access to the application, even though the response status is
|
|
{@code RETRY}. <p>The value is managed by the server, however a typical value would be 5
|
|
or more days. See the section
|
|
below about <a href="#GTGR">Retry period and maximum retry count</a>.</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code GR}</td>
|
|
<td>Maximum retries count. Specifies how many consecutive {@code RETRY} license checks
|
|
the {@code Policy} should allow, before denying the user access to the application.
|
|
<p>The value is managed by the server, however a typical value would be "10" or
|
|
higher. See the section
|
|
below about <a href="#GTGR">Retry period and maximum retry count</a>.</p></td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code UT}</td>
|
|
<td>Update timestamp. Specifies the day/time when the most recent update to
|
|
this application was uploaded and published. <p>The server returns this extra
|
|
only for {@code LICENSED_OLD_KEYS} responses, to allow the {@code Policy} to determine how much
|
|
time has elapsed since an update was published with new licensing keys before
|
|
denying the user access to the application. </p></td>
|
|
</tr>
|
|
|
|
|
|
<!-- APK EXPANSION FILE RESPONSES -->
|
|
|
|
<tr>
|
|
<td>{@code FILE_URL1} or {@code FILE_URL2}</td>
|
|
<td>The URL for an expansion file (1 is for the main file, 2 is the patch file). Use this to
|
|
download the file over HTTP.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code FILE_NAME1} or {@code FILE_NAME2}</td>
|
|
<td>The expansion file's name (1 is for the main file, 2 is the patch file). You must use this
|
|
name when saving the file on the device.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>{@code FILE_SIZE1} or {@code FILE_SIZE2}</td>
|
|
<td>The size of the file in bytes (1 is for the main file, 2 is the patch file). Use this to
|
|
assist with downloading and to ensure that enough space is available on the device's shared
|
|
storage location before downloading.</td>
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
|
|
|
<h4 id="VT">License validity period</h4>
|
|
|
|
<p>The Google Play licensing server sets a license validity period for all
|
|
downloaded applications. The period expresses the interval of time over which an
|
|
application's license status should be considered as unchanging and cacheable by
|
|
a licensing {@code Policy} in the application. The licensing server includes the
|
|
validity period in its response to all license checks, appending an
|
|
end-of-validity timestamp to the response as an extra under the key {@code VT}. A
|
|
{@code Policy} can extract the VT key value and use it to conditionally allow access to
|
|
the application without rechecking the license, until the validity period
|
|
expires. </p>
|
|
|
|
<p>The license validity signals to a licensing {@code Policy} when it must recheck the
|
|
licensing status with the licensing server. It is <em>not</em> intended to imply
|
|
whether an application is actually licensed for use. That is, when an
|
|
application's license validity period expires, this does not mean that the
|
|
application is no longer licensed for use — rather, it indicates only that
|
|
the {@code Policy} must recheck the licensing status with the server. It follows that,
|
|
as long as the license validity period has not expired, it is acceptable for the
|
|
{@code Policy} to cache the initial license status locally and return the cached license
|
|
status instead of sending a new license check to the server.</p>
|
|
|
|
<p>The licensing server manages the validity period as a means of helping the
|
|
application properly enforce licensing across the refund period offered by
|
|
Google Play for paid applications. It sets the validity period based on
|
|
whether the application was purchased and, if so, how long ago. Specifically,
|
|
the server sets a validity period as follows:</p>
|
|
|
|
<ul>
|
|
<li>For a paid application, the server sets the initial license validity period
|
|
so that the license response remains valid for as long as the application is
|
|
refundable. A licensing {@code Policy} in the application may cache the
|
|
result of the initial license check and does not need to recheck the license
|
|
until the validity period has expired.</li>
|
|
<li>When an application is no longer refundable, the server
|
|
sets a longer validity period — typically a number of days. </li>
|
|
|
|
<!-- TODO: Verify the following behavior is still true w/ OBB: -->
|
|
<li>For a free application, the server sets the validity period to a very high
|
|
value (<code>long.MAX_VALUE</code>). This ensures that, provided the {@code Policy} has
|
|
cached the validity timestamp locally, it will not need to recheck the
|
|
license status of the application in the future.</li>
|
|
</ul>
|
|
|
|
<p>The {@code ServerManagedPolicy} implementation uses the extracted timestamp
|
|
(<code>mValidityTimestamp</code>) as a primary condition for determining whether
|
|
to recheck the license status with the server before allowing the user access to
|
|
the application. </p>
|
|
|
|
|
|
<h4 id="GTGR">Retry period and maximum retry count</h4>
|
|
|
|
<p>In some cases, system or network conditions can prevent an application's
|
|
license check from reaching the licensing server, or prevent the server's
|
|
response from reaching the Google Play client application. For example, the
|
|
user might launch an application when there is no cell network or data
|
|
connection available—such as when on an airplane—or when the
|
|
network connection is unstable or the cell signal is weak. </p>
|
|
|
|
<p>When network problems prevent or interrupt a license check, the Google
|
|
Play client notifies the application by returning a {@code RETRY} response code to
|
|
the {@code Policy}'s <code>processServerResponse()</code> method. In the case of system
|
|
problems, such as when the application is unable to bind with Google Play's
|
|
{@code ILicensingService} implementation, the {@code LicenseChecker} library itself calls the
|
|
Policy <code>processServerResonse()</code> method with a {@code RETRY} response code.
|
|
</p>
|
|
|
|
<p>In general, the {@code RETRY} response code is a signal to the application that an
|
|
error has occurred that has prevented a license check from completing.
|
|
|
|
<p>The Google Play server helps an application to manage licensing under
|
|
error conditions by setting a retry "grace period" and a recommended maximum
|
|
retries count. The server includes these values in all license check responses,
|
|
appending them as extras under the keys {@code GT} and {@code GR}. </p>
|
|
|
|
<p>The application {@code Policy} can extract the {@code GT} and {@code GR} extras and use them to
|
|
conditionally allow access to the application, as follows:</p>
|
|
|
|
<ul>
|
|
<li>For a license check that results in a {@code RETRY} response, the {@code Policy} should
|
|
cache the {@code RETRY} response code and increment a count of {@code RETRY} responses.</li>
|
|
<li>The {@code Policy} should allow the user to access the application, provided that
|
|
either the retry grace period is still active or the maximum retries count has
|
|
not been reached.</li>
|
|
</ul>
|
|
|
|
<p>The {@code ServerManagedPolicy} uses the server-supplied {@code GT} and {@code GR} values as
|
|
described above. The example below shows the conditional handling of the retry
|
|
responses in the <code>allow()</code> method. The count of {@code RETRY} responses is
|
|
maintained in the <code>processServerResponse()</code> method, not shown. </p>
|
|
|
|
|
|
<pre>
|
|
public boolean allowAccess() {
|
|
long ts = System.currentTimeMillis();
|
|
if (mLastResponse == LicenseResponse.LICENSED) {
|
|
// Check if the LICENSED response occurred within the validity timeout.
|
|
if (ts <= mValidityTimestamp) {
|
|
// Cached LICENSED response is still valid.
|
|
return true;
|
|
}
|
|
} else if (mLastResponse == LicenseResponse.RETRY &&
|
|
ts < mLastResponseTime + MILLIS_PER_MINUTE) {
|
|
// Only allow access if we are within the retry period or we haven't used up our
|
|
// max retries.
|
|
return (ts <= mRetryUntil || mRetryCount <= mMaxRetries);
|
|
}
|
|
return false;
|
|
}</pre>
|
|
|