Avertissement : Lorsque votre application lance la procédure de validation des licences côté client, les potentiels pirates informatiques peuvent plus facilement modifier ou supprimer la logique associée à cette procédure de validation.
C'est pourquoi nous vous encourageons vivement à valider vos licences côté serveur.
Après avoir configuré un compte d'éditeur et un environnement de développement (consultez Configurer la gestion des licences), vous pouvez ajouter la validation des licences à votre application à l'aide de la bibliothèque LVL (License Verification Library).
Pour ajouter une vérification des licences avec la bibliothèque LVL vous devez :
- ajouter l'autorisation de licence au fichier manifeste de votre application ;
- implémenter une règle. Vous pouvez choisir l'une des implémentations complètes fournies dans la bibliothèque LVL ou créer la vôtre ;
- implémenter un offuscateur si votre
Policy
met en cache les données de réponse de licence ; - ajouter du code pour vérifier la licence dans l'activité principale de votre application ;
- implémenter un DeviceLimiter (facultatif et déconseillé pour la plupart des applications).
Les sections ci-dessous détaillent les étapes précédemment citées. Une fois l'intégration terminée, vous devriez pouvoir compiler votre application et commencer à effectuer des tests, comme décrit dans la section configurer l'environnement de test.
Pour obtenir une présentation de l'ensemble complet des fichiers sources inclus dans la bibliothèque LVL, consultez la page résumé des classes et des interfaces de la bibliothèque LVL.
Ajouter l'autorisation de licence
Pour envoyer une vérification de licence au serveur à l'aide de l'application Google Play, celle-ci doit demander l'autorisation appropriée, com.android.vending.CHECK_LICENSE
. Si votre application ne déclare pas l'autorisation de licence, mais tente de lancer une vérification de licence, la bibliothèque LVL génère une exception de sécurité.
Pour demander l'autorisation de licence dans votre application, déclarez un élément <uses-permission>
en tant qu'enfant de l'élément <manifest>
comme suit :
<uses-permission
android:name="com.android.vending.CHECK_LICENSE" />
Par exemple, voici comment l'application exemple de la bibliothèque LVL déclare l'autorisation :
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" ..."> <!-- Devices >= 3 have version of Google Play that supports licensing. --> <uses-sdk android:minSdkVersion="3" /> <!-- Required permission to check licensing. --> <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> ... </manifest>
Remarque : Pour le moment, vous ne pouvez pas déclarer l'autorisation CHECK_LICENSE
dans le fichier manifeste de la bibliothèque LVL du projet, car les SDK Tools ne la fusionneront pas avec les fichiers manifestes des applications dépendantes. Vous devez donc déclarer l'autorisation dans le fichier manifeste de chaque application dépendante.
Implémenter une stratégie
Le service de gestion des licences de Google Play ne détermine pas lui-même si un utilisateur donné avec une licence donnée doit avoir accès à votre application.
Cette responsabilité incombe plutôt à une implémentation Policy
que vous faites dans votre application.
Policy est une interface déclarée par la bibliothèque LVL conçue pour contenir la logique de votre application pour autoriser ou interdire l'accès des utilisateurs, en fonction du résultat de la vérification des licences. Pour utiliser la bibliothèque LVL, votre application doit fournir une implémentation de Policy
.
L'interface Policy
déclare deux méthodes, allowAccess()
et processServerResponse()
, qui sont appelées par une instance LicenseChecker
lors du traitement d'une réponse du serveur de licences. L'interface déclare également une énumération nommée LicenseResponse
, qui spécifie la valeur de réponse de la licence transmise lors des appels à processServerResponse()
.
processServerResponse()
vous permet de prétraiter les données de réponse brutes reçues du serveur de gestion des licences, avant de déterminer si l'accès doit être accordé ou non.Une implémentation classique consisterait à extraire tout ou partie des champs de la réponse de licence et à stocker les données localement dans un stockage persistant, par exemple via le stockage
SharedPreferences
, pour garantir que les données sont accessibles lors des appels de l'application et cycles d'alimentation de l'appareil. Par exemple, unePolicy
(règle) préserverait le code temporel de la dernière vérification réussie de licence, du nombre de tentatives, de la période de validité de la licence et d'autres informations similaires dans un stockage persistant, plutôt que de réinitialiser les valeurs à chaque lancement de l'application.Lorsque vous stockez des données de réponse localement,
Policy
doit s'assurer que les données sont obscurcies (voir la section implémenter un offuscateur ci-dessous).allowAccess()
détermine si l'utilisateur doit être autorisé à accéder à votre application, en fonction des données de réponse de licence disponibles (à partir du serveur de gestion des licences ou du cache) ou à d'autres informations spécifiques à l'application. Par exemple, votre implémentation d'allowAccess()
peut prendre en compte des critères supplémentaires, tels que l'utilisation ou d'autres données récupérées sur un serveur backend. Dans tous les cas, une implémentation d'allowAccess()
ne doit renvoyertrue
(vrai) que si l'utilisateur est autorisé à utiliser l'application, comme déterminé par le serveur de gestion des licences, ou en cas de problèmes transitoires liés au réseau ou au système et qui empêchent la vérification des licences. Dans ce cas, votre implémentation peut lister le nombre de tentatives de réponses et autoriser provisoirement l'accès jusqu'à ce que la prochaine vérification de licence soit terminée.
Pour simplifier le processus d'ajout de licence à votre application et montrer comment concevoir une Policy
, la bibliothèque LVL comprend deux implémentations complètes de Policy
, que vous pouvez utiliser telles quelles ou adapter à vos besoins :
- ServerManagedPolicy, une
Policy
flexible qui utilise les paramètres fournis par le serveur et les réponses mises en cache pour gérer l'accès dans différentes conditions de réseau ; et - StrictPolicy, option qui ne met en cache aucune donnée de réponse et autorise l'accès uniquement si le serveur renvoie une réponse sous licence.
Pour la plupart des applications, il est vivement recommandé d'utiliser ServerManagedPolicy. ServerManagedPolicy est la bibliothèque LVL par défaut et est intégré à l'application exemple de la bibliothèque LVL.
Consignes concernant les règles personnalisées
Dans votre implémentation de la licence, vous pouvez utiliser l'une des règles complètes fournies dans la bibliothèque LVL (ServerManagedPolicy ou StrictPolicy) ou créer une règle personnalisée. Quel que soit le type de règle personnalisée, vous devez comprendre certains points importants et les prendre en compte lors de l'implémentation.
Le serveur de gestion des licences applique des limites de requêtes générales pour éviter toute utilisation excessive des ressources pouvant entraîner un déni de service. Lorsqu'une application dépasse la limite de requêtes, le serveur de gestion des licences renvoie une réponse 503, qui est transmise à votre application en tant qu'erreur générale du serveur. Cela signifie qu'aucune réponse de licence ne sera disponible pour l'utilisateur jusqu'à la réinitialisation de la limite, ce qui peut affecter l'utilisateur pendant une période indéfinie.
Si vous concevez une règle personnalisée, nous vous recommandons que Policy
:
- Mette en cache (et obscurcisse correctement) la réponse de licence réussie la plus récente dans le stockage persistant local.
- Renvoie la réponse mise en cache pour toutes les vérifications de licence, tant que la réponse mise en cache est valide, au lieu d'envoyer une requête au serveur de gestion des licences.
Il est vivement recommandé de définir la validité de la réponse en fonction du
VT
extra fourni par le serveur. Pour en savoir plus, consultez la section Réponses du serveur : extras. - Utilise un intervalle exponentiel entre les tentatives. Si vous relancez une requête, cela génère des erreurs. Notez que le client Google Play relance automatiquement les requêtes ayant échoué. Dans la plupart des cas, il n'est donc pas nécessaire que votre
Policy
ne le fasse. - Fournisse un "délai de grâce" permettant à l'utilisateur d'accéder à votre application pendant une durée limitée ou pendant un nombre limité d'utilisations, pendant qu'une vérification des licences est en cours. Le délai de grâce profite à l'utilisateur en autorisant l'accès jusqu'à la fin de la vérification des licences. Il vous permet en outre de limiter l'accès à votre application lorsqu'aucune réponse de licence valide n'est disponible.
Il est essentiel de concevoir votre Policy
conformément aux indications ci-dessus, car elle garantit une expérience optimale aux utilisateurs tout en vous permettant de contrôler efficacement votre application, même en cas d'erreur.
Notez que toutes les Policy
peuvent utiliser les paramètres fournis par le serveur de gestion des licences pour faciliter la gestion de la validité et de la mise en cache, relancer un délai de grâce, etc. L'extraction des paramètres fournis par le serveur est simple. Il est vivement recommandé de les utiliser. Reportez-vous à l'implémentation de ServerManagedPolicy pour obtenir un exemple illustrant comment extraire et utiliser les extras. Pour obtenir la liste des paramètres du serveur et savoir comment les utiliser, consultez la section Réponses du serveur : extras.
ServerManagedPolicy
Ce fichier comprend une implémentation complète et recommandée de l'interface dePolicy
appelée ServerManagedPolicy. L'implémentation est intégrée aux classes de la bibliothèque LVL et sert de Policy
par défaut dans la bibliothèque.
ServerManagedPolicy assure tout le traitement des réponses de licence et de relance. Il met en cache toutes les données de réponse localement dans un fichier SharedPreferences
, en les obscurcissant avec l'implémentation de l'Obfuscator
(offuscateur) de l'application. Cela garantit que les données de réponse de licence sont sécurisées et persistantes pendant les cycles d'alimentation de l'appareil. ServerManagedPolicy fournit des implémentations concrètes des méthodes d'interface processServerResponse()
et allowAccess()
, et inclut également un ensemble de méthodes et de types pris en charge pour gérer les réponses aux licences.
Il est important de noter que l'une des principales fonctionnalités de ServerManagedPolicy est l'utilisation de paramètres fournis par le serveur comme base pour la gestion des licences pendant la période de remboursement d'une application et dans des conditions de réseau instables et d'erreur.
Lorsqu'une application contacte le serveur Google Play pour une vérification de licence, le serveur ajoute plusieurs paramètres en tant que paires clé/valeur dans le champ extras de certains types de réponses de licence. Par exemple, le serveur fournit, entre autres, des valeurs recommandées pour la période de validité de la licence de l'application, le délai de grâce pour les relances et le nombre maximal de relances. ServerManagedPolicy extrait les valeurs de la réponse de licence dans sa méthode processServerResponse()
et les vérifie dans sa méthode allowAccess()
. Pour obtenir la liste des paramètres fournis par le serveur utilisés par ServerManagedPolicy, consultez la section Réponses du serveur : extras.
Pour plus de commodité, des performances optimales et les avantages liés à l'utilisation des paramètres de licence du serveur Google Play, nous vous recommandons vivement d'utiliser ServerManagedPolicy en tant que Policy
de licence.
Si vous êtes préoccupé par la sécurité des données de réponse de licence stockées localement dans SharedPreferences
, vous pouvez utiliser un algorithme d'obscurcissement plus puissant ou concevoir une Policy
plus stricte qui ne stocke pas de données de licence. La bibliothèque LVL comprend un exemple de Policy
. Pour en savoir plus, consultez la section StrictPolicy.
Pour utiliser ServerManagedPolicy, il vous suffit de l'importer dans votre activité, de créer une instance et de transmettre une référence à celle-ci lors de la création de votre LicenseChecker
. Pour en savoir plus, consultez Instancier LicenseChecker et LicenseCheckerCallback.
StrictPolicy
La bibliothèque LVL inclut une autre implémentation complète de l'interface Policy
appelée StrictPolicy. L'implémentation de StrictPolicy fournit une règle plus restrictive que ServerManagedPolicy, en ce sens qu'elle ne permet pas à l'utilisateur d'accéder à l'application, sauf si le serveur reçoit une réponse de licence au moment de l'accès indiquant que l'utilisateur dispose d'une licence.
La fonctionnalité principale de StrictPolicy est qu'elle ne stocke aucune donnée de réponse de licence en local, dans un stockagepersistant. Étant donné qu'aucune donnée n'est stockée, les requêtes de nouvelle tentative ne sont pas suivies et les réponses mises en cache ne peuvent pas être utilisées pour effectuer des vérifications de licence. Policy
n'autorise l'accès que si :
- La réponse à la licence provient du serveur de gestion des licences, et
- La réponse de la licence indique que l'utilisateur est autorisé à accéder à l'application.
L'utilisation de StrictPolicy est appropriée si votre principale préoccupation est de vous assurer, dans tous les cas possibles, qu'aucun utilisateur ne sera autorisé à accéder à l'application, sauf s'il est confirmé qu'il dispose d'une licence au moment de l'utilisation. En outre, StrictPolicy offre un niveau de sécurité légèrement supérieur à celui de ServerManagedPolicy, car aucune donnée n'est mise en cache localement. De ce fait, aucun utilisateur malveillant ne peut manipuler les données mises en cache et accéder à l'application.
En même temps, cette Policy
pose problème aux utilisateurs normaux, car cela signifie qu'ils ne pourront pas accéder à l'application en l'absence de connexion réseau (cellulaire ou Wi-Fi) disponible. Un autre effet indirect est le fait que votre application enverra plus de requêtes de vérification de licence au serveur, car l'utilisation d'une réponse mise en cache n'est pas possible.
Dans l'ensemble, cette stratégie représente un compromis entre confort et sécurité absolue pour les utilisateurs et contrôle de leurs accès. Réfléchissez bien à ce compromis avant d'utiliser cette Policy
.
Pour utiliser StrictPolicy, importez-la simplement dans votre activité, créez une instance et transmettez-lui une référence lorsque vous créez votre LicenseChecker
. Pour en savoir plus, consultez Instancier LicenseChecker et LicenseCheckerCallback.
Une implémentation type de Policy
doit enregistrer les données de réponse de licence d'une application dans un stockage persistant, afin qu'elles soient accessibles au fil des appels d'application et des cycles d'alimentation de l'appareil. Par exemple, une Policy
(règle) préserverait le code temporel de la dernière vérification réussie de licence, du nombre de tentatives, de la période de validité de la licence et d'autres informations similaires dans un stockage persistant, plutôt que de réinitialiser les valeurs à chaque lancement de l'application. La Policy
par défaut incluse dans la bibliothèque LVL, ServerManagedPolicy, stocke les données de réponse de licence dans une instance SharedPreferences
, afin de garantir la persistance des données.
Étant donné que Policy
utilisera les données de réponse de licence stockées pour déterminer si l'application doit être autorisée ou non, elle doit s'assurer que toutes les données stockées sont sécurisées et ne peuvent pas être réutilisées ou manipulées par un utilisateur racine sur un appareil. Plus précisément, la Policy
doit toujours obscurcir les données avant de les stocker, à l'aide d'une clé unique pour l'application et l'appareil. Il est essentiel d'obscurcir à l'aide d'une clé spécifique à une application ou à un appareil, car cela évite que les données obscurcies soient partagées entre plusieurs applications et appareils.
La bibliothèque LVL aide l'application à stocker ses données de réponse de licence de manière sécurisée et persistante. Tout d'abord, elle fournit une interface Obfuscator
qui permet à votre application de fournir l'algorithme d'obscurcissement de son choix pour les données stockées. Sur la base de celui-ci, la bibliothèque LVL fournit la classe PreferenceObfuscator, qui gère la plupart du travail d'appel de la classe Obfuscator
de l'application, ainsi que de lecture et d'écriture des données obscurcies dans une instance SharedPreferences
.
Cette bibliothèque offre une implémentation complète de Obfuscator
appelée AESObfuscator, qui utilise le chiffrement AES pour obscurcir les données. Vous pouvez utiliser AESObfuscator dans votre application sans le modifier ou bien en l'adaptant à vos besoins. Si vous utilisez une Policy
(telle que ServerManagedPolicy) qui met en cache les données de réponse de licence, nous vous recommandons vivement d'utiliser l'algorithme AESObfuscator pour votre implémentation de Obfuscator
.
Pour en savoir plus, consultez la section suivante.
AESObfuscator
Ce fichier comprend une implémentation complète et recommandée de l'interface d'Obfuscator
appelée AESObfuscator. L'implémentation est intégrée à l'application exemple de la bibliothèque LVL et sert de Obfuscator
par défaut dans la bibliothèque.
AESObfuscator fournit un obscurcissement sécurisé des données en utilisant AES pour chiffrer et déchiffrer les données au moment de leur écriture ou de leur lecture dans l'espace de stockage.
L'Obfuscator
partage le chiffrement à l'aide de trois champs de données fournis par l'application :
- Un salage : tableau d'octets aléatoires à utiliser pour chaque (dés)obscurcissement.
- Une chaîne d'identifiant d'application, généralement le nom de package de l'application.
- Une chaîne d'identifiant d'appareil, issue d'un maximum de sources propres à chaque appareil, afin de la rendre unique.
Pour utiliser AESObfuscator, commencez par l'importer dans votre activité. Déclarez un tableau final statique privé pour contenir les octets de salage et initialisez-le avec 20 octets générés de manière aléatoire.
Kotlin
// Generate 20 random bytes, and put them here. private val SALT = byteArrayOf( -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64, 89 )
Java
... // Generate 20 random bytes, and put them here. private static final byte[] SALT = new byte[] { -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95, -45, 77, -117, -36, -113, -11, 32, -64, 89 }; ...
Ensuite, déclarez une variable destinée à contenir un identifiant d'appareil et à générer une valeur pour celle-ci, si nécessaire. Par exemple, l'exemple d'application inclus dans la bibliothèque LVL interroge les paramètres système pour l'android.Settings.Secure.ANDROID_ID
, qui est unique à chaque appareil.
Notez qu'en fonction des API que vous utilisez, votre application devra peut-être demander des autorisations supplémentaires afin d'obtenir des informations spécifiques à l'appareil.
Par exemple, pour interroger le TelephonyManager
afin d'obtenir le code IMEI de l'appareil ou les données associées, l'application doit également demander l'autorisation android.permission.READ_PHONE_STATE
dans son fichier manifeste.
Avant de demander de nouvelles autorisations dans le seul objectif d'acquérir des informations spécifiques à l'appareil pour votre Obfuscator
, réfléchissez à la manière dont cela pourrait affecter votre application ou son filtrage sur Google Play. (car certaines autorisations peuvent entraîner l'ajout des <uses-feature>
associés par les outils de compilation du SDK).
Enfin, construisez une instance d'AESObfuscator en transmettant le salage, l'identifiant d'application et l'identifiant d'appareil. Vous pouvez construire l'instance directement, tout en construisant Policy
et LicenseChecker
. Exemple :
Kotlin
... // Construct the LicenseChecker with a Policy. private val checker = LicenseChecker( this, ServerManagedPolicy(this, AESObfuscator(SALT, packageName, deviceId)), BASE64_PUBLIC_KEY ) ...
Java
... // Construct the LicenseChecker with a Policy. checker = new LicenseChecker( this, new ServerManagedPolicy(this, new AESObfuscator(SALT, getPackageName(), deviceId)), BASE64_PUBLIC_KEY // Your public licensing key. ); ...
Pour obtenir un exemple complet, consultez MainActivity dans l'application exemple de la bibliothèque LVL.
Vérifier la licence associée à une activité
Une fois que vous avez implémenté une Policy
pour gérer l'accès à votre application, l'étape suivante consiste à ajouter un contrôle de licence à votre application, qui lance une requête au serveur de gestion des licences si nécessaire et gère l'accès à l'application en fonction de la réponse obtenue. Toutes les opérations d'ajout de la vérification de licence et de gestion de la réponse sont effectuées dans votre fichier source activité (Activity
) principale.
Pour ajouter la vérification des licences et gérer la réponse, vous devez :
- Ajouter des importations
- Implémenter LicenseCheckerCallback en tant que classe interne privée
- Créer un gestionnaire pour la publication depuis LicenseCheckerCallback vers le thread UI
- Instancier LicenseChecker et LicenseCheckerCallback
- Appeler checkAccess() pour lancer la vérification des licences
- Intégrer votre clé publique pour l'attribution des licences
- Appeler la méthode onDestroy() de la fonction LicenseChecker pour fermer les connexions IPC.
Les sections ci-dessous détaillent les étapes précédemment citées.
Présentation de la vérification des licences et des réponses
Dans la plupart des cas, vous devez ajouter la vérification des licences à l'Activity
principale de votre application, dans la méthode onCreate()
. Ainsi, lorsque l'utilisateur lance directement votre application, la vérification de licence est immédiatement appelée. Dans certains cas, vous pouvez également ajouter des vérifications de licence à d'autres emplacements. Par exemple, si votre application inclut plusieurs composants "Activity" que d'autres applications peuvent démarrer depuis Intent
, vous pouvez ajouter des vérifications de licence dans ces activités.
La vérification des licences se compose de deux actions principales :
- Un appel à une méthode pour lancer la vérification de licence : dans la bibliothèque LVL, il s'agit d'un appel à la méthode
checkAccess()
d'un objetLicenseChecker
que vous créez. - Rappel qui renvoie le résultat de la vérification des licences. Dans la bibliothèque LVL, il s'agit d'une interface
LicenseCheckerCallback
que vous implémentez. L'interface déclare deux méthodes,allow()
etdontAllow()
, qui sont appelées par la bibliothèque en fonction du résultat de la vérification des licences. Vous implémentez ces deux méthodes avec la logique dont vous avez besoin pour autoriser ou interdire à l'utilisateur l'accès à votre application. Notez que ces méthodes ne déterminent pas s'il faut autoriser l'accès. Cette détermination relève de votre implémentation dePolicy
. Ces méthodes fournissent simplement les comportements de l'application indiquant comment autoriser et interdire l'accès (et gérer les erreurs d'application).Les méthodes
allow()
etdontAllow()
fournissent un "motif" pour leur réponse, qui peut être l'une des valeursPolicy
,LICENSED
,NOT_LICENSED
ouRETRY
. Vous devez plus particulièrement gérer le cas où la méthode reçoit la réponseRETRY
pourdontAllow()
et fournir à l'utilisateur un bouton "Réessayer", ce qui peut être dû au fait que le service était indisponible au moment de la demande.
Le schéma ci-dessus illustre le processus typique de vérification des licences :
- Le code présent dans l'activité principale de l'application instancie les objets
LicenseCheckerCallback
etLicenseChecker
. Lors de la création deLicenseChecker
, le code transmetContext
, une implémentation dePolicy
à utiliser et la clé publique du compte d'éditeur pour les licences en tant que paramètres. - Le code appelle ensuite la méthode
checkAccess()
sur l'objetLicenseChecker
. L'implémentation de la méthode appelle laPolicy
pour déterminer s'il existe une réponse de licence valide mise en cache localement,dansSharedPreferences
.- Si tel est le cas, l'implémentation de
checkAccess()
appelleallow()
. - Sinon,
LicenseChecker
lance une requête de vérification de licence au serveur de gestion des licences.
Remarque : Le serveur de gestion des licences renvoie toujours
LICENSED
lorsque vous vérifiez la licence d'une version préliminaire. - Si tel est le cas, l'implémentation de
- Lorsqu'une réponse est reçue,
LicenseChecker
crée un outil LicenseValidator qui valide les données de licence signées et extrait les champs de la réponse, puis les transmet à votrePolicy
pour une évaluation plus approfondie.- Si la licence est valide,
Policy
met en cache la réponse dansSharedPreferences
et en informe le validateur, qui appelle ensuite la méthodeallow()
sur l'objetLicenseCheckerCallback
. - Si la licence n'est pas valide,
Policy
en informe le validateur, qui appelle la méthodedontAllow()
surLicenseCheckerCallback
.
- Si la licence est valide,
- En cas d'erreur locale ou de serveur récupérable, par exemple lorsque le réseau n'est pas disponible pour envoyer la requête,
LicenseChecker
passe une réponseRETRY
à la méthodeprocessServerResponse()
de votre objetPolicy
.Les méthodes de rappel
allow()
etdontAllow()
reçoivent également un argumentreason
. Le motif de la méthodeallow()
est généralementPolicy.LICENSED
ouPolicy.RETRY
, et le motifdontAllow()
est généralementPolicy.NOT_LICENSED
ouPolicy.RETRY
. Ces valeurs de réponse sont utiles pour afficher une réponse appropriée à l'utilisateur, par exemple en fournissant un bouton "Réessayer" lorsquedontAllow()
répond parPolicy.RETRY
, ce qui peut être dû au fait que le service était indisponible. - En cas d'erreur d'application, par exemple lorsque l'application tente de vérifier la licence d'un nom de package non valide,
LicenseChecker
transmet une réponse d'erreur à la méthodeapplicationError()
de LicenseCheckerCallback.
Notez qu'en plus de lancer la vérification des licences et de gérer le résultat, décrit dans les sections ci-dessous, votre application doit également fournir une implémentation de la règle et, si le Policy
stocke des données de réponse (telles que ServerManagedPolicy), elle doit fournir une implémentation d'offuscateur.
Ajouter des importations
Commencez par ouvrir le fichier de classe de l'activité principale de l'application, puis importez LicenseChecker
et LicenseCheckerCallback
à partir du package LVL.
Kotlin
import com.google.android.vending.licensing.LicenseChecker import com.google.android.vending.licensing.LicenseCheckerCallback
Java
import com.google.android.vending.licensing.LicenseChecker; import com.google.android.vending.licensing.LicenseCheckerCallback;
Si vous utilisez l'implémentation par défaut de Policy
fournie avec la bibliothèque LVL, ServerManagedPolicy, importez-la également avec l'AESObfuscator. Si vous utilisez des Policy
ou Obfuscator
personnalisés, importez-les à la place.
Kotlin
import com.google.android.vending.licensing.ServerManagedPolicy import com.google.android.vending.licensing.AESObfuscator
Java
import com.google.android.vending.licensing.ServerManagedPolicy; import com.google.android.vending.licensing.AESObfuscator;
Implémenter LicenseCheckerCallback en tant que classe interne privée
LicenseCheckerCallback
est une interface fournie par la bibliothèque LVL pour gérer le résultat d'une vérification de licence. Pour accepter les licences à l'aide de la bibliothèque LVL, vous devez implémenter LicenseCheckerCallback
et ses méthodes pour autoriser ou interdire l'accès à l'application.
Le résultat d'une vérification de licence est toujours un appel à l'une des méthodes LicenseCheckerCallback
, effectué à partir de la validation de la charge utile de réponse, du code de réponse du serveur lui-même et de toute opération supplémentaire effectuée par votre Policy
. Votre application peut implémenter ces méthodes comme vous le souhaitez. En général, il est préférable d'utiliser des méthodes simples, en les limitant à la gestion de l'état de l'interface utilisateur et de l'accès aux applications. Si vous souhaitez ajouter un traitement supplémentaire aux réponses de licence, par exemple en contactant un serveur backend ou en appliquant des contraintes personnalisées, vous pouvez envisager d'intégrer ce code dans votre Policy
au lieu de le placer dans les méthodes LicenseCheckerCallback
.
Dans la plupart des cas, vous devez déclarer votre implémentation de LicenseCheckerCallback
en tant que classe privée dans la classe d'activité principale de votre application.
Implémentez les méthodes allow()
et dontAllow()
si nécessaire. Pour commencer, vous pouvez utiliser de simples comportements de gestion des résultats dans les méthodes, comme afficher le résultat de la licence dans une boîte de dialogue. Cela vous permet d'exécuter votre application plus rapidement et peut faciliter le débogage. Plus tard, après avoir déterminé les comportements précis que vous souhaitez, vous pouvez ajouter une gestion plus complexe.
Voici quelques suggestions pour gérer les réponses sans licence dans dontAllow()
:
- Afficher une boîte de dialogue "Réessayer" incluant un bouton pour lancer une nouvelle vérification de licence si le motif (
reason
) fourni estPolicy.RETRY
- Afficher une boîte de dialogue "Acheter cette application", avec un bouton qui redirige l'utilisateur vers la page d'informations de l'application sur Google Play, à partir de laquelle il peut acheter l'application Pour en savoir plus sur la configuration de ces liens, consultez la page Associer vos produits.
- Afficher une notification toast indiquant que les fonctionnalités de l'application sont limitées, car elle ne dispose pas de licence
L'exemple ci-dessous montre comment l'application exemple de la bibliothèque LVL implémente LicenseCheckerCallback
, avec des méthodes qui affichent le résultat de la vérification de licence dans une boîte de dialogue.
Kotlin
private inner class MyLicenseCheckerCallback : LicenseCheckerCallback { override fun allow(reason: Int) { if (isFinishing) { // Don't update UI if Activity is finishing. return } // Should allow user access. displayResult(getString(R.string.allow)) } override fun dontAllow(reason: Int) { if (isFinishing) { // Don't update UI if Activity is finishing. return } displayResult(getString(R.string.dont_allow)) if (reason == Policy.RETRY) { // If the reason received from the policy is RETRY, it was probably // due to a loss of connection with the service, so we should give the // user a chance to retry. So show a dialog to retry. showDialog(DIALOG_RETRY) } else { // Otherwise, the user isn't licensed to use this app. // Your response should always inform the user that the application // isn't licensed, but your behavior at that point can vary. You might // provide the user a limited access version of your app or you can // take them to Google Play to purchase the app. showDialog(DIALOG_GOTOMARKET) } } }
Java
private class MyLicenseCheckerCallback implements LicenseCheckerCallback { public void allow(int reason) { if (isFinishing()) { // Don't update UI if Activity is finishing. return; } // Should allow user access. displayResult(getString(R.string.allow)); } public void dontAllow(int reason) { if (isFinishing()) { // Don't update UI if Activity is finishing. return; } displayResult(getString(R.string.dont_allow)); if (reason == Policy.RETRY) { // If the reason received from the policy is RETRY, it was probably // due to a loss of connection with the service, so we should give the // user a chance to retry. So show a dialog to retry. showDialog(DIALOG_RETRY); } else { // Otherwise, the user isn't licensed to use this app. // Your response should always inform the user that the application // isn't licensed, but your behavior at that point can vary. You might // provide the user a limited access version of your app or you can // take them to Google Play to purchase the app. showDialog(DIALOG_GOTOMARKET); } } }
De plus, vous devez implémenter la méthode applicationError()
, que la bibliothèque LVL appelle pour permettre à votre application de gérer les erreurs qui ne peuvent pas faire l'objet d'une nouvelle tentative. Pour obtenir la liste de ces erreurs, consultez la section Codes de réponse du serveur dans la documentation de référence sur la gestion des licences. Vous pouvez implémenter la méthode comme vous le souhaitez. Dans la plupart des cas, la méthode doit consigner le code d'erreur et appeler dontAllow()
.
Créer un gestionnaire pour la publication depuis LicenseCheckerCallback vers le thread UI
Lors d'une vérification de licence, la bibliothèque LVL transmet la requête à l'application Google Play, qui gère la communication avec le serveur de gestion des licences. Ce dernier transmet la requête via l'IPC asynchrone (à l'aide de Binder
). Par conséquent, le traitement et la communication réseau ne sont pas effectués sur un thread géré par votre application. De même, lorsque l'application Google Play reçoit le résultat, elle appelle une méthode de rappel sur IPC, qui s'exécute à son tour dans un pool de threads IPC dans le processus de votre application.
La classe LicenseChecker
gère la communication IPC de votre application avec l'application Google Play, y compris l'appel qui envoie la requête et le rappel qui reçoit la réponse. LicenseChecker
suit également les requêtes de licence ouvertes et gère leurs délais avant expiration.
Pour pouvoir gérer correctement les délais avant expiration et traiter les réponses entrantes sans affecter le thread UI de votre application, LicenseChecker
génère un thread d'arrière-plan lors de l'instanciation. Dans le thread, il traite les résultats de la vérification des licences, qu'il s'agisse d'une réponse envoyée par le serveur ou d'une erreur d'expiration de délai. À la fin du traitement, la bibliothèque LVL appelle vos méthodes LicenseCheckerCallback
à partir du thread d'arrière-plan.
Pour votre application, cela signifie que :
- Vos méthodes
LicenseCheckerCallback
seront invoquées dans de nombreux cas à partir d'un thread d'arrière-plan. - Ces méthodes ne pourront pas mettre à jour l'état ni appeler de traitement dans le thread UI, sauf si vous créez un gestionnaire dans ce dernier et que vos méthodes de rappel y sont publiées.
Si vous souhaitez que vos méthodes LicenseCheckerCallback
mettent à jour le thread UI, instanciez un Handler
(gestionnaire) dans la méthode onCreate()
de l'activité principale, comme indiqué ci-dessous. Dans cet exemple, les méthodes LicenseCheckerCallback
de l'application exemple de la bibliothèque LVL (voir ci-dessus) appellent displayResult()
pour mettre à jour le thread UI via la méthode post()
du gestionnaire.
Kotlin
private lateinit var handler: Handler override fun onCreate(savedInstanceState: Bundle?) { ... handler = Handler() }
Java
private Handler handler; @Override public void onCreate(Bundle savedInstanceState) { ... handler = new Handler(); }
Ensuite, dans vos méthodes LicenseCheckerCallback
, vous pouvez utiliser les méthodes du gestionnaire pour publier des exécutables ou des objets de message. Voici comment l'exemple d'application inclus dans la bibliothèque LVL publie un exécutable dans un gestionnaire dans le thread UI pour afficher l'état de la licence.
Kotlin
private fun displayResult(result: String) { handler.post { statusText.text = result setProgressBarIndeterminateVisibility(false) checkLicenseButton.isEnabled = true } }
Java
private void displayResult(final String result) { handler.post(new Runnable() { public void run() { statusText.setText(result); setProgressBarIndeterminateVisibility(false); checkLicenseButton.setEnabled(true); } }); }
Instancier LicenseChecker et LicenseCheckerCallback
Dans la méthode onCreate()
de l'activité principale, créez des instances privées de LicenseCheckerCallback et LicenseChecker
. Vous devez d'abord instancier LicenseCheckerCallback
, car vous devez transmettre une référence à cette instance lorsque vous appelez le constructeur de LicenseChecker
.
Lorsque vous instanciez LicenseChecker
, vous devez transmettre les paramètres suivants :
- Le
Context
de l'application - Une référence à l'implémentation de
Policy
à utiliser pour la vérification des licences. Dans la plupart des cas, vous devez utiliser l'implémentation par défaut dePolicy
fournie par la bibliothèque LVL, ServerManagedPolicy. - La variable String (chaîne) contenant la clé publique de votre compte d'éditeur pour les licences.
Si vous utilisez ServerManagedPolicy, vous n'aurez pas besoin d'accéder directement à la classe pour l'instancier. Vous pouvez le faire directement dans le constructeur LicenseChecker
, comme illustré dans l'exemple ci-dessous. Notez que vous devez transmettre une référence à une nouvelle instance d'offuscateur lorsque vous construisez ServerManagedPolicy.
L'exemple ci-dessous montre l'instanciation de LicenseChecker
et de LicenseCheckerCallback
à partir de la méthode onCreate()
d'une classe Activity.
Kotlin
class MainActivity : AppCompatActivity() { ... private lateinit var licenseCheckerCallback: LicenseCheckerCallback private lateinit var checker: LicenseChecker override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Construct the LicenseCheckerCallback. The library calls this when done. licenseCheckerCallback = MyLicenseCheckerCallback() // Construct the LicenseChecker with a Policy. checker = LicenseChecker( this, ServerManagedPolicy(this, AESObfuscator(SALT, packageName, deviceId)), BASE64_PUBLIC_KEY // Your public licensing key. ) ... } }
Java
public class MainActivity extends Activity { ... private LicenseCheckerCallback licenseCheckerCallback; private LicenseChecker checker; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Construct the LicenseCheckerCallback. The library calls this when done. licenseCheckerCallback = new MyLicenseCheckerCallback(); // Construct the LicenseChecker with a Policy. checker = new LicenseChecker( this, new ServerManagedPolicy(this, new AESObfuscator(SALT, getPackageName(), deviceId)), BASE64_PUBLIC_KEY // Your public licensing key. ); ... } }
Notez que LicenseChecker
n'appelle les méthodes LicenseCheckerCallback
à partir du thread UI que si une réponse de licence valide est mise en cache localement. Si la vérification de licence est envoyée au serveur, les rappels proviennent toujours du thread en arrière-plan, même en cas d'erreurs réseau.
Appeler checkAccess() pour lancer la vérification des licences
Dans votre activité principale, ajoutez un appel à la méthode checkAccess()
de l'instance LicenseChecker
. Dans l'appel, transmettez une référence à votre instance LicenseCheckerCallback
en tant que paramètre. Si vous devez gérer des effets spéciaux dans l'interface utilisateur ou des états avant l'appel, il peut être utile d'appeler checkAccess()
à partir d'une méthode de type wrapper. Par exemple, l'application exemple de la bibliothèque LVL appelle checkAccess()
à partir d'une méthode de type wrapper doCheck()
:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... // Call a wrapper method that initiates the license check doCheck() ... } ... private fun doCheck() { checkLicenseButton.isEnabled = false setProgressBarIndeterminateVisibility(true) statusText.setText(R.string.checking_license) checker.checkAccess(licenseCheckerCallback) }
Java
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... // Call a wrapper method that initiates the license check doCheck(); ... } ... private void doCheck() { checkLicenseButton.setEnabled(false); setProgressBarIndeterminateVisibility(true); statusText.setText(R.string.checking_license); checker.checkAccess(licenseCheckerCallback); }
Intégrer votre clé publique pour l'attribution des licences
Pour chaque application, le service Google Play génère automatiquement une paire de clés publique/privée RSA de 2 048 bits utilisée pour les licences et la facturation des achats in-app. La paire de clés est associée de manière unique à l'application. Bien qu'elle soit associée à l'application, la paire de clés est différente de la clé que vous utilisez pour signer vos applications (ou dérivée de celle-ci).
La Google Play Console expose la clé publique d'attribution de licence à tout développeur connecté à la Play Console, mais elle la masque à tous les utilisateurs dans un emplacement sécurisé. Lorsqu'une application demande une vérification des licences d'une application publiée dans votre compte, le serveur de gestion des licences signe la réponse de licence à l'aide de la clé privée de la paire de clés de votre application. Lorsque la bibliothèque LVL reçoit la réponse, elle utilise la clé publique fournie par l'application pour vérifier la signature de la réponse de la licence.
Pour ajouter une licence à une application, vous devez obtenir sa clé publique de licence et la copier dans votre application. Voici comment trouver la clé publique d'attribution de licence de votre application :
- Accédez à la Google Play Console et connectez-vous. Veillez à vous connecter au compte à partir duquel l'application sous licence est publiée (ou sera publiée).
- Sur la page d'informations de l'application, recherchez le lien Services et API, puis cliquez dessus.
- Sur la page Services et API, accédez à la section Gestion des licences et facturation des achats in-app. Votre clé publique de licence est indiquée dans le champ Votre clé de licence pour cette application.
Pour ajouter la clé publique à votre application, il vous suffit de copier/coller la chaîne de la clé depuis le champ dans votre application en tant que valeur de la variable de la chaîne BASE64_PUBLIC_KEY
. Assurez-vous d'avoir sélectionné toute la chaîne de la clé lorsque vous la copiez, sans omettre de caractères.
Voici un exemple de l'application exemple de la bibliothèque LVL :
Kotlin
private const val BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... " //truncated for this example class LicensingActivity : AppCompatActivity() { ... }
Java
public class MainActivity extends Activity { private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... "; //truncated for this example ... }
Appeler la méthode onDestroy() de la fonction LicenseChecker pour fermer les connexions IPC.
Enfin, pour laisser la bibliothèque LVL effectuer un nettoyage avant que votre Context
d'application ne soit modifié, ajoutez un appel à la méthode onDestroy()
de LicenseChecker
à partir de l'implémentation onDestroy()
de votre activité. L'appel force LicenseChecker
à fermer correctement toute connexion IPC ouverte au service ILicensingService de l'application Google Play et supprime toutes les références locales au service et au gestionnaire.
L'échec de l'appel de la méthode onDestroy()
de LicenseChecker
peut entraîner des problèmes tout au long du cycle de vie de votre application. Par exemple, si l'utilisateur change d'orientation d'écran pendant qu'une vérification de licence est active, l'application Context
est détruite. Si votre application ne ferme pas correctement la connexion IPC de LicenseChecker
, elle plantera une fois la réponse reçue. De même, si l'utilisateur quitte votre application alors qu'une vérification de licence est en cours, celle-ci plantera une fois la réponse reçue, sauf si elle a correctement appelé la méthode onDestroy()
de LicenseChecker
pour vous déconnecter du service.
Voici un exemple de l'application exemple incluse dans la bibliothèque LVL, où mChecker
est l'instance LicenseChecker
:
Kotlin
override fun onDestroy() { super.onDestroy() checker.onDestroy() ... }
Java
@Override protected void onDestroy() { super.onDestroy(); checker.onDestroy(); ... }
Si vous étendez ou modifiez LicenseChecker
, vous devrez peut-être également appeler la méthode finishCheck()
de LicenseChecker
pour nettoyer toutes les connexions IPC ouvertes.
Implémenter un DeviceLimiter
Il se peut que dans certains cas vous souhaitiez que votre Policy
limite le nombre d'appareils autorisés à utiliser une seule licence. Cela empêcherait un utilisateur de déplacer une application sous licence vers un certain nombre d'appareils et de l'utiliser sur ces appareils avec le même ID de compte. Cela empêcherait également un utilisateur de "partager" l'application en fournissant les informations de compte associées à la licence à d'autres personnes, qui pourraient alors se connecter à ce compte sur leurs appareils et accéder à la licence pour l'application.
La bibliothèque LVL prend en charge les licences par appareil en fournissant une interface DeviceLimiter
, qui déclare une seule méthode : allowDeviceAccess()
. Lorsqu'un LicenseValidator traite une réponse du serveur de gestion des licences, il appelle allowDeviceAccess()
en transmettant une chaîne d'ID utilisateur extraite de la réponse.
Si vous ne souhaitez pas utiliser la fonctionnalité de limitation des appareils, aucune action n'est requise. La classe LicenseChecker
utilise automatiquement une implémentation par défaut appelée NullDeviceLimiter. Comme son nom l'indique, NullDeviceLimiter est une classe "no-op" dont la méthode allowDeviceAccess()
renvoie simplement une réponse LICENSED
pour tous les utilisateurs et appareils.
Attention : L'attribution de licences par appareil est déconseillée pour la plupart des applications, car :
- Vous devez fournir un serveur backend pour gérer le mappage des utilisateurs et des appareils, et
- Un utilisateur pourrait par erreur se voir refuser l'accès à une application qu'il a légitimement achetée sur un autre appareil.
Obscurcir votre code
Pour assurer la sécurité de votre application, en particulier pour une application payante qui utilise des licences et/ou des protections et contraintes personnalisées, obscurcir le code de votre application est primordial. En obscurcissant correctement votre code, il est plus difficile pour un utilisateur malveillant de décompiler le bytecode de l'application, de le modifier (par exemple en supprimant la vérification des licences) puis de le recompiler.
Plusieurs programmes d'obscurcissement sont disponibles pour les applications Android, dont ProGuard, qui propose également des fonctionnalités d'optimisation du code. L'utilisation de ProGuard ou d'un programme similaire pour obscurcir votre code est fortement recommandée pour toutes les applications utilisant le service de gestion des licences Google Play.
Publier une application sous licence
Lorsque vous avez terminé de tester l'implémentation de votre licence, vous êtes prêt à publier l'application sur Google Play. Suivez la procédure normale pour préparer, signer, puis publier l'application.
Où obtenir de l'aide ?
Si vous avez des questions ou rencontrez des difficultés lors de l'implémentation ou du déploiement de la publication dans vos applications, veuillez utiliser les ressources d'assistance répertoriées dans le tableau ci-dessous. En orientant vos requêtes vers le forum approprié, vous pouvez obtenir l'assistance dont vous avez besoin plus rapidement.
Type d'assistance | Ressource | Thèmes abordés |
---|---|---|
Problèmes liés au développement et au test | Google Groupes : android-developers | Téléchargement et intégration de la bibliothèque LVL, projets de bibliothèque, questions relatives à Policy , idées d'expérience utilisateur, gestion des réponses, Obfuscator , IPC, configuration de l'environnement de test |
Stack Overflow : http://stackoverflow.com/questions/tagged/android | ||
Problèmes liés aux comptes, à la publication et au déploiement | Forum d'aide de Google Play | Comptes d'éditeur, paire de clés de licence, comptes de test, réponses du serveur, réponses de test, déploiement d'applications et résultats |
Questions fréquentes à l'assistance concernant licences Market | ||
Outils de suivi des problèmes pour la bibliothèque LVL | Outil de suivi des problèmes liés aux projets Marketlicensing | Rapports sur les bugs et problèmes spécifiques liés aux classes de code source de la bibliothèque LVL et aux implémentations d'interface |
Pour obtenir des informations générales sur la publication de messages dans les groupes cités ci-dessus, consultez la section Ressources de la communauté de la page "Ressources d'assistance pour les développeurs".
Autres ressources
L'application exemple incluse dans la bibliothèque LVL fournit un exemple complet de comment initier une vérification de licence et comment gérer le résultat obtenu dans la classe MainActivity
.