Классы и интерфейсы LVL
В таблице 1 перечислены все исходные файлы библиотеки проверки лицензий (LVL), доступные в Android SDK. Все файлы входят в пакет com.android.vending.licensing.
Таблица 1. Обзор классов и интерфейсов библиотеки LVL.
| Категория | Название | Описание |
|---|---|---|
| Проверка лицензии и ее результат | LicenseChecker | Класс, экземпляр которого вы создаете (или подкласс), чтобы инициировать проверку лицензии. |
| LicenseCheckerCallback | Интерфейс, который вы реализуете для обработки результатов проверки лицензии. | |
| Правила | Правила | Интерфейс, который вы реализуете, чтобы определить, разрешать ли доступ к приложению на основе ответа лицензии. |
| ServerManagedPolicy | Реализация Policy по умолчанию. Использует настройки, предоставленные сервером лицензирования, для управления локальным хранилищем данных лицензии, сроком действия лицензии и повторными попытками. |
|
| StrictPolicy | Альтернативная реализация Policy. Принудительно применяет лицензирование только на основе прямого ответа сервера. Кеширование и повторная отправка запросов не выполняются. |
|
| Обфускация данных (необязательно) |
Обфускатор | Интерфейс, который вы реализуете, если используете Policy (например, ServerManagedPolicy), кэширующий данные ответа лицензии в постоянном хранилище.
Применяет алгоритм обфускации для кодирования и декодирования записываемых или считываемых данных. |
| AESObfuscator | Реализация обфускатора по умолчанию, которая использует алгоритм шифрования/дешифрования AES для обфускации/деобфускации данных. | |
| Ограничение по устройствам (необязательно) |
DeviceLimiter | Интерфейс, который вы реализуете, если хотите ограничить использование приложения определенным устройством. Вызывается из LicenseValidator. Реализация DeviceLimiter не рекомендуется для большинства приложений, поскольку для этого требуется бэкенд-сервер и пользователь может потерять доступ к лицензированным приложениям, если они не разработаны с осторожностью. |
| NullDeviceLimiter | Реализация DeviceLimiter по умолчанию, которая не выполняет никаких действий (разрешает доступ ко всем устройствам). | |
| Ядро библиотеки, интеграция не требуется | ResponseData | Класс, содержащий поля ответа на запрос лицензии. |
| LicenseValidator | Класс, который расшифровывает и проверяет ответ, полученный от сервера лицензирования. | |
| ValidationException | Класс, указывающий на ошибки, которые возникают при проверке целостности данных, управляемых обфускатором. | |
| PreferenceObfuscator | Вспомогательный класс, который записывает и считывает обфусцированные данные в хранилище SharedPreferences системы. |
|
| ILicensingService | Односторонний интерфейс IPC, через который запрос на проверку лицензии передается клиенту Google Play. | |
| ILicenseResultListener | Реализация одностороннего обратного вызова IPC, по которому приложение получает асинхронный ответ от сервера лицензирования. |
Ответ сервера
В таблице 2 перечислены все поля ответа, возвращаемые сервером лицензирования.
Таблица 2. Сводка полей ответа лицензии, возвращаемых сервером Google Play.
| Поле | Описание |
|---|---|
responseCode |
Код ответа, возвращенный сервером лицензирования. Коды ответов описаны в разделе Коды ответов сервера. |
signedData |
Объединенная строка, содержащая данные, возвращенные сервером лицензирования:
responseCode|nonce|packageName|versionCode|userId|timestamp:extras.
|
signature |
Подпись signedData с использованием ключа, предназначенного для конкретного приложения.
|
Коды ответов сервера
В таблице 3 перечислены все коды ответов, поддерживаемые сервером лицензирования. В общем случае приложение должно обрабатывать все эти коды ответов. По умолчанию класс LicenseValidator в LVL обеспечивает всю необходимую обработку этих кодов ответа.
Таблица 3. Сводка кодов ответов, возвращаемых сервером Google Play в ответе на запрос лицензии.
| Код ответа | Представление в виде целого числа | Описание | Подписано? | Дополнительный контент | Комментарии |
|---|---|---|---|---|---|
LICENSED |
0 |
Пользователь имеет лицензию на приложение. Пользователь купил приложение или имеет право скачать и установить его альфа- или бета-версию. | Да | VT, GT, GR |
Разрешить доступ в соответствии с ограничениями Policy. |
LICENSED_OLD_KEY |
2 |
У пользователя есть лицензия на приложение, но доступна обновленная версия, подписанная другим ключом. | Да | VT, GT, GR, UT |
При необходимости разрешите доступ в соответствии с ограничениями Policy.
Это может означать, что пара ключей, используемая установленной версией приложения, недействительна или скомпрометирована. Приложение может разрешить доступ, если это необходимо, или сообщить пользователю о доступности обновления и ограничить дальнейшее использование до обновления. |
NOT_LICENSED |
1 |
У пользователя нет лицензии на приложение. | Нет | Запретить доступ. | |
ERROR_CONTACTING_SERVER |
257 |
Локальная ошибка – приложению Google Play не удалось связаться с сервером лицензирования, возможно, из-за проблем с сетью. | Нет | Повторите проверку лицензии в соответствии с ограничениями Policy. |
|
ERROR_SERVER_FAILURE |
4 |
Ошибка сервера – серверу не удалось загрузить пару ключей приложения для лицензирования. | Нет | Повторите проверку лицензии в соответствии с ограничениями Policy.
|
|
ERROR_INVALID_PACKAGE_NAME |
258 |
Локальная ошибка – приложение запросило проверку лицензии для пакета, который не установлен на устройстве. | Нет | Не повторять проверку лицензии.
Обычно это связано с ошибкой разработки. |
|
ERROR_NON_MATCHING_UID |
259 |
Локальная ошибка. Приложение запросило проверку лицензии для пакета, UID которого (пара "пакет – идентификатор пользователя") не совпадает с UID запрашивающего приложения. | Нет | Не повторять проверку лицензии.
Обычно это связано с ошибкой разработки. |
|
ERROR_NOT_MARKET_MANAGED |
3 |
Ошибка сервера – приложение (название пакета) не распознано Google Play. | Нет | Не повторяйте проверку лицензии.
Может означать, что приложение было опубликовано не через Google Play или что при реализации лицензирования была допущена ошибка. |
Примечание. Как описано в разделе Настройка тестовой среды, код ответа можно вручную переопределить для разработчика приложения и любых зарегистрированных тестировщиков в Google Play Console.
Примечание. Раньше для тестирования приложения можно было загрузить неопубликованный черновик. Эта функция больше не поддерживается. Вместо этого вам нужно опубликовать приложение в альфа- или бета-канале распространения. Подробнее о прекращении поддержки черновиков приложений…
Дополнительная информация об ответе сервера
Чтобы помочь вашему приложению управлять доступом к нему в течение периода возврата средств и предоставить другую информацию, сервер лицензирования включает в ответы на запросы лицензии несколько фрагментов информации. В частности, сервис рекомендует значения для срока действия лицензии приложения, льготного периода повторной попытки, максимально допустимого количества повторных попыток и других настроек. Если в приложении используются файлы дополнений APK, в ответе также будут указаны их названия, размеры и URL. Сервер добавляет настройки в виде пар "ключ-значение" в поле extras ответа о лицензии.
Любая реализация Policy может извлечь дополнительные настройки из ответа на запрос лицензии и использовать их по мере необходимости. ServerManagedPolicy – это стандартная реализация Policy в LVL, которая показывает, как получать, хранить и использовать настройки.
Таблица 4. Сводка настроек управления лицензиями, предоставленных сервером Google Play в ответе на запрос лицензии.
| Дополнительно | Описание |
|---|---|
VT |
Временная метка срока действия лицензии. Указывает дату и время, когда срок действия текущего (кешированного) ответа лицензии истекает и его необходимо повторно проверить на сервере лицензирования. Подробнее о периоде действия лицензии… |
GT |
Временная метка льготного периода. Указывает на окончание периода, в течение которого правило может разрешать доступ к приложению, даже если статус ответа – RETRY. Значение управляется сервером, но обычно составляет 5 или более дней. Подробнее о периоде повторных попыток и их максимальном количестве… |
GR |
Максимальное количество повторных попыток. Указывает, сколько последовательных проверок лицензии RETRY должно разрешить приложение Policy, прежде чем запретить пользователю доступ к приложению.
Значение управляется сервером, но обычно оно равно 10 или больше. Подробнее о периоде повторных попыток и их максимальном количестве… |
UT |
Временная метка обновления. Дата и время загрузки и публикации последнего обновления приложения. Сервер возвращает это дополнительное поле только для ответов |
FILE_URL1 или FILE_URL2 |
URL файла дополнения (1 – основной файл, 2 – файл исправления). Используйте его, чтобы скачать файл по протоколу HTTP. |
FILE_NAME1 или FILE_NAME2 |
Название файла дополнения (1 – основной файл, 2 – файл исправления). При сохранении файла на устройстве необходимо использовать это имя. |
FILE_SIZE1 или FILE_SIZE2 |
Размер файла в байтах (1 – основной файл, 2 – файл исправления). Используйте его, чтобы упростить скачивание и убедиться, что в общем хранилище устройства достаточно места. |
Срок действия лицензии
Сервер лицензирования Google Play устанавливает период действия лицензии для всех скачанных приложений. Этот период представляет собой интервал времени, в течение которого статус лицензии приложения должен считаться неизменным и кэшируемым Policy лицензирования в приложении. Сервер лицензирования включает период действия в ответ на все проверки лицензии, добавляя в ответ временную метку окончания срока действия в качестве дополнительного параметра под ключом VT. Policy может извлечь значение ключа VT и использовать его, чтобы условно разрешить доступ к приложению без повторной проверки лицензии, пока не истечет период действия.
Срок действия лицензии указывает Policy, когда нужно повторно проверить статус лицензии на сервере лицензирования. Оно не означает, что у приложения есть лицензия на использование. Это означает, что по истечении периода действия лицензии приложение не перестает быть лицензированным, а лишь указывает на то, что Policy должен повторно проверить статус лицензии на сервере. Пока период действия лицензии не истек, Policy может кешировать исходный статус лицензии локально и возвращать его вместо отправки нового запроса на сервер.
Сервер лицензирования управляет сроком действия, чтобы помочь приложению правильно применять лицензирование в течение периода возврата средств, предлагаемого Google Play для платных приложений. Период действия устанавливается в зависимости от того, было ли приложение куплено, и если да, то как давно. В частности, сервер устанавливает период действия следующим образом:
- Для платных приложений сервер устанавливает начальный период действия лицензии, чтобы ответ на запрос лицензии оставался действительным в течение всего времени, когда за приложение можно вернуть средства.
Policyлицензирования в приложении может кешировать результат первоначальной проверки лицензии и не проверять ее повторно до истечения периода действия. - Если возврат средств за приложение уже невозможен, сервер устанавливает более длительный период действия – обычно несколько дней.
- Для бесплатного приложения сервер устанавливает очень большой период действия (
long.MAX_VALUE). Это гарантирует, что еслиPolicyсохранит временную метку в локальном кеше, то в будущем ему не потребуется повторно проверять статус лицензии приложения.
Реализация ServerManagedPolicy использует извлеченную временную метку (mValidityTimestamp) в качестве основного условия для определения того, следует ли повторно проверять статус лицензии на сервере, прежде чем разрешить пользователю доступ к приложению.
Период повторных попыток и их максимальное количество
В некоторых случаях из-за проблем с системой или сетью запрос на проверку лицензии может не дойти до сервера лицензирования или ответ сервера может не дойти до клиентского приложения Google Play. Например, пользователь может запустить приложение, когда мобильная сеть или подключение для передачи данных недоступны (например, в самолете), когда сетевое подключение нестабильно или сигнал сотовой связи слабый.
Если проблемы с сетью мешают проверке лицензии или прерывают ее, клиент Google Play уведомляет приложение, возвращая код ответа RETRY методу processServerResponse() интерфейса Policy. В случае системных проблем, например когда приложение не может связаться с реализацией ILicensingService в Google Play, библиотека LicenseChecker сама вызывает метод processServerResponse() правила с кодом ответа RETRY.
В целом код ответа RETRY сигнализирует приложению о том, что произошла ошибка, которая помешала завершить проверку лицензии.
Сервер Google Play помогает приложению управлять лицензированием в условиях ошибок, устанавливая льготный период повторных попыток и рекомендуемое максимальное количество повторных попыток. Сервер включает эти значения во все ответы на запросы проверки лицензии, добавляя их в качестве дополнительных данных под ключами GT и GR.
Приложение Policy может извлечь дополнительные параметры GT и GR и использовать их, чтобы разрешить доступ к приложению при соблюдении следующих условий:
- Если проверка лицензии возвращает ответ
RETRY,Policyдолжен кешировать код ответаRETRYи увеличить счетчик ответовRETRY. Policyдолжно разрешать пользователю доступ к приложению, если льготный период повторной попытки ещё не истек или максимальное количество повторных попыток не достигнуто.
ServerManagedPolicy использует значения GT и GR, предоставленные сервером, как описано выше. В примере ниже показана условная обработка ответов на повторные попытки в методе allow(). Количество ответов RETRY поддерживается в методе processServerResponse(), который не показан.
Kotlin
fun allowAccess(): Boolean { val ts = System.currentTimeMillis() return when(lastResponse) { LICENSED -> { // Check if the LICENSED response occurred within the validity timeout. ts <= validityTimestamp // Cached LICENSED response is still valid. } RETRY -> { ts < lastResponseTime + MILLIS_PER_MINUTE && // Only allow access if we are within the retry period // or we haven't used up our max retries. (ts <= retryUntil || retryCount <= maxRetries) } else -> false } }
Java
public boolean allowAccess() { long ts = System.currentTimeMillis(); if (lastResponse == LicenseResponse.LICENSED) { // Check if the LICENSED response occurred within the validity timeout. if (ts <= validityTimestamp) { // Cached LICENSED response is still valid. return true; } } else if (lastResponse == LicenseResponse.RETRY && ts < lastResponseTime + MILLIS_PER_MINUTE) { // Only allow access if we are within the retry period // or we haven't used up our max retries. return (ts <= retryUntil || retryCount <= maxRetries); } return false; }