إضافة إثبات ملكية الترخيص من جهة العميل إلى تطبيقك

تحذير: عندما يجري تطبيقك عملية التحقّق من الترخيص من جهة العميل، يصبح من الأسهل على المهاجمين المحتملين تعديل أو إزالة المنطق المرتبط بعملية التحقّق هذه.

لهذا السبب، ننصحك بشدة بتنفيذ عملية إثبات صحة التراخيص من جهة الخادم بدلاً من ذلك.

بعد إعداد حساب الناشر وبيئة التطوير (راجِع إعداد الترخيص)، تكون جاهزًا لإضافة التحقق من الترخيص إلى تطبيقك باستخدام مكتبة التحقق من الترخيص (LVL).

تتضمّن عملية إضافة التحقّق من الترخيص من خلال LVL المهام التالية:

  1. بيان تطبيقك حول إضافة إذن الترخيص
  2. تنفيذ سياسة: يمكنك اختيار إحدى عمليات التنفيذ الكاملة الواردة في LVL أو إنشاء سياسة خاصة بك.
  3. تنفيذ تشويش، إذا كان Policy سيخزّن مؤقتًا أي بيانات استجابة للترخيص.
  4. إضافة رمز للتحقق من الترخيص في النشاط الرئيسي لتطبيقك.
  5. تنفيذ Devicelimiter (اختياري ولا يُنصَح به لمعظم التطبيقات).

تصف الأقسام أدناه هذه المهام. بعد إتمام عملية الدمج، من المفترض أن تتمكّن من تجميع تطبيقك بنجاح والبدء في الاختبار، كما هو موضّح في القسم إعداد بيئة الاختبار.

لإلقاء نظرة عامة على المجموعة الكاملة من الملفات المصدر المضمّنة في LVL، يُرجى الاطّلاع على ملخّص فئات LVL والواجهات.

إضافة إذن الترخيص

لاستخدام تطبيق Google Play لإرسال فحص الترخيص إلى الخادم، يجب أن يطلب تطبيقك الإذن المناسب، com.android.vending.CHECK_LICENSE. إذا لم يعلن طلبك عن إذن الترخيص ولكنه يحاول بدء فحص للترخيص، سيطرح LVL استثناءً أمنيًا.

لطلب إذن الترخيص في تطبيقك، يجب تعريف عنصر <uses-permission> على أنّه عنصر ثانوي في <manifest> على النحو التالي:

<uses-permission android:name="com.android.vending.CHECK_LICENSE" />

على سبيل المثال، نوضّح في ما يلي كيفية طلب نموذج LVL للإذن:

<?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>

ملاحظة: في الوقت الحالي، لا يمكنك الإفصاح عن الإذن CHECK_LICENSE في ملف البيان الخاص بمشروع مكتبة LVL، لأنّ أدوات SDK لن تدمجه في بيانات التطبيقات التابعة. بدلاً من ذلك، يجب الإفصاح عن الإذن في ملف البيان الخاص بكل تطبيق تابع.

تنفيذ سياسة

لا تحدّد خدمة ترخيص Google Play بحد ذاتها ما إذا كان يجب منح مستخدم لديه ترخيص معيّن إمكانية الوصول إلى تطبيقك. وبدلاً من ذلك، يتم إسناد هذه المسؤولية إلى عمليات تنفيذ Policy التي تقدّمها في طلبك.

السياسة هي واجهة مُعلَن عنها من قِبل LVL مُصمَّمة للحفاظ على منطق تطبيقك في ما يتعلق بالسماح بوصول المستخدم أو منعه، وذلك بناءً على نتيجة فحص الترخيص. لاستخدام LVL، يجب أن يوفّر تطبيقك طريقة تنفيذ Policy.

تعرض واجهة Policy طريقتَين، هما allowAccess() وprocessServerResponse()، ويتم طلبهما من خلال مثيل LicenseChecker عند معالجة استجابة من خادم الترخيص. وتعرِض أيضًا تعدادًا يُسمى LicenseResponse، ويحدّد قيمة استجابة الترخيص التي تم تمريرها في الاستدعاءات إلى processServerResponse().

  • يتيح لك processServerResponse() المعالجة المسبقة لبيانات الاستجابة الأولية الواردة من خادم الترخيص قبل تحديد ما إذا كنت تريد منح إذن الوصول أم لا.

    في عملية التنفيذ النموذجية، يتم استخراج بعض الحقول أو كلها من استجابة الترخيص وتخزينها محليًا في متجر دائم، مثلاً من خلال مساحة تخزين بسعة SharedPreferences، لضمان إمكانية الوصول إلى البيانات من خلال عمليات استدعاء التطبيقات ودورات طاقة الجهاز. على سبيل المثال، سيحافظ Policy على الطابع الزمني لآخر عملية فحص للترخيص بنجاح، وعدد إعادة المحاولة، وفترة صلاحية الترخيص، والمعلومات المشابهة في متجر دائم، بدلاً من إعادة ضبط القيم في كل مرة يتم فيها تشغيل التطبيق.

    عند تخزين بيانات الاستجابة على الجهاز، يجب أن يتأكّد Policy من إخفاء مفاتيح فك تشفير البيانات (راجِع تنفيذ أداة تشويش أدناه).

  • يحدّد allowAccess() ما إذا كان سيتم منح المستخدم إذن الوصول إلى تطبيقك، استنادًا إلى أي بيانات متاحة لاستجابة الترخيص (من خادم الترخيص أو من ذاكرة التخزين المؤقت) أو معلومات أخرى خاصة بالتطبيق. على سبيل المثال، يمكن أن يراعي تنفيذ allowAccess() المعايير الإضافية، مثل الاستخدام أو البيانات الأخرى التي يتم استردادها من خادم خلفية. في جميع الحالات، يجب أن يؤدي تنفيذ allowAccess() إلى عرض true فقط إذا كان المستخدم حاصلاً على ترخيص لاستخدام التطبيق، على النحو الذي يحدّده خادم الترخيص، أو إذا كانت هناك مشكلة مؤقتة في الشبكة أو النظام تمنع اكتمال فحص الترخيص. وفي هذه الحالات، يمكن أن يحافظ التنفيذ على عدد الردود على إعادة المحاولة والسماح مؤقتًا بالوصول إلى أن يكتمل فحص الترخيص التالي.

لتبسيط عملية إضافة الترخيص إلى تطبيقك ولتقديم صورة توضيحية حول الطريقة التي يجب تصميم Policy بها، يتضمّن LVL عمليتَي تنفيذ Policy كاملتين يمكنك استخدامهما بدون تعديل أو التكيّف مع احتياجاتك:

  • ServerManagePolicy، وهي Policy مرنة تستخدم الإعدادات التي يقدّمها الخادم والردود المخزّنة مؤقتًا لإدارة إمكانية الوصول في حالات الشبكة المتنوعة،
  • لا تخزّن السياسة StrictPolicy في ذاكرة التخزين المؤقّت لأيّ بيانات استجابة، وتسمح بالوصول فقط في حال عرض الخادم استجابة مرخَّصة.

بالنسبة إلى معظم التطبيقات، يوصى بشدة باستخدام ServerManagedPolicy. ServerManagePolicy هو الإعداد التلقائي LVL ويتم دمجه مع نموذج تطبيق LVL.

إرشادات السياسات المخصّصة

في تنفيذ الترخيص، يمكنك استخدام إحدى السياسات الكاملة المتوفرة في LVL (ServerManagedPolicy أو StrictPolicy) أو يمكنك إنشاء سياسة مخصّصة. بالنسبة إلى أي نوع من السياسات المخصّصة، هناك العديد من نقاط التصميم المهمة التي يجب فهمها وأخذها في الاعتبار في عملية التنفيذ.

يطبق خادم الترخيص الحدود العامة للطلبات للحماية من الإفراط في استخدام الموارد التي قد تؤدي إلى رفض الخدمة. عندما يتخطى تطبيق حد الطلب، يعرض خادم الترخيص استجابة 503، والتي يتم تمريرها إلى تطبيقك كخطأ عام في الخادم. ويعني ذلك أنّه لن يكون هناك استجابة ترخيص متاحة للمستخدم إلى أن تتم إعادة ضبط الحدّ المسموح به، ما قد يؤثّر في المستخدم لفترة غير محدَّدة.

في حال تصميم سياسة مخصّصة، ننصحك بما يلي: Policy

  1. تخزِّن ذاكرة التخزين المؤقت (وتُخفي تشويشًا مناسبًا) لأحدث استجابة ترخيص ناجحة في مساحة التخزين الدائمة المحلية.
  2. تعرض الاستجابة المخزّنة مؤقتًا لجميع عمليات فحص التراخيص ما دامت الاستجابة المخزَّنة مؤقتًا صالحة، بدلاً من تقديم طلب إلى خادم الترخيص. ننصحك بشدة بضبط صلاحية الاستجابة وفقًا لخيار VT الإضافي الذي يقدّمه الخادم. راجع العناصر الإضافية لاستجابة الخادم للحصول على مزيد من المعلومات.
  3. تستخدم فترة تراجع أسّية، في حال إعادة محاولة أي طلبات تؤدي إلى حدوث أخطاء. تجدر الإشارة إلى أنّ برنامج Google Play يُعيد تلقائيًا محاولة إرسال الطلبات التي تعذّر تنفيذها، لذا لن يكون عليك في معظم الحالات أن تعيد Policy المحاولة.
  4. يوفر "فترة سماح" تسمح للمستخدم بالوصول إلى طلبك لفترة محدودة أو لعدد من الاستخدامات، أثناء إعادة محاولة التحقق من الترخيص. تفيد فترة السماح المستخدم من خلال السماح بالوصول إلى أن يتم إكمال فحص الترخيص التالي بنجاح، وتفيدك فترة السماح من خلال وضع حدّ أقصى على إمكانية الوصول إلى تطبيقك في حال عدم توفّر استجابة ترخيص صالحة.

إنّ تصميم Policy وفقًا للإرشادات المذكورة أعلاه أمر بالغ الأهمية، لأنه يضمن توفير أفضل تجربة ممكنة للمستخدمين مع منحك تحكمًا فعّالاً في تطبيقك حتى في حالات الخطأ.

تجدر الإشارة إلى أنّه يمكن لأي Policy استخدام الإعدادات التي يوفّرها خادم الترخيص للمساعدة في إدارة الصلاحية والتخزين المؤقت، وإعادة محاولة فترة السماح، والمزيد. إنّ استخراج الإعدادات التي يوفّرها الخادم واضح وننصحك بشدّة باستخدامها. اطلع على تنفيذ ServerManagePolicy للحصول على مثال عن كيفية استخراج البيانات الإضافية واستخدامها. للحصول على قائمة بإعدادات الخادم ومعلومات حول كيفية استخدامها، اطلع على العناصر الإضافية لاستجابة الخادم.

سياسة إدارة الخادم

يتضمّن LVL تنفيذًا كاملاً ومقترَحًا لواجهة Policy المسماة ServerManagedPolicy. يتم دمج عملية التنفيذ مع فئات LVL وتكون بمثابة Policy التلقائي في المكتبة.

توفر السياسة ServerManagePolicy جميع عمليات المعالجة للترخيص وإعادة محاولة الاستجابة. يخزِّن هذا المتصفح جميع بيانات الاستجابة مؤقتًا محليًا في ملف SharedPreferences، ما يؤدي إلى تشويشها باستخدام تنفيذ Obfuscator في التطبيق. ويضمن ذلك أن بيانات استجابة الترخيص آمنة واستمرارها في جميع دورات طاقة الجهاز. توفِّر سياسة ServerManagePolicy عمليات تنفيذ ملموسة لطرق الواجهة processServerResponse() وallowAccess()، وتتضمّن أيضًا مجموعة من الأساليب والأنواع الداعمة لإدارة استجابات التراخيص.

والأهم من ذلك، أن ميزة ServerManagedPolicy الرئيسية هي استخدام الإعدادات التي يوفّرها الخادم كأساس لإدارة الترخيص خلال فترة ردّ الأموال للتطبيق ومن خلال حالات متفاوتة للشبكة والأخطاء. عندما يتصل تطبيق بخادم Google Play للتحقق من الترخيص، يلحق الخادم عدة إعدادات كأزواج المفتاح/القيمة في حقل الإضافات لأنواع معيّنة من استجابة الترخيص. على سبيل المثال، يوفر الخادم القيم المقترحة لفترة صلاحية ترخيص التطبيق، وفترة السماح لإعادة المحاولة، والحد الأقصى المسموح به لعدد إعادة المحاولة، وغيرها. يستخلص ServerManagePolicy القيم من استجابة الترخيص في طريقة processServerResponse() ويتحقق منها في طريقة allowAccess(). للحصول على قائمة بالإعدادات التي يوفرها الخادم والمستخدَمة بواسطة ServerManagedPolicy، يُرجى الاطّلاع على العناصر الإضافية لاستجابة الخادم.

للتيسير عليك وتحقيق أفضل أداء والاستفادة من استخدام إعدادات الترخيص من خادم Google Play، ننصحك بشدة باستخدام ServerManagedPolicy (السياسة المُدارة) للترخيص من خلال Policy.

في حال قلقك بشأن أمان بيانات استجابة الترخيص المخزّنة محليًا في SharedPreferences، يمكنك استخدام خوارزمية أقوى لإخفاء مفاتيح فك التشفير أو تصميم سياسة Policy أكثر صرامة لا تخزِّن بيانات الترخيص. تتضمّن LVL مثالاً على Policy. يُرجى الاطّلاع على سياسة صارمة للحصول على مزيد من المعلومات.

لاستخدام ServerManagedPolicy، ما عليك سوى استيراده إلى "النشاط" وإنشاء مثيل وتمرير مرجع إلى المثيل عند إنشاء LicenseChecker. للحصول على مزيد من المعلومات، يمكنك الاطّلاع على اختبار "التحقّق من التراخيص" و "فحص ترخيص الترخيص" للحصول على مزيد من المعلومات.

سياسة صارمة

تتضمّن LVL تنفيذًا كاملاً بديلاً لواجهة Policy يُسمى StrictPolicy. يوفر تنفيذ StrictPolicy سياسة أكثر تقييدًا من ServerManagedPolicy، حيث إنه لا يسمح للمستخدم بالوصول إلى التطبيق ما لم يتم تلقي استجابة ترخيص من الخادم في وقت الوصول تشير إلى أن المستخدم مُرخَّص.

إنّ الميزة الرئيسية لتطبيق StrictPolicy هي أنّه لا يخزّن أي بيانات حول استجابة الترخيص محليًا في متجر دائم. وبسبب عدم تخزين أي بيانات، لا يتم تتبُّع طلبات إعادة المحاولة ولا يمكن استخدام الردود المخزّنة مؤقتًا لتنفيذ عمليات فحص الترخيص. لا يسمح Policy بالوصول إلا في الحالات التالية:

  • يتم تلقي استجابة الترخيص من خادم الترخيص.
  • تشير استجابة الترخيص إلى أنّ المستخدم لديه ترخيص للوصول إلى التطبيق.

من المناسب استخدام RelayPolicy إذا كان مصدر قلقك الأساسي هو ضمان عدم السماح لأي مستخدم بالوصول إلى التطبيق في جميع الحالات الممكنة ما لم يتم تأكيد حصول المستخدم على ترخيص في وقت الاستخدام. بالإضافة إلى ذلك، توفّر هذه السياسة مستوى أمان أعلى قليلاً من ServerManagedPolicy - وبما أنّه لا تتوفّر بيانات مخزّنة مؤقتًا محليًا، فليس هناك طريقة يمكن لأيّ مستخدِم ضارّ التلاعب بالبيانات المخزّنة مؤقتًا والحصول على إمكانية الوصول إلى التطبيق.

في الوقت نفسه، يشكّل Policy هذا تحديًا للمستخدمين العاديين، إذ يعني ذلك أنّهم لن يتمكّنوا من الوصول إلى التطبيق عند عدم توفّر اتصال بشبكة (خلوية أو Wi-Fi). أثر جانبي آخر هو أن تطبيقك سيرسل المزيد من طلبات فحص الترخيص إلى الخادم، لأن استخدام استجابة مخزّنة مؤقتًا غير ممكن.

بشكل عام، تمثل هذه السياسة مقايضة إلى حد ما في راحة المستخدم في ما يتعلق بالأمان المطلق والتحكم في الوصول. ننصحك بالتفكير في المقايضة بعناية قبل استخدام Policy.

لاستخدام RelayPolicy، ما عليك سوى استيرادها إلى نشاطك وإنشاء مثيل وتمرير مرجع إليه عند إنشاء LicenseChecker. يمكنك الاطّلاع على Instantcate LicenseChecker و LicenseCheckerCallback للحصول على مزيد من المعلومات.

في عملية تنفيذ Policy النموذجية، يجب حفظ بيانات استجابة الترخيص لتطبيق في متجر دائم، بحيث يمكن الوصول إليه من خلال استدعاءات التطبيق ودورات تشغيل الجهاز. على سبيل المثال، سيحتفظ Policy بالطابع الزمني لآخر عملية فحص للترخيص، وعدد إعادة المحاولة، وفترة صلاحية الترخيص، والمعلومات المشابهة في متجر دائم، بدلاً من إعادة ضبط القيم في كل مرة يتم فيها تشغيل التطبيق. إنّ Policy التلقائي المضمّن في LVL، ServerManagedPolicy، يخزّن بيانات استجابة الترخيص في مثيل SharedPreferences لضمان أن تكون البيانات ثابتة.

بما أنّ Policy سيستخدم بيانات استجابة الترخيص المخزَّنة لتحديد ما إذا كان سيتم السماح بالوصول إلى التطبيق أو عدم السماح به، يجب التأكد من أنّ أي بيانات مخزَّنة آمنة ولا يمكن إعادة استخدامها أو التلاعب بها من قِبل المستخدم الجذر على الجهاز. وعلى وجه التحديد، على Policy دائمًا إخفاء مفاتيح فك تشفير البيانات قبل تخزينها باستخدام مفتاح فريد للتطبيق والجهاز. يُعد إخفاء مفاتيح فك التشفير باستخدام مفتاح خاص بالتطبيق وخاصًا بالجهاز أمرًا بالغ الأهمية، لأنه يمنع مشاركة البيانات التي تم إخفاء مفاتيح فك تشفيرها بين التطبيقات والأجهزة.

يساعد LVL التطبيق في تخزين بيانات استجابة الترخيص بطريقة آمنة ودائمة. أولاً، يوفّر التطبيق واجهة Obfuscator تتيح لتطبيقك توفير خوارزمية التشويش التي تختارها للبيانات المخزّنة. بناءً على ذلك، يوفّر LVL الفئة المساعدة PreferredObfuscator، والتي تعالج معظم مهام استدعاء فئة Obfuscator الخاصة بالتطبيق وقراءة وكتابة البيانات المبهمة في حالة SharedPreferences.

يوفر LVL تطبيق Obfuscator كاملاً يُطلق عليه AESObfuscator يستخدم تشفير AES لتشويش البيانات. ويمكنك استخدام AESObfuscator في تطبيقك بدون تعديل أو يمكنك تكييفه حسب احتياجاتك. إذا كنت تستخدم سياسة Policy (مثل ServerManagedPolicy) تخزّن بيانات استجابة الترخيص مؤقتًا، ننصحك بشدة باستخدام أداة AESObfuscator كأساس لتطبيق Obfuscator. لمزيد من المعلومات، يُرجى الاطّلاع على القسم التالي.

ملف تعريف ارتباط AES

يتضمّن LVL التنفيذ الكامل والموصى به لواجهة Obfuscator التي يُطلق عليها اسم AESObfuscator. يتم دمج عملية التنفيذ مع نموذج تطبيق LVL، كما يتم استخدامها كإعدادات Obfuscator التلقائية في المكتبة.

يوفر برنامج AESObfuscator تشويشًا آمنًا للبيانات باستخدام AES لتشفير البيانات وفك تشفيرها أثناء كتابتها أو قراءتها في وحدة التخزين. يبدأ Obfuscator التشفير باستخدام ثلاثة حقول بيانات يوفّرها التطبيق:

  1. القيمة العشوائية: مصفوفة من وحدات البايت العشوائية لاستخدامها مع كل عملية تشويش.
  2. سلسلة معرّف التطبيق، وعادةً ما تكون اسم حزمة التطبيق.
  3. سلسلة معرّف جهاز يتم اشتقاقها من أكبر عدد ممكن من المصادر الخاصة بالجهاز، بحيث تجعلها فريدة من نوعها.

لاستخدام AESObfuscator، يجب أولاً استيرادها إلى نشاطك. حدِّد صفيفًا نهائيًا خاصًا للاحتفاظ بوحدات البايت العشوائية وضبطه على 20 بايت يتم إنشاؤه عشوائيًا.

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
     };
    ...

بعد ذلك، عليك تعريف متغيّر للاحتفاظ بمعرّف الجهاز وإنشاء قيمة له بأي طريقة مطلوبة. على سبيل المثال، يطلب نموذج التطبيق المضمّن في LVL إعدادات النظام الخاصة بـ android.Settings.Secure.ANDROID_ID، وهو فريد لكل جهاز.

لاحظ أنه بناءً على واجهات برمجة التطبيقات التي تستخدمها، قد يحتاج تطبيقك إلى طلب أذونات إضافية من أجل الحصول على معلومات متعلقة بالجهاز. على سبيل المثال، لإجراء طلب بحث عن TelephonyManager للحصول على رمز IMEI للجهاز أو البيانات ذات الصلة، سيحتاج التطبيق أيضًا إلى طلب إذن android.permission.READ_PHONE_STATE في ملف البيان.

قبل طلب أذونات جديدة للغرض الوحيد المتمثل في الحصول على معلومات خاصة بالجهاز لاستخدامها في Obfuscator، يُرجى مراعاة كيفية تأثير ذلك في تطبيقك أو فلترته على Google Play (نظرًا لأن بعض الأذونات قد تجعل أدوات إصدار SDK تضيف <uses-feature> المرتبطة).

وأخيرًا، قم بإنشاء مثيل لـ AESObfuscator، مع تمرير القيمة العشوائية ومعرف التطبيق ومعرف الجهاز. يمكنك إنشاء المثيل مباشرةً أثناء إنشاء الترميزَين Policy وLicenseChecker. مثلاً:

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.
        );
    ...

للاطلاع على مثال كامل، راجع MainActivity في نموذج تطبيق LVL.

التحقّق من الترخيص من نشاط

بعد تنفيذ Policy لإدارة الوصول إلى تطبيقك، تتمثّل الخطوة التالية في إضافة فحص للترخيص إلى تطبيقك، يؤدي إلى بدء طلب بحث إلى خادم الترخيص عند اللزوم وإدارة الوصول إلى التطبيق بناءً على استجابة الترخيص. تتم جميع إجراءات إضافة فحص الترخيص ومعالجة الاستجابة في ملف مصدر Activity الرئيسي.

لإضافة فحص الترخيص والتعامل مع الاستجابة، يجب:

  1. إضافة عمليات استيراد
  2. تنفيذ LicenseCheckerCallback كفئة داخلية خاصة
  3. إنشاء معالِج للنشر من LicenseCheckerCallback إلى سلسلة محادثات واجهة المستخدم
  4. Intecate LicenseChecker و LicenseCheckerCallback
  5. Call checkAccess() لبدء التحقّق من الترخيص.
  6. تضمين مفتاحك العام للترخيص
  7. استدع طريقة onDestroy() الخاصة بفحص الترخيص لإغلاق اتصالات IPC.

تصف الأقسام أدناه هذه المهام.

نظرة عامة على التحقّق من التراخيص والاستجابة لها

في معظم الحالات، عليك إضافة فحص الترخيص إلى Activity الرئيسي لتطبيقك من خلال طريقة onCreate(). ويضمن ذلك استدعاء فحص الترخيص على الفور عندما يشغِّل المستخدم تطبيقك مباشرةً. وفي بعض الحالات، يمكنك إضافة عمليات فحص الترخيص في مواقع أخرى أيضًا. على سبيل المثال، إذا كان تطبيقك يتضمن العديد من مكوّنات الأنشطة التي يمكن أن تبدأ التطبيقات الأخرى بحلول Intent، يمكنك إضافة عمليات فحص الترخيص إلى تلك الأنشطة.

تتألف عملية التحقّق من الترخيص من إجراءَين أساسيَين:

  • تمثّل هذه السمة استدعاء لطريقة لبدء فحص الترخيص، أي في LVL، هو استدعاء لطريقة checkAccess() لكائن LicenseChecker تنشئه.
  • استدعاء يعرض نتيجة فحص الترخيص. في LVL، تكون هذه واجهة LicenseCheckerCallback تنفّذها. تشير الواجهة إلى طريقتين، هما allow() وdontAllow()، اللتان تستدعيان المكتبة بناءً على نتيجة فحص الترخيص. يمكنك تنفيذ هاتين الطريقتين بأي منطق تحتاجه، للسماح للمستخدم بالوصول إلى تطبيقك أو عدم السماح له. يُرجى العلم أنّ هذه الطرق لا تحدّد ما إذا كان يسمح بالوصول إلى البيانات أم لا - وهذا تحديد مسؤولية التنفيذ Policy. بدلاً من ذلك، توفّر هذه الطرق ببساطة سلوكيات التطبيق لكيفية السماح بالوصول ومنعه (والتعامل مع أخطاء التطبيق).

    تقدّم الطريقتان allow() وdontAllow() "سببًا" للردّ، ويمكن أن يكون ذلك عبارة عن إحدى قيم Policy أو LICENSED أو NOT_LICENSED أو RETRY. وعلى وجه الخصوص، يجب التعامل مع الحالة التي تتلقّى فيها الطريقة استجابة RETRY لـ dontAllow()، تزويد المستخدم بالزر "إعادة المحاولة"، والذي قد يكون بسبب عدم توفّر الخدمة أثناء الطلب.

الشكل 1. نظرة عامة على تفاعل فحص الترخيص النموذجي.

يوضّح المخطّط أعلاه كيفية إجراء عملية تحقّق نموذجية للترخيص:

  1. ينشئ الرمز البرمجي في قسم "النشاط الرئيسي" في التطبيق مثيلاً للكائنات LicenseCheckerCallback وLicenseChecker. عند إنشاء LicenseChecker، يتم تمرير الرمز في Context، وعملية تنفيذ Policy لاستخدامها، والمفتاح العام لحساب الناشر للترخيص كمعلَمات.
  2. ثم يستدعي الرمز الإجراء checkAccess() على الكائن LicenseChecker. تستدعي الطريقة Policy لتحديد ما إذا كان هناك استجابة ترخيص صالحة تم تخزينها مؤقتًا محليًا في SharedPreferences.
    • إذا كان الأمر كذلك، تستدعي عملية تنفيذ checkAccess() السمة allow().
    • وبخلاف ذلك، تبدأ LicenseChecker طلب فحص الترخيص الذي يتم إرساله إلى خادم الترخيص.

    ملاحظة: يعرض خادم الترخيص دائمًا LICENSED عند إجراء فحص ترخيص لتطبيق مسودة.

  3. عند تلقّي ردّ، ينشئ LicenseChecker أداة تصديق الترخيص التي يتحقّق من صحة بيانات الترخيص الموقَّعة ويستخرج حقول الاستجابة، ثم ينقلها إلى Policy لإجراء مزيد من التقييم.
    • إذا كان الترخيص صالحًا، يخزن Policy مؤقتًا الرد في SharedPreferences ويُعلم أداة التحقق التي تطلب بعد ذلك الإجراء allow() في كائن LicenseCheckerCallback.
    • في حال كان الترخيص غير صالح، ترسل Policy إشعارًا إلى أداة التحقّق التي تستدعي طريقة dontAllow() في LicenseCheckerCallback.
  4. في حال حدوث خطأ محلي أو في الخادم قابل للإصلاح، مثلاً عندما تكون الشبكة غير متاحة لإرسال الطلب، يرسل LicenseChecker استجابة RETRY إلى طريقة processServerResponse() للعنصر Policy.

    وتتلقّى أيضًا طريقتا معاودة الاتصال allow() وdontAllow() وسيطة reason. يكون سبب استخدام الطريقة allow() عادةً Policy.LICENSED أو Policy.RETRY ويكون السبب dontAllow() عادةً هو Policy.NOT_LICENSED أو Policy.RETRY. وتكون قيم الاستجابة هذه مفيدة لتتمكّن من عرض ردّ مناسب للمستخدم، مثلاً من خلال توفير الزر "إعادة المحاولة" عند استجابة dontAllow() بـ Policy.RETRY، ما قد يكون بسبب عدم توفّر الخدمة.

  5. في حال حدوث خطأ في التطبيق، مثلاً عندما يحاول التطبيق التحقق من ترخيص اسم حزمة غير صالح، يرسل LicenseChecker ردًّا على الخطأ إلى طريقة applicationError() في LicenseCheckerCallback.

يُرجى ملاحظة أنّه بالإضافة إلى بدء فحص الترخيص والتعامل مع النتيجة الموضّحة في الأقسام أدناه، على تطبيقك أيضًا توفير تنفيذ السياسة، وفي حال كان Policy يخزّن بيانات الاستجابة (مثل ServerManagedPolicy)، قد يتم تنفيذ Obfuscator.

إضافة عمليات استيراد

أولاً، افتح ملف الفئة "النشاط الرئيسي" للتطبيق واستيراد LicenseChecker وLicenseCheckerCallback من حزمة 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;

في حال استخدام تطبيق Policy التلقائي المتوفّر مع LVL أو ServerManagePolicy، يمكنك استيراده أيضًا مع AESObfuscator. إذا كنت تستخدم Policy أو Obfuscator مخصّصَين، يمكنك استيرادهما بدلاً من ذلك.

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;

تنفيذ LicenseCheckerCallback كفئة داخلية خاصة

LicenseCheckerCallback هي واجهة تقدّمها منصة LVL للتعامل مع نتيجة التحقّق من الترخيص. للحصول على ترخيص باستخدام LVL، عليك تنفيذ سياسة LicenseCheckerCallback وطرقها للسماح بالوصول إلى التطبيق أو منعه.

وتكون نتيجة فحص الترخيص دائمًا استدعاء إحدى طرق LicenseCheckerCallback، ويتم إجراؤها استنادًا إلى التحقق من صحة حمولة الاستجابة ورمز استجابة الخادم نفسه وأي معالجة إضافية يوفّرها Policy. يمكن لتطبيقك تنفيذ الطرق بأي طريقة مطلوبة. بشكلٍ عام، من الأفضل الحفاظ على بساطة الطرق، وحصرها على إدارة حالة واجهة المستخدم والوصول إلى التطبيق. إذا أردت إضافة المزيد من عمليات المعالجة لاستجابات التراخيص، مثل التواصل مع خادم خلفية أو تطبيق قيود مخصّصة، ننصحك بتضمين هذا الرمز في Policy بدلاً من إضافته في طريقتَي LicenseCheckerCallback.

في معظم الحالات، يجب الإعلان عن تنفيذ LicenseCheckerCallback كفئة خاصة داخل فئة النشاط الرئيسية لتطبيقك.

نفِّذ طريقتَي allow() وdontAllow() حسب الحاجة. للبدء، يمكنك استخدام سلوكيات بسيطة للتعامل مع النتائج من خلال الطرق، مثل عرض نتيجة الترخيص في مربّع حوار. يساعدك هذا في تشغيل تطبيقك في وقت أقرب ويمكن أن يساعد في تصحيح الأخطاء. لاحقًا، بعد تحديد السلوكيات الدقيقة التي تريدها، يمكنك إضافة معالجة أكثر تعقيدًا.

إليك بعض الاقتراحات للتعامل مع الردود غير المرخَّصة في dontAllow() ما يلي:

  • اعرض مربّع الحوار "إعادة المحاولة" للمستخدم، بما في ذلك زر لبدء عملية تحقّق جديدة من الترخيص إذا كانت قيمة reason المقدَّمة هي Policy.RETRY.
  • عرض مربع الحوار "شراء هذا التطبيق"، بما في ذلك الزر الذي يربط المستخدم بصفحة تفاصيل التطبيق على Google Play، والتي يمكن للاستخدام من خلالها شراء التطبيق. للمزيد من المعلومات حول كيفية إعداد هذه الروابط، يمكنك الاطّلاع على الربط بمنتجاتك.
  • عرض إشعار نخب يشير إلى أن ميزات التطبيق محدودة لأنه غير مرخّص.

يوضِّح المثال أدناه كيف ينفّذ نموذج تطبيق LVL LicenseCheckerCallback، مع طرق تعرض نتيجة التحقّق من الترخيص في مربّع حوار.

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);
        }
    }
}

بالإضافة إلى ذلك، يجب تنفيذ طريقة applicationError()، التي تستدعيها LVL للسماح للتطبيق بمعالجة الأخطاء التي لا يمكن إعادة المحاولة فيها. للحصول على قائمة بهذه الأخطاء، يُرجى الاطّلاع على رموز استجابة الخادم في مرجع الترخيص. يمكنك تنفيذ الطريقة بأي طريقة مطلوبة. في معظم الحالات، يجب أن تسجّل الطريقة رمز الخطأ وتستدعي dontAllow().

إنشاء معالج للنشر من LicenseCheckerCallback إلى مؤشر ترابط واجهة المستخدم

وأثناء فحص الترخيص، يمرّر LVL الطلب إلى تطبيق Google Play الذي يعالج الاتصال بخادم الترخيص. يمرر LVL الطلب عبر IPC غير متزامن (باستخدام Binder) بحيث لا تتم المعالجة الفعلية والاتصال بالشبكة على سلسلة محادثات يديرها تطبيقك. وبالمثل، عندما يتلقّى تطبيق Google Play النتيجة، يستدعي طريقة لمعاودة الاتصال عبر IPC، والتي يتم تنفيذها بدورها في مجموعة سلاسل محادثات IPC في عملية تطبيقك.

تدير الفئة LicenseChecker اتصال IPC الخاص بتطبيقك بتطبيق Google Play، بما في ذلك المكالمة التي ترسل الطلب ومعاودة الاتصال التي تتلقّى الردّ. يتتبّع "LicenseChecker" أيضًا طلبات التراخيص المفتوحة ويدير مهلتها.

وحتى يمكنها التعامل مع المهلات بشكل صحيح ومعالجة الردود الواردة بدون التأثير في مؤشر ترابط واجهة المستخدم للتطبيق الخاص بك، LicenseChecker ينتج عنها سلسلة في الخلفية عند إنشاء مثيل. تُجري السلسلة بأكملها عملية معالجة نتائج التحقق من الترخيص، سواء كانت النتيجة استجابة من الخادم أو خطأ بسبب انتهاء المهلة. في نهاية عملية المعالجة، يتم استدعاء طُرق LicenseCheckerCallback من سلسلة التعليمات في الخلفية.

ويعني هذا في طلبك ما يلي:

  1. وسيتم استدعاء طُرق LicenseCheckerCallback الخاصة بك، في كثير من الحالات، من سلسلة محادثات في الخلفية.
  2. لن تتمكن هذه الطرق من تحديث الحالة أو استدعاء أي معالجة في مؤشر ترابط واجهة المستخدم، ما لم تنشئ معالجًا في سلسلة محادثات واجهة المستخدم ويتم نشر طرق معاودة الاتصال على المعالج.

إذا كنت تريد أن تعدّل طُرق LicenseCheckerCallback سلسلة محادثات واجهة المستخدم، يمكنك إنشاء Handler في طريقة onCreate() الرئيسية ضمن قسم "النشاط" كما هو موضّح أدناه. في هذا المثال، تستدعي طرق LicenseCheckerCallback لنموذج تطبيق LVL (انظر أعلاه) displayResult() لتعديل سلسلة محادثات واجهة المستخدم من خلال طريقة post() للمعالج.

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();
    }

بعد ذلك، يمكنك في طرق LicenseCheckerCallback استخدام طرق المعالج لنشر كائنات قابلة للتشغيل أو كائنات رسائل إلى المعالج. إليك كيفية نشر نموذج التطبيق المضمّن في LVL رسالة "قابل للتشغيل" على معالِج في سلسلة محادثات واجهة المستخدم لعرض حالة الترخيص.

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);
            }
        });
    }

إنشاء نسخة افتراضية من LicenseChecker و LicenseCheckerCallback

في طريقة onCreate() في "النشاط" الرئيسية، يمكنك إنشاء مثيلات خاصة لـ LicenseCheckerCallback وLicenseChecker. يجب إنشاء LicenseCheckerCallback أولاً كإجراء بديل، لأنّك تحتاج إلى تمرير مرجع لهذا المثال عند استدعاء الدالة الإنشائية لـ LicenseChecker.

عند إنشاء مثيل لـ LicenseChecker، يجب تمرير المعلَمات التالية:

  • تطبيق "Context"
  • مرجع إلى آلية تنفيذ سياسة Policy المطلوب استخدامها في عملية التحقّق من الترخيص. في معظم الحالات، يمكنك استخدام تطبيق Policy التلقائي الذي توفّره LVL، ServerManagedPolicy.
  • متغير السلسلة الذي يحمل المفتاح العام لحساب الناشر للترخيص.

إذا كنت تستخدم ServerManagedPolicy، لن تحتاج إلى الوصول إلى الفئة مباشرةً، لكي تتمكّن من إنشاء مثيل لها في الدالة الإنشائية LicenseChecker، كما هو موضَّح في المثال أدناه. لاحظ أنك تحتاج إلى تمرير مرجع إلى مثيل Obfuscator جديد عند إنشاء ServerManagedPolicy.

يوضّح المثال أدناه إنشاء مثيل لـ LicenseChecker وLicenseCheckerCallback من طريقة onCreate() لفئة نشاط.

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.
            );
        ...
    }
}

تجدر الإشارة إلى أنّ LicenseChecker يستدعي طُرق LicenseCheckerCallback من سلسلة واجهة المستخدم فقط إذا كان هناك استجابة ترخيص صالحة مخزّنة مؤقتًا محليًا. إذا تم إرسال فحص الترخيص إلى الخادم، تنشأ عمليات رد الاتصال دائمًا من سلسلة التعليمات في الخلفية، حتى بسبب أخطاء الشبكة.

يمكنك استدعاء checkAccess() لبدء التحقّق من الترخيص.

في نشاطك الرئيسي، أضِف استدعاءً إلى طريقة checkAccess() في مثيل LicenseChecker. في الطلب، مرِّر إشارة إلى مثيل LicenseCheckerCallback الخاص بك كمَعلمة. إذا كنت بحاجة إلى التعامل مع أي تأثيرات خاصة لواجهة المستخدم أو إدارة الحالة قبل المكالمة، قد يكون من المفيد استدعاء checkAccess() من طريقة برنامج تضمين. على سبيل المثال، يستدعي تطبيق LVL العينة checkAccess() من طريقة برنامج تضمين 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);
    }

تضمين مفتاحك العام للترخيص

لكل تطبيق، تنشئ خدمة Google Play تلقائيًا زوج مفاتيح عام/خاص RSA 2048 بت يُستخدم في الترخيص والفوترة داخل التطبيقات. ويرتبط زوج المفاتيح بشكل فريد بالتطبيق. وعلى الرغم من أنّ زوج المفاتيح مرتبط بالتطبيق، فإنّه ليس هو نفسه المفتاح الذي تستخدمه لتوقيع التطبيقات (أو المشتق منه).

تعرض Google Play Console المفتاح العام للترخيص لأي مطوّر برامج يسجِّل الدخول إلى Play Console، ولكنّها تُبقي المفتاح الخاص مخفيًا عن جميع المستخدمين في موقع آمن. عندما يطلب تطبيق فحص ترخيص لتطبيق منشور في حسابك، يوقّع خادم الترخيص على استجابة الترخيص باستخدام المفتاح الخاص لزوج مفاتيح تطبيقك. وعندما تتلقّى جهة تسجيل LVL الاستجابة، فإنّها تستخدم المفتاح العام الذي يقدّمه التطبيق للتحقّق من توقيع استجابة الترخيص.

لإضافة ترخيص إلى تطبيق، يجب الحصول على المفتاح العام للتطبيق من أجل الحصول على ترخيص له ونسخه في تطبيقك. إليك كيفية العثور على المفتاح العام لتطبيقك للحصول على ترخيص:

  1. انتقِل إلى Google Play Console وسجِّل الدخول. احرص على تسجيل الدخول إلى الحساب الذي تم منه نشر (أو سيتم نشر التطبيق) الذي ترخّصه.
  2. في صفحة تفاصيل التطبيق، حدِّد موقع رابط الخدمات وواجهات برمجة التطبيقات وانقر عليه.
  3. في صفحة الخدمات وواجهات برمجة التطبيقات، ابحث عن قسم الترخيص والفوترة داخل التطبيق. يتوفر المفتاح العام للترخيص في حقل مفتاح الترخيص لهذا التطبيق.

لإضافة المفتاح العام إلى تطبيقك، ما عليك سوى نسخ سلسلة المفتاح ولصقها من الحقل في تطبيقك كقيمة لمتغيّر السلسلة BASE64_PUBLIC_KEY. عند النسخ، تأكد من أنك حددت سلسلة المفتاح بالكامل، دون حذف أي أحرف.

فيما يلي مثال من نموذج تطبيق 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
    ...
}

استدعِ طريقة onDestroy() الخاصة بـ LicenseChecker لإغلاق اتصالات IPC

وأخيرًا، للسماح لـ LVL بالمسح قبل تغيير Context التطبيق، أضف استدعاءً إلى طريقة LicenseChecker onDestroy() من تنفيذ onDestroy() في النشاط. يؤدي الاستدعاء إلى إغلاق LicenseChecker على نحو سليم لأي اتصال IPC مفتوح ببروتوكول ILicensingService الخاص بتطبيق Google Play وإزالة أي مراجع محلية للخدمة والمعالج.

قد يؤدي عدم استدعاء طريقة onDestroy() في LicenseChecker إلى حدوث مشاكل على مدار دورة حياة تطبيقك. على سبيل المثال، إذا غيّر المستخدم اتجاه الشاشة أثناء تفعيل عملية التحقق من الترخيص، سيتم إتلاف تطبيق Context. إذا لم يغلق تطبيقك اتصال IPC الخاص بـ LicenseChecker بشكل صحيح، سيتعطّل التطبيق عند تلقّي الاستجابة. وبالمثل، إذا خرج المستخدم من طلبك أثناء إجراء عملية فحص للترخيص، سيتوقف طلبك عند تلقي الاستجابة، ما لم يتم طلب طريقة LicenseChecker onDestroy() الخاصة بقطع الاتصال بالخدمة بشكل صحيح.

في ما يلي مثال من نموذج التطبيق المُضمَّن في LVL، حيث يكون mChecker هو مثيل LicenseChecker:

Kotlin

    override fun onDestroy() {
        super.onDestroy()
        checker.onDestroy()
        ...
    }

Java

    @Override
    protected void onDestroy() {
        super.onDestroy();
        checker.onDestroy();
        ...
    }

إذا كنت بصدد تمديد أو تعديل LicenseChecker، قد تحتاج أيضًا إلى استدعاء طريقة finishCheck() في LicenseChecker لإزالة أي اتصالات مفتوحة بتنسيق IPC.

تطبيق Devicelimiter

في بعض الحالات، قد تحتاج إلى وضع حدّ لجهاز Policy في عدد الأجهزة الفعلية المسموح لها باستخدام ترخيص واحد. وسيمنع هذا المستخدم من نقل التطبيق المرخَّص إلى عدد من الأجهزة واستخدام التطبيق على تلك الأجهزة التي تحمل رقم تعريف الحساب نفسه. وسيمنع أيضًا المستخدم من "مشاركة" التطبيق من خلال تقديم معلومات الحساب المرتبطة بالترخيص للأفراد الآخرين، الذين يمكنهم بعد ذلك تسجيل الدخول إلى هذا الحساب على أجهزتهم والوصول إلى الترخيص للتطبيق.

يوفر LVL الترخيص لكل جهاز من خلال توفير واجهة DeviceLimiter توضح طريقة واحدة، allowDeviceAccess(). أثناء معالجة برنامج License يتعلّق باستجابة معيّنة من خادم الترخيص، يتم استدعاء allowDeviceAccess()، مع تمرير سلسلة رقم تعريف المستخدم المستخرجة من الاستجابة.

إذا كنت لا تريد إتاحة وضع قيود على الأجهزة، ليس عليك تنفيذ أي إجراءات، لأنّ الفئة LicenseChecker تستخدم تلقائيًا عملية تنفيذ تلقائية تُسمى NullDeviceLimiter. كما يشير اسمها، فإن NullDeviceLimiter هي فئة "no-op" تعرض طريقة allowDeviceAccess() ببساطة استجابة LICENSED لجميع المستخدمين والأجهزة.

تنبيه: لا ننصح باستخدام الترخيص حسب الجهاز لمعظم التطبيقات للأسباب التالية:

  • يتطلب منك توفير خادم خلفية لإدارة تعيين المستخدمين والأجهزة،
  • وقد يؤدي ذلك بدون قصد إلى منع المستخدم من الوصول إلى أحد التطبيقات التي تم شراؤها بشكل شرعي على جهاز آخر.

تشويش الرمز

لضمان أمان تطبيقك، خصوصًا مع تطبيق مدفوع يستخدم الترخيص و/أو القيود المخصصة والحماية، من المهم جدًا إخفاء مفاتيح فك تشفير رمز التطبيق. يؤدي تشويش التعليمات البرمجية بشكل صحيح إلى زيادة صعوبة فك تشفير رمز بايت للتطبيق وتعديله، مثلاً من خلال إزالة فحص الترخيص، ثم إعادة تجميع الرمز.

تتوفر العديد من برامج التشويش لتطبيقات Android، بما في ذلك ProGuard الذي يوفر أيضًا ميزات تحسين الرموز البرمجية. وننصح بشدة باستخدام ProGuard أو برنامج مشابه للتشويش على الرمز في جميع التطبيقات التي تستخدم ترخيص Google Play.

نشر تطبيق مرخَّص

عند الانتهاء من اختبار تنفيذ الترخيص، تكون جاهزًا لنشر التطبيق على Google Play. اتّبِع الخطوات العادية لإعداد التطبيق وتوقيعه ثم نشره.

كيفية الحصول على الدعم

إذا كانت لديك أسئلة أو إذا واجهت مشاكل أثناء تنفيذ النشر أو نشره، يُرجى استخدام موارد الدعم المدرَجة في الجدول أدناه. من خلال توجيه استعلاماتك إلى المنتدى الصحيح، يمكنك الحصول على الدعم الذي تحتاجه بسرعة أكبر.

الجدول 2. مصادر دعم المطوّرين بخصوص خدمة ترخيص Google Play

نوع الدعم المورد مجموعة المواضيع
مشاكل التطوير والاختبار مجموعات Google: android-developers تنزيل LVL ودمجها، ومشاريع المكتبة، وPolicy أسئلة، وأفكار تجربة المستخدم، والتعامل مع الردود، Obfuscator، IPC، إعداد بيئة الاختبار
Stack Overflow: http://stackoverflow.com/questions/لإعلانك/android
مشاكل الحسابات والنشر والنشر منتدى مساعدة Google Play حسابات الناشرين، وزوج مفاتيح الترخيص، وحسابات الاختبار، واستجابات الخادم، واستجابات الاختبار، ونشر التطبيق ونتائجه
الأسئلة الشائعة حول الدعم المتعلّق بالترخيص
أداة تتبُّع مشاكل LVL أداة تتبُّع مشاكل مشروع الترخيص تقارير الأخطاء والمشكلات المتعلقة تحديدًا بفئات رمز المصدر LVL وعمليات تنفيذ الواجهة

للحصول على معلومات عامة حول كيفية النشر في المجموعات المدرجة أعلاه، راجع قسم موارد المنتدى في صفحة موارد دعم المطوّرين.

مراجع إضافية

يقدّم نموذج التطبيق المُرفَق مع LVL مثالًا كاملاً عن كيفية بدء فحص الترخيص والتعامل مع النتيجة في فئة MainActivity.