افزودن درستی‌سنجی پروانه سمت کارخواه به برنامه

هشدار: وقتی برنامه‌تان فرایند درستی‌سنجی پروانه را در سمت کارخواه انجام می‌دهد، برای مهاجمان احتمالی آسان‌تر است که منطق مرتبط با این فرایند درستی‌سنجی را تغییر دهند یا حذف کنند.

به همین دلیل، قویاً توصیه می‌کنیم درستی‌سنجی پروانه را در سمت سرور انجام دهید.

پس‌از راه‌اندازی حساب ناشر و محیط توسعه نرم‌افزار (به راه‌اندازی برای صدور پروانه مراجعه کنید)، آماده‌اید تا درستی‌سنجی پروانه را بااستفاده از «کتابخانه درستی‌سنجی پروانه» (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 اعلام کنید، زیرا «ابزارهای کیت توسعه نرم‌افزار» آن را در مانیفست‌های برنامه‌های وابسته ادغام نخواهد کرد. درعوض، باید این اجازه را در مانیفست هر برنامه وابسته تعریف کنید.

اجرای خط‌مشی

سرویس صدور پروانه Google Play خودش تعیین نمی‌کند که آیا باید به کاربر خاصی با پروانه خاصی اجازه دسترسی به برنامه شما داده شود یا نه. بلکه این مسئولیت به پیاده‌سازی Policy واگذار می‌شود که شما در برنامه‌تان ارائه می‌دهید.

خط‌مشی میانایی است که توسط LVL اعلام شده است و برای نگهداری منطق برنامه شما برای اجازه دادن یا ندادن به دسترسی کاربر، براساس نتیجه بررسی پروانه طراحی شده است. برای استفاده از LVL، برنامه شما باید Policy را پیاده‌سازی کند.

میانای Policy دو روش allowAccess() و processServerResponse() را اعلام می‌کند که توسط نمونه LicenseChecker هنگام پردازش پاسخ از سرور پروانه فراخوانی می‌شوند. همچنین یک enum به نام LicenseResponse را اعلام می‌کند که مقدار پاسخ پروانه ارسال‌شده در تماس‌ها به processServerResponse() را مشخص می‌کند.

  • processServerResponse() به شما امکان می‌دهد داده‌های پاسخ خام دریافتی از سرور صدور پروانه را پیش‌پردازش کنید، قبل‌از اینکه تصمیم بگیرید دسترسی را اعطا کنید یا نه.

    یک پیاده‌سازی معمولی برخی یا همه فیلدها را از پاسخ پروانه استخراج می‌کند و داده‌ها را به‌صورت محلی در یک فضای ذخیره‌سازی پایدار، مانند فضای ذخیره‌سازی SharedPreferences، ذخیره می‌کند تا اطمینان حاصل شود که داده‌ها در سراسر فراخوانی‌های برنامه و چرخه‌های روشن/خاموش دستگاه قابل دسترسی هستند. برای مثال، یک Policy مُهر زمان آخرین بررسی موفقیت‌آمیز پروانه، تعداد دفعات تکرار، دوره اعتبار پروانه، و اطلاعات مشابه را در یک فضای ذخیره‌سازی پایدار حفظ می‌کند، به‌جای اینکه هر بار که برنامه راه‌اندازی می‌شود، مقادیر را بازنشانی کند.

    هنگام ذخیره کردن داده‌های پاسخ به‌صورت محلی، Policy باید مطمئن شود که داده‌ها مبهم‌سازی شده‌اند (به پیاده‌سازی مبهم‌ساز در زیر مراجعه کنید).

  • allowAccess() براساس هرگونه داده پاسخ پروانه دردسترس (از سرور صدور پروانه یا از حافظه نهان) یا سایر اطلاعات مختص برنامه، تعیین می‌کند که آیا به کاربر اجازه دسترسی به برنامه شما داده شود یا نه. برای مثال، پیاده‌سازی شما از allowAccess() می‌تواند معیارهای دیگری را درنظر بگیرد، مثل استفاده یا داده‌های دیگری که از سرور زیرینه بازیابی شده است. در همه موارد، پیاده‌سازی allowAccess() باید فقط درصورتی true را برگرداند که کاربر برای استفاده از برنامه پروانه داشته باشد (همان‌طور که سرور صدور پروانه تعیین کرده است) یا اگر مشکل شبکه یا سیستم گذرا وجود داشته باشد که مانع از تکمیل بررسی پروانه شود. در چنین مواردی، پیاده‌سازی شما می‌تواند تعداد پاسخ‌های تکرار را حفظ کند و به‌طور موقت اجازه دسترسی بدهد تا زمانی که بررسی پروانه بعدی تکمیل شود.

برای ساده کردن فرایند افزودن صدور پروانه به برنامه‌تان و برای ارائه تصویری از نحوه طراحی Policy، «کتابخانه تأیید پروانه» شامل دو پیاده‌سازی کامل Policy است که می‌توانید بدون اصلاح از آن‌ها استفاده کنید یا آن‌ها را با نیازهایتان تطبیق دهید:

  • ServerManagedPolicy، یک Policy انعطاف‌پذیر که از تنظیمات ارائه‌شده توسط سرور و پاسخ‌های ذخیره‌شده در حافظه نهان برای مدیریت دسترسی در شرایط مختلف شبکه استفاده می‌کند، و
  • StrictPolicy، که هیچ داده پاسخی را ذخیره موقت نمی‌کند و فقط درصورتی اجازه دسترسی می‌دهد که سرور پاسخ دارای پروانه برگرداند.

برای اکثر برنامه‌ها، استفاده از ServerManagedPolicy به‌شدت توصیه می‌شود. ServerManagedPolicy پیش‌فرض LVL است و با برنامه نمونه LVL ادغام شده است.

رهنمودهای خط‌مشی‌های سفارشی

در پیاده‌سازی صدور پروانه، می‌توانید از یکی از خط‌مشی‌های کامل ارائه‌شده در LVL‏ (ServerManagedPolicy یا StrictPolicy) استفاده کنید یا می‌توانید خط‌مشی سفارشی ایجاد کنید. برای هر نوع خط‌مشی سفارشی، چندین نکته مهم طراحی وجود دارد که باید در پیاده‌سازی خود درک کنید و درنظر بگیرید.

سرور صدور پروانه محدودیت‌های درخواست عمومی را اعمال می‌کند تا از استفاده بیش‌ازحد از منابعی که می‌تواند منجر به عدم ارائه خدمات شود جلوگیری کند. وقتی برنامه‌ای از حد درخواست فراتر می‌رود، سرور صدور پروانه پاسخ ۵۰۳ برمی‌گرداند که به‌عنوان خطای عمومی سرور به برنامه شما منتقل می‌شود. این یعنی تا زمانی که حد استفاده بازنشانی نشود، هیچ پاسخ پروانه‌ای برای کاربر دردسترس نخواهد بود، که می‌تواند برای مدت نامحدودی بر کاربر تأثیر بگذارد.

اگر درحال طراحی خط‌مشی سفارشی هستید، توصیه می‌کنیم که Policy:

  1. جدیدترین پاسخ موفقیت‌آمیز پروانه را در فضای ذخیره‌سازی پایدار محلی ذخیره موقت (و به‌درستی مبهم‌سازی) می‌کند.
  2. پاسخ ذخیره‌شده در حافظه نهان را برای همه بررسی‌های پروانه برمی‌گرداند، تا زمانی که پاسخ ذخیره‌شده در حافظه نهان معتبر باشد، به‌جای اینکه درخواستی به سرور صدور پروانه ارسال کند. تنظیم اعتبار پاسخ براساس VT اضافی ارائه‌شده توسط سرور به‌شدت توصیه می‌شود. برای اطلاعات بیشتر، موارد اضافی پاسخ سرور را ببینید.
  3. اگر درخواست‌هایی را که منجر به خطا شده‌اند دوباره امتحان می‌کنید، از دوره عقب‌گرد نمایی استفاده می‌کند. توجه داشته باشید که کارخواه Google Play به‌طور خودکار درخواست‌های ناموفق را دوباره امتحان می‌کند، بنابراین در اکثر موارد نیازی نیست Policy شما آن‌ها را دوباره امتحان کند.
  4. یک «مهلت ارفاقی» ارائه می‌دهد که به کاربر اجازه می‌دهد برای مدت زمان یا تعداد دفعات استفاده محدود به برنامه شما دسترسی داشته باشد، درحالی‌که بررسی پروانه درحال تکرار است. مهلت ارفاقی با اجازه دادن به دسترسی تا زمانی که بررسی پروانه بعدی با موفقیت تکمیل شود، به نفع کاربر است و با قرار دادن محدودیت سخت بر دسترسی به برنامه شما زمانی که پاسخ پروانه معتبری دردسترس نیست، به نفع شما است.

طراحی Policy طبق دستورالعمل‌های فهرست‌شده در بالا بسیار مهم است، زیرا بهترین تجربه ممکن را برای کاربران تضمین می‌کند و درعین‌حال به شما کنترل مؤثری بر برنامه‌تان می‌دهد، حتی در شرایط خطا.

توجه داشته باشید که هر Policy می‌تواند از تنظیمات ارائه‌شده توسط سرور صدور پروانه برای کمک به مدیریت اعتبار و ذخیره‌سازی، مهلت ارفاقی برای امتحان مجدد، و غیره استفاده کند. استخراج تنظیمات ارائه‌شده توسط سرور ساده است و استفاده از آن‌ها به‌شدت توصیه می‌شود. برای نمونه‌ای از نحوه استخراج و استفاده از موارد اضافی، پیاده‌سازی ServerManagedPolicy را ببینید. برای مشاهده فهرست تنظیمات سرور و اطلاعات مربوط به نحوه استفاده از آن‌ها، به موارد اضافی پاسخ سرور مراجعه کنید.

ServerManagedPolicy

LVL شامل پیاده‌سازی کامل و توصیه‌شده‌ای از میانای Policy به‌نام ServerManagedPolicy است. پیاده‌سازی با کلاس‌های LVL ادغام شده است و به‌عنوان Policy پیش‌فرض در کتابخانه عمل می‌کند.

ServerManagedPolicy همه رسیدگی‌ها را برای پاسخ‌های پروانه و تلاش مجدد ارائه می‌دهد. این بدافزار همه داده‌های پاسخ را به‌طور محلی در فایل SharedPreferences ذخیره می‌کند و آن را با پیاده‌سازی Obfuscator برنامه پنهان می‌کند. این کار تضمین می‌کند که داده‌های پاسخ پروانه ایمن است و در چرخه‌های خاموش و روشن شدن دستگاه حفظ می‌شود. ServerManagedPolicy پیاده‌سازی‌های مشخصی از روش‌های میانای processServerResponse() و allowAccess() ارائه می‌دهد و همچنین شامل مجموعه‌ای از روش‌ها و انواع پشتیبانی برای مدیریت پاسخ‌های پروانه است.

نکته مهم این است که یکی از ویژگی‌های کلیدی ServerManagedPolicy استفاده از تنظیمات ارائه شده توسط سرور به عنوان مبنایی برای مدیریت مجوزها در طول دوره بازپرداخت برنامه و در شرایط مختلف شبکه و خطا است. وقتی برنامه‌ای برای بررسی پروانه با سرور Google Play تماس می‌گیرد، سرور چندین تنظیم را به‌عنوان جفت‌های کلید-مقدار در فیلد موارد اضافی برخی‌از انواع پاسخ پروانه اضافه می‌کند. برای مثال، سرور مقادیر توصیه‌شده‌ای را برای دوره اعتبار پروانه برنامه، مهلت ارفاقی امتحان مجدد، و حداکثر تعداد مجاز امتحان مجدد، و غیره ارائه می‌دهد. ‫ServerManagedPolicy مقادیر را از پاسخ پروانه در روش processServerResponse() خود استخراج می‌کند و آن‌ها را در روش allowAccess() خود بررسی می‌کند. برای مشاهده فهرست تنظیمات ارائه‌شده توسط سرور که ServerManagedPolicy از آن‌ها استفاده می‌کند، به موارد اضافی پاسخ سرور مراجعه کنید.

برای راحتی، بهترین عملکرد، و بهره‌مندی از تنظیمات پروانه از سرور Google Play، استفاده از ServerManagedPolicy به‌عنوان پروانه Policy اکیداً توصیه می‌شود.

اگر نگران امنیت داده‌های پاسخ پروانه هستید که به‌طور محلی در SharedPreferences ذخیره شده است، می‌توانید از الگوریتم مبهم‌سازی قوی‌تری استفاده کنید یا Policy سخت‌گیرانه‌تری طراحی کنید که داده‌های پروانه را ذخیره نکند. LVL شامل نمونه‌ای از چنین Policy است — برای اطلاعات بیشتر، StrictPolicy را ببینید.

برای استفاده از ServerManagedPolicy، کافی است آن را به «فعالیت» خود وارد کنید، یک نمونه ایجاد کنید، و هنگام ساختن LicenseChecker خود، مرجعی را به نمونه ارسال کنید. برای اطلاعات بیشتر، نمونه‌سازی LicenseChecker و LicenseCheckerCallback را ببینید.

StrictPolicy

LVL شامل پیاده‌سازی کامل جایگزینی از میانای Policy به‌نام StrictPolicy است. پیاده‌سازی StrictPolicy خط‌مشی محدودکننده‌تری نسبت‌به ServerManagedPolicy ارائه می‌دهد، زیرا به کاربر اجازه دسترسی به برنامه را نمی‌دهد مگر اینکه در زمان دسترسی، پاسخ پروانه از سرور دریافت شود که نشان دهد کاربر دارای پروانه است.

ویژگی اصلی StrictPolicy این است که هیچ‌کدام از داده‌های پاسخ پروانه را به‌صورت محلی در فضای ذخیره‌سازی دائمی ذخیره نمی‌کند. چون هیچ داده‌ای ذخیره نمی‌شود، درخواست‌های تکرار مجدد ردیابی نمی‌شوند و از پاسخ‌های ذخیره‌شده در حافظه نهان نمی‌توان برای انجام بررسی‌های پروانه استفاده کرد. Policy فقط در شرایط زیر اجازه دسترسی می‌دهد:

  • پاسخ پروانه از سرور صدور پروانه دریافت می‌شود، و
  • پاسخ پروانه نشان می‌دهد که کاربر برای دسترسی به برنامه پروانه دارد.

اگر نگرانی اصلی شما این است که مطمئن شوید در همه موارد ممکن، هیچ کاربری اجازه دسترسی به برنامه را نخواهد داشت مگر اینکه تأیید شود کاربر در زمان استفاده دارای پروانه است، استفاده از StrictPolicy مناسب است. علاوه‌براین، Policy امنیت کمی بیشتر از ServerManagedPolicy ارائه می‌دهد — چون هیچ داده‌ای به‌صورت محلی ذخیره نمی‌شود، هیچ راهی وجود ندارد که کاربر مخرب بتواند داده‌های ذخیره‌شده را دستکاری کند و به برنامه دسترسی پیدا کند.

درعین‌حال، این Policy چالشی برای کاربران عادی ایجاد می‌کند، زیرا به این معنی است که وقتی اتصال شبکه (تلفن همراه یا Wi-Fi) دردسترس نباشد، نمی‌توانند به برنامه دسترسی داشته باشند. عوارض جانبی دیگر این است که برنامه شما درخواست‌های بررسی پروانه بیشتری به سرور ارسال خواهد کرد، زیرا استفاده از پاسخ ذخیره‌شده در حافظه نهان امکان‌پذیر نیست.

به‌طور کلی، این خط‌مشی نشان‌دهنده معاوضه تاحدودی از راحتی کاربر برای امنیت مطلق و کنترل بر دسترسی است. پیش‌از استفاده از این Policy، مبادله پایاپای را به‌دقت درنظر بگیرید.

برای استفاده از StrictPolicy، کافی است آن را به «فعالیت» خود وارد کنید، نمونه‌ای ایجاد کنید، و هنگام ساختن LicenseChecker، مرجعی به آن ارسال کنید. برای اطلاعات بیشتر، نمونه‌سازی LicenseChecker و LicenseCheckerCallback را ببینید.

یک پیاده‌سازی معمولی Policy باید داده‌های پاسخ پروانه را برای یک برنامه در یک فضای ذخیره‌سازی پایدار ذخیره کند تا در سراسر فراخوانی‌های برنامه و چرخه‌های روشن/خاموش دستگاه قابل‌دسترسی باشد. برای مثال، یک Policy مُهر زمان آخرین بررسی موفقیت‌آمیز پروانه، تعداد دفعات تکرار، دوره اعتبار پروانه، و اطلاعات مشابه را در یک فضای ذخیره‌سازی پایدار حفظ می‌کند، به‌جای اینکه هر بار که برنامه راه‌اندازی می‌شود، مقادیر را بازنشانی کند. Policy پیش‌فرض موجود در LVL،‏ ServerManagedPolicy، داده‌های پاسخ پروانه را در نمونه SharedPreferences ذخیره می‌کند تا مطمئن شود داده‌ها ماندگار هستند.

ازآنجایی‌که Policy از داده‌های پاسخ پروانه ذخیره‌شده برای تعیین اینکه آیا دسترسی به برنامه مجاز است یا نه استفاده می‌کند، باید اطمینان حاصل کند که هرگونه داده ذخیره‌شده ایمن است و نمی‌تواند توسط کاربر ریشه در دستگاه دوباره استفاده یا دستکاری شود. به‌طور خاص، Policy باید همیشه داده‌ها را قبل‌از ذخیره کردن بااستفاده از کلیدی که برای برنامه و دستگاه منحصربه‌فرد است مبهم‌سازی کند. مبهم‌سازی بااستفاده از کلیدی که هم مختص برنامه و هم مختص دستگاه باشد بسیار مهم است، زیرا مانع از هم‌رسانی داده‌های مبهم‌شده بین برنامه‌ها و دستگاه‌ها می‌شود.

‫LVL به برنامه کمک می‌کند تا داده‌های پاسخ پروانه خود را به‌صورت ایمن و پایدار ذخیره کند. اول اینکه، Obfuscator میانایی ارائه می‌دهد که به برنامه شما اجازه می‌دهد الگوریتم ابهام‌سازی انتخابی خود را برای داده‌های ذخیره‌شده ارائه دهد. با تکیه بر این، LVL کلاس کمکی PreferenceObfuscator را ارائه می‌دهد که بیشتر کار فراخوانی کلاس Obfuscator برنامه و خواندن و نوشتن داده‌های مبهم‌شده در نمونه SharedPreferences را انجام می‌دهد.

LVL یک پیاده‌سازی کامل Obfuscator به نام AESObfuscator ارائه می‌دهد که از رمزگذاری AES برای مبهم‌سازی داده‌ها استفاده می‌کند. می‌توانید از AESObfuscator در برنامه‌تان بدون اصلاح استفاده کنید یا می‌توانید آن را با نیازهایتان تطبیق دهید. اگر از Policy (مانند ServerManagedPolicy) استفاده می‌کنید که داده‌های پاسخ پروانه را ذخیره می‌کند، استفاده از AESObfuscator به‌عنوان پایه پیاده‌سازی Obfuscator شما اکیداً توصیه می‌شود. برای کسب اطلاعات بیشتر، بخش بعدی را ببینید.

AESObfuscator

LVL شامل پیاده‌سازی کامل و توصیه‌شده‌ای از میانای Obfuscator به‌نام AESObfuscator است. این پیاده‌سازی با برنامه نمونه LVL ادغام شده است و به‌عنوان Obfuscator پیش‌فرض در کتابخانه عمل می‌کند.

AESObfuscator بااستفاده از AES برای رمزگذاری و رمزگشایی داده‌ها هنگام نوشتن یا خواندن از فضای ذخیره‌سازی، ابهام‌سازی امنی برای داده‌ها فراهم می‌کند. Obfuscator رمزگذاری را بااستفاده از سه فیلد داده ارائه‌شده توسط برنامه مقداردهی اولیه می‌کند:

  1. یک سالت — آرایه‌ای از بایت‌های تصادفی برای استفاده در هر (رفع) ابهام.
  2. رشته شناسه برنامه، معمولاً نام بسته برنامه.
  3. رشته شناسه دستگاه، برگرفته از منابع خاص دستگاه تا حد امکان، برای اینکه تا حد ممکن منحصربه‌فرد باشد.

برای استفاده از AESObfuscator، ابتدا آن را به «فعالیت» خود وارد کنید. یک آرایه نهایی ثابت خصوصی برای نگهداری بایت‌های سالت اعلام کنید و آن را با ۲۰ بایت تولیدشده به‌صورت تصادفی مقداردهی اولیه کنید.

کاتلین

// 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
)

جاوا

...
    // 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 تأثیر بگذارد (زیرا برخی‌از اجازه‌ها می‌توانند باعث شوند ابزارهای ساخت کیت توسعه نرم‌افزار <uses-feature> مربوطه را اضافه کنند).

درنهایت، نمونه‌ای از AESObfuscator بسازید و نمک، شناسه برنامه، و شناسه دستگاه را به آن منتقل کنید. می‌توانید نمونه را مستقیماً بسازید، درحالی‌که Policy و LicenseChecker را می‌سازید. برای مثال:

کاتلین

    ...
    // Construct the LicenseChecker with a Policy.
    private val checker = LicenseChecker(
            this,
            ServerManagedPolicy(this, AESObfuscator(SALT, packageName, deviceId)),
            BASE64_PUBLIC_KEY
    )
    ...

جاوا

    ...
    // 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 ببینید.

بررسی پروانه از Activity

پس‌از پیاده‌سازی Policy برای مدیریت دسترسی به برنامه‌تان، گام بعدی افزودن بررسی پروانه به برنامه‌تان است که درصورت نیاز، درخواستی را به سرور صدور پروانه ارسال می‌کند و دسترسی به برنامه را براساس پاسخ پروانه مدیریت می‌کند. همه کارهای مربوط به افزودن بررسی پروانه و رسیدگی به پاسخ در فایل منبع اصلی Activity شما انجام می‌شود.

برای افزودن بررسی پروانه و رسیدگی به پاسخ، باید:

  1. افزودن واردشده‌ها
  2. LicenseCheckerCallback را به‌عنوان یک کلاس داخلی خصوصی پیاده‌سازی کنید
  3. یک Handler ایجاد کنید تا از LicenseCheckerCallback به رشته میانای کاربر پست کند
  4. نمونه‌سازی LicenseChecker و LicenseCheckerCallback
  5. Call checkAccess() برای شروع بررسی پروانه
  6. جاسازی کلید عمومی برای صدور پروانه
  7. روش onDestroy() مربوط به LicenseChecker را فراخوانی کنید تا اتصال‌های IPC بسته شود.

بخش‌های زیر این وظایف را شرح می‌دهند.

نمای کلی بررسی پروانه و پاسخ

در بیشتر موارد، باید بررسی پروانه را به Activity اصلی برنامه‌تان در روش onCreate() اضافه کنید. این تضمین می‌کند که وقتی کاربر مستقیماً برنامه شما را راه‌اندازی می‌کند، بررسی پروانه بلافاصله فراخوانده می‌شود. در برخی موارد، می‌توانید بررسی پروانه را در مکان‌های دیگر نیز اضافه کنید. برای مثال، اگر برنامه‌تان شامل چند مؤلفه «فعالیت» است که برنامه‌های دیگر می‌توانند با Intent شروع کنند، می‌توانید بررسی‌های پروانه را در آن «فعالیت‌ها» اضافه کنید.

بررسی پروانه شامل دو کنش اصلی است:

  • فراخوانی روشی برای شروع بررسی پروانه — در LVL، این فراخوانی روش checkAccess() از شیء LicenseChecker است که شما می‌سازید.
  • یک فراخوانی برگشتی که نتیجه بررسی پروانه را برمی‌گرداند. در LVL، این یک میانای LicenseCheckerCallback است که شما پیاده‌سازی می‌کنید. رابط دو روش allow() و dontAllow() را اعلام می‌کند که توسط کتابخانه براساس نتیجه بررسی پروانه فراخوانی می‌شوند. این دو روش را با هر منطقی که نیاز دارید پیاده‌سازی می‌کنید تا به کاربر اجازه دهید یا ندهید به برنامه‌تان دسترسی داشته باشد. توجه داشته باشید که این روش‌ها تعیین نمی‌کنند که آیا دسترسی مجاز است یا نه — این تعیین برعهده پیاده‌سازی Policy شما است. بلکه این روش‌ها صرفاً رفتارهای برنامه را برای نحوه مجاز کردن و مجاز نکردن دسترسی (و رسیدگی به خطاهای برنامه) ارائه می‌دهند.

    روش‌های allow() و dontAllow() «دلیلی» برای پاسخشان ارائه می‌دهند که می‌تواند یکی از مقادیر Policy،‏ LICENSED،‏ NOT_LICENSED، یا RETRY باشد. به‌ویژه، باید موردی را که در آن روش پاسخ RETRY را برای dontAllow() دریافت می‌کند مدیریت کنید و دکمه «امتحان مجدد» را به کاربر ارائه دهید، که ممکن است به‌دلیل دردسترس نبودن سرویس درطول درخواست اتفاق افتاده باشد.

شکل ۱. نمای کلی تعامل بررسی پروانه معمولی.

نمودار بالا نشان می‌دهد که بررسی پروانه معمولی چگونه انجام می‌شود:

  1. کد در «فعالیت» اصلی برنامه، اشیاء LicenseCheckerCallback و LicenseChecker را نمونه‌سازی می‌کند. هنگام ساختن LicenseChecker، کد در Context، یک پیاده‌سازی Policy برای استفاده، و کلید عمومی حساب ناشر برای صدور پروانه به‌عنوان پارامترها عبور می‌کند.
  2. سپس کد روش checkAccess() را در شیء LicenseChecker فراخوانی می‌کند. پیاده‌سازی روش با فراخوانی Policy تعیین می‌کند که آیا پاسخ پروانه معتبری به‌صورت محلی در SharedPreferences ذخیره شده است یا نه.
    • اگر چنین باشد، پیاده‌سازی checkAccess() allow() را فراخوانی می‌کند.
    • درغیراین‌صورت، LicenseChecker درخواست بررسی پروانه را آغاز می‌کند که به سرور صدور پروانه ارسال می‌شود.

    توجه: وقتی بررسی پروانه را برای برنامه پیش‌نویس انجام می‌دهید، سرور صدور پروانه همیشه LICENSED را برمی‌گرداند.

  3. وقتی پاسخی دریافت می‌شود، LicenseChecker یک LicenseValidator ایجاد می‌کند که داده‌های پروانه امضاشده را تأیید می‌کند و فیلدهای پاسخ را استخراج می‌کند، سپس آن‌ها را برای ارزیابی بیشتر به 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)، باید پیاده‌سازی مبهم‌ساز را نیز ارائه دهد.

افزودن واردات

ابتدا، فایل کلاس «فعالیت» اصلی برنامه را باز کنید و LicenseChecker و LicenseCheckerCallback را از بسته LVL وارد کنید.

کاتلین

import com.google.android.vending.licensing.LicenseChecker
import com.google.android.vending.licensing.LicenseCheckerCallback

جاوا

import com.google.android.vending.licensing.LicenseChecker;
import com.google.android.vending.licensing.LicenseCheckerCallback;

اگر از پیاده‌سازی پیش‌فرض Policy ارائه‌شده با LVL، ServerManagedPolicy استفاده می‌کنید، آن را نیز همراه با AESObfuscator وارد کنید. اگر از Policy یا Obfuscator سفارشی استفاده می‌کنید، آن‌ها را وارد کنید.

کاتلین

import com.google.android.vending.licensing.ServerManagedPolicy
import com.google.android.vending.licensing.AESObfuscator

جاوا

import com.google.android.vending.licensing.ServerManagedPolicy;
import com.google.android.vending.licensing.AESObfuscator;

LicenseCheckerCallback را به‌عنوان کلاس داخلی خصوصی پیاده‌سازی کنید

‫LicenseCheckerCallback میانایی است که توسط LVL برای مدیریت نتیجه بررسی پروانه ارائه شده است. برای پشتیبانی از صدور پروانه بااستفاده از LVL، باید LicenseCheckerCallback و روش‌های آن را برای مجاز کردن یا نکردن دسترسی به برنامه اجرا کنید.

نتیجه بررسی پروانه همیشه تماسی با یکی از روش‌های LicenseCheckerCallback است که براساس اعتبارسنجی محموله پاسخ، خود کد پاسخ سرور، و هرگونه پردازش اضافی ارائه‌شده توسط Policy شما انجام می‌شود. برنامه شما می‌تواند روش‌ها را به هر طریقی که لازم است پیاده‌سازی کند. به‌طور کلی، بهتر است روش‌ها را ساده نگه دارید و آن‌ها را به مدیریت وضعیت میانای کاربر و دسترسی به برنامه محدود کنید. اگر می‌خواهید پردازش بیشتری روی پاسخ‌های پروانه انجام دهید، مثلاً با تماس با سرور زیرینه یا اعمال محدودیت‌های سفارشی، باید آن کد را در Policy خود بگنجانید، نه اینکه آن را در روش‌های LicenseCheckerCallback قرار دهید.

در اکثر موارد، باید پیاده‌سازی LicenseCheckerCallback را به‌عنوان کلاس خصوصی در کلاس Activity اصلی برنامه‌تان اعلام کنید.

روش‌های allow() و dontAllow() را درصورت نیاز پیاده‌سازی کنید. برای شروع، می‌توانید از رفتارهای ساده مدیریت نتیجه در روش‌ها استفاده کنید، مثلاً نمایش نتیجه پروانه در کادر گفتگو. این کار به شما کمک می‌کند برنامه‌تان را زودتر اجرا کنید و می‌تواند به اشکال‌زدایی کمک کند. بعداً، پس‌از اینکه رفتارهای دقیقی را که می‌خواهید تعیین کردید، می‌توانید مدیریت پیچیده‌تری اضافه کنید.

برخی‌از پیشنهادها برای رسیدگی به پاسخ‌های بدون مجوز در dontAllow() عبارت‌اند از:

  • کادر گفتگوی «دوباره امتحان کنید» را به کاربر نشان دهید، ازجمله دکمه‌ای برای شروع بررسی پروانه جدید اگر reason ارائه‌شده Policy.RETRY باشد.
  • کادر گفتگوی «خرید این برنامه» را نمایش می‌دهد، ازجمله دکمه‌ای که کاربر را با پیوند عمیق به صفحه جزئیات برنامه در Google Play هدایت می‌کند و کاربر می‌تواند برنامه را از آنجا خریداری کند. برای اطلاعات بیشتر درباره نحوه راه‌اندازی چنین پیوندهایی، به پیوند دادن به محصولات مراجعه کنید.
  • اعلان Toast نمایش دهید که نشان می‌دهد ویژگی‌های برنامه محدود است چون پروانه ندارد.

مثال زیر نشان می‌دهد که چگونه برنامه نمونه LVL LicenseCheckerCallback را با روش‌هایی که نتیجه بررسی پروانه را در یک کادر گفتگو نمایش می‌دهند، پیاده‌سازی می‌کند.

کاتلین

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

جاوا

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() را فراخوانی کند.

ایجاد یک Handler برای پست کردن از LicenseCheckerCallback به رشته میانای کاربر

درطول بررسی پروانه، LVL درخواست را به برنامه Google Play ارسال می‌کند که ارتباط با سرور صدور پروانه را مدیریت می‌کند. LVL درخواست را ازطریق IPC ناهمگام (بااستفاده از Binder) ارسال می‌کند تا پردازش واقعی و ارتباط شبکه در رشته‌ای که توسط برنامه‌تان مدیریت می‌شود انجام نشود. به‌طور مشابه، وقتی برنامه Google Play نتیجه را دریافت می‌کند، یک روش فراخوانی را ازطریق IPC فراخوانی می‌کند که به‌نوبه خود در یک استخر رشته IPC در فرایند برنامه شما اجرا می‌شود.

کلاس LicenseChecker ارتباط IPC برنامه شما را با برنامه Google Play مدیریت می‌کند، ازجمله تماسی که درخواست را ارسال می‌کند و فراخوانی برگشتی که پاسخ را دریافت می‌کند. LicenseChecker همچنین درخواست‌های پروانه باز را ردیابی می‌کند و زمان‌های اتمام آن‌ها را مدیریت می‌کند.

برای اینکه بتواند زمان‌های اتمام را به‌درستی مدیریت کند و همچنین پاسخ‌های ورودی را بدون تأثیر بر رشته میانای کاربر برنامه شما پردازش کند، LicenseChecker یک رشته پس‌زمینه را در زمان نمونه‌سازی ایجاد می‌کند. در رشته، تمام پردازش نتایج بررسی پروانه را انجام می‌دهد، خواه نتیجه پاسخی باشد که از سرور دریافت شده است یا خطای اتمام مهلت. در پایان پردازش، LVL روش‌های LicenseCheckerCallback شما را از رشته پس‌زمینه فراخوانی می‌کند.

برای برنامه شما، این یعنی:

  1. در بسیاری از موارد، روش‌های LicenseCheckerCallback شما از یک رشته پس‌زمینه فراخوانی می‌شوند.
  2. آن روش‌ها نمی‌توانند وضعیت را به‌روز کنند یا پردازشی را در رشته میانای کاربر فراخوانی کنند، مگراینکه یک Handler در رشته میانای کاربر ایجاد کنید و روش‌های فراخوانی شما به Handler پست شوند.

اگر می‌خواهید روش‌های LicenseCheckerCallback شما رشته میانای کاربر را به‌روز کند، یک Handler را در روش onCreate() «فعالیت» اصلی نمونه‌سازی کنید، همان‌طور که در زیر نشان داده شده است. در این مثال، روش‌های LicenseCheckerCallback برنامه نمونه LVL (بالا را ببینید) displayResult() را فراخوانی می‌کنند تا رشته میانای کاربر را ازطریق روش post() «گرداننده» به‌روز کنند.

کاتلین

    private lateinit var handler: Handler

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        handler = Handler()
    }

جاوا

    private Handler handler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        handler = new Handler();
    }

سپس، در روش‌های LicenseCheckerCallback، می‌توانید از روش‌های Handler برای ارسال اشیای Runnable یا Message به Handler استفاده کنید. در اینجا نحوه ارسال Runnable به Handler در رشته میانای کاربر توسط برنامه نمونه موجود در LVL برای نمایش وضعیت پروانه نشان داده شده است.

کاتلین

private fun displayResult(result: String) {
    handler.post {
        statusText.text = result
        setProgressBarIndeterminateVisibility(false)
        checkLicenseButton.isEnabled = true
    }
}

جاوا

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 نمونه‌سازی کنید، همان‌طور که در مثال زیر نشان داده شده است. توجه داشته باشید که هنگام ساختن ServerManagedPolicy، باید ارجاعی به نمونه جدید Obfuscator ارسال کنید.

مثال زیر نمونه‌سازی LicenseChecker و LicenseCheckerCallback را از روش onCreate() کلاس «فعالیت» نشان می‌دهد.

کاتلین

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

جاوا

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() فراخوانی می‌کند:

کاتلین

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

جاوا

    @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‏ ۲۰۴۸ بیتی تولید می‌کند که برای صدور پروانه و صورت‌حساب درون‌برنامه استفاده می‌شود. جفت کلید به‌طور منحصربه‌فردی با برنامه مرتبط است. اگرچه جفت کلید با برنامه مرتبط است، اما با کلیدی که برای امضای برنامه‌هایتان استفاده می‌کنید (یا از آن مشتق شده است) یکسان نیست.

Google Play Console کلید عمومی را برای صدور پروانه برای هر توسعه‌دهنده‌ای که به سیستم Play Console وارد شده باشد آشکار می‌کند، اما کلید خصوصی را در مکانی امن از دید همه کاربران پنهان نگه می‌دارد. وقتی برنامه‌ای درخواست بررسی پروانه برای برنامه‌ای را می‌کند که در حسابتان منتشر شده است، سرور صدور پروانه پاسخ پروانه را بااستفاده از کلید خصوصی جفت کلید برنامه‌تان امضا می‌کند. وقتی LVL پاسخ را دریافت می‌کند، از کلید عمومی ارائه‌شده توسط برنامه برای تأیید امضای پاسخ مجوز استفاده می‌کند.

برای افزودن صدور پروانه به برنامه، باید کلید عمومی برنامه‌تان را برای صدور پروانه دریافت کنید و آن را در برنامه‌تان کپی کنید. در اینجا نحوه پیدا کردن کلید عمومی برنامه‌تان برای صدور پروانه آورده شده است:

  1. به کنسول Play Google بروید و به سیستم وارد شوید. مطمئن شوید به سیستم حسابی وارد شده‌اید که برنامه‌ای که برای آن پروانه صادر می‌کنید از آن منتشر شده است (یا منتشر خواهد شد).
  2. در صفحه جزئیات برنامه، پیوند سرویس‌ها و میاناهای برنامه‌سازی کاربردی را پیدا کنید و روی آن کلیک کنید.
  3. در صفحه سرویس‌ها و میاناهای برنامه‌سازی کاربردی، بخش صدور پروانه و صورت‌حساب درون‌برنامه را پیدا کنید. کلید عمومی شما برای صدور پروانه در فیلد کلید پروانه شما برای این برنامه ارائه شده است.

برای افزودن کلید عمومی به برنامه‌تان، کافی است رشته کلید را از فیلد کپی کنید و به‌عنوان مقدار متغیر رشته BASE64_PUBLIC_KEY در برنامه‌تان جای‌گذاری کنید. هنگام کپی کردن، مطمئن شوید که کل رشته کلید را بدون حذف هیچ نویسه‌ای انتخاب کرده‌اید.

در اینجا مثالی از برنامه نمونه LVL آورده شده است:

کاتلین

private const val BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... " //truncated for this example
class LicensingActivity : AppCompatActivity() {
    ...
}

جاوا

public class MainActivity extends Activity {
    private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... "; //truncated for this example
    ...
}

برای بستن اتصال‌های IPC، روش onDestroy()‎ مربوط به LicenseChecker را فراخوانی کنید

درنهایت، برای اینکه LVL بتواند قبل‌از تغییرات برنامه شما Context پاک‌سازی کند، تماسی به روش LicenseChecker onDestroy() از پیاده‌سازی onDestroy() «فعالیت» خود اضافه کنید. این فراخوانی باعث می‌شود LicenseChecker هرگونه اتصال IPC باز به ILicensingService برنامه Google Play را به‌درستی ببندد و هرگونه ارجاع محلی به سرویس و کنترل‌کننده را حذف کند.

عدم فراخوانی روش onDestroy() مربوط به LicenseChecker می‌تواند به بروز مشکلاتی در چرخه حیات برنامه‌تان منجر شود. برای مثال، اگر کاربر جهت صفحه را درحین فعال بودن بررسی پروانه تغییر دهد، برنامه Context ازبین می‌رود. اگر برنامه‌تان اتصال IPC مربوط به LicenseChecker را به‌درستی نبندد، هنگام دریافت پاسخ، برنامه‌تان ازکار خواهد افتاد. به‌طور مشابه، اگر کاربر درحین بررسی پروانه از برنامه شما خارج شود، برنامه شما هنگام دریافت پاسخ ازکار خواهد افتاد، مگر اینکه به‌درستی روش onDestroy() مربوط به LicenseChecker را برای قطع اتصال از سرویس فراخوانی کرده باشد.

در اینجا مثالی از برنامه نمونه موجود در LVL آورده شده است، که در آن mChecker نمونه LicenseChecker است:

کاتلین

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

جاوا

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

اگر LicenseChecker را گسترش می‌دهید یا اصلاح می‌کنید، ممکن است لازم باشد روش finishCheck() مربوط به LicenseChecker را نیز فراخوانی کنید تا همه اتصال‌های IPC باز پاک‌سازی شود.

پیاده‌سازی DeviceLimiter

در برخی موارد، ممکن است بخواهید Policy شما تعداد دستگاه‌های واقعی را که مجاز به استفاده از یک پروانه هستند محدود کند. این کار مانع از آن می‌شود که کاربر برنامه دارای پروانه را به چند دستگاه منتقل کند و از آن برنامه در آن دستگاه‌ها تحت همان شناسه حساب استفاده کند. همچنین از «هم‌رسانی» برنامه توسط کاربر با ارائه اطلاعات حساب مرتبط با پروانه به افراد دیگر جلوگیری می‌کند، زیرا این افراد می‌توانند به آن حساب در دستگاه‌هایشان وارد شوند و به پروانه برنامه دسترسی پیدا کنند.

LVL با ارائه میانای DeviceLimiter که یک روش واحد، allowDeviceAccess()، را اعلام می‌کند، از صدور پروانه برای هر دستگاه پشتیبانی می‌کند. وقتی LicenseValidator پاسخی را از سرور صدور پروانه مدیریت می‌کند، allowDeviceAccess() را فراخوانی می‌کند و رشته شناسه کاربر استخراج‌شده از پاسخ را ارسال می‌کند.

اگر نمی‌خواهید از محدودیت دستگاه پشتیبانی کنید، نیازی به انجام کاری نیست — کلاس LicenseChecker به‌طور خودکار از پیاده‌سازی پیش‌فرضی به‌نام NullDeviceLimiter استفاده می‌کند. همان‌طور که از نامش پیداست، NullDeviceLimiter یک کلاس «no-op» است که روش allowDeviceAccess() آن به‌سادگی پاسخ LICENSED را برای همه کاربران و دستگاه‌ها برمی‌گرداند.

احتیاط: صدور پروانه برای هر دستگاه برای اکثر برنامه‌ها توصیه نمی‌شود زیرا:

  • این روش مستلزم آن است که سرور زیرینه‌ای برای مدیریت نگاشت کاربران و دستگاه‌ها ارائه دهید، و
  • این امر ممکن است به‌طور ناخواسته منجر به این شود که کاربر از دسترسی به برنامه‌ای که به‌طور قانونی در دستگاه دیگری خریداری کرده است محروم شود.

مبهم‌سازی کد

برای اطمینان از امنیت برنامه‌تان، به‌ویژه برای برنامه پولی که از صدور پروانه و/یا محدودیت‌ها و محافظت‌های سفارشی استفاده می‌کند، بسیار مهم است که کد برنامه‌تان را مبهم کنید. مبهم‌سازی صحیح کدتان باعث می‌شود که کاربر مخرب به‌سختی بتواند بایت‌کد برنامه را تبدیل کد کند، آن را تغییر دهد (مثلاً با برداشتن بررسی پروانه)، و سپس آن را بازهمگردانی کند.

چندین برنامه مبهم‌ساز برای برنامه‌های Android دردسترس است، ازجمله ProGuard که ویژگی‌های بهینه‌سازی کد را نیز ارائه می‌دهد. استفاده از ProGuard یا برنامه‌ای مشابه برای مبهم‌سازی کد شما برای همه برنامه‌هایی که از «صدور پروانه Google Play» استفاده می‌کنند اکیداً توصیه می‌شود.

انتشار برنامه پروانه‌دار

وقتی آزمایش پیاده‌سازی پروانه را به‌پایان رساندید، آماده‌اید برنامه را در Google Play منتشر کنید. مراحل عادی را برای آماده‌سازی، امضا، و سپس انتشار برنامه دنبال کنید.

از کجا پشتیبانی دریافت کنید

اگر درحین پیاده‌سازی یا استقرار انتشار در برنامه‌هایتان سؤالی دارید یا با مشکلی مواجه شدید، لطفاً از منابع پشتیبانی فهرست‌شده در جدول زیر استفاده کنید. با هدایت کردن پرسمان‌هایتان به تالار گفتمان صحیح، می‌توانید پشتیبانی موردنیازتان را سریع‌تر دریافت کنید.

جدول ۲. منابع پشتیبانی توسعه‌دهنده برای «سرویس صدور پروانه Google Play».

نوع پشتیبانی منبع محدوده موضوعات
مشکلات توسعه و آزمایش گروه‌های Google: android-developers بارگیری و ادغام LVL، پروژه‌های کتابخانه، پرسش‌های Policy، ایده‌های تجربه کاربری، مدیریت پاسخ‌ها، Obfuscator، IPC، راه‌اندازی محیط آزمایشی
‫Stack Overflow: ‏ http://stackoverflow.com/questions/tagged/android
مشکلات حساب، انتشار، و استقرار تالار گفتمان راهنمایی Google Play حساب‌های ناشر، جفت کلید صدور پروانه، حساب‌های آزمایشی، پاسخ‌های سرور، پاسخ‌های آزمایشی، استقرار برنامه و نتایج
پرسشگان پشتیبانی صدور پروانه Market
ردیاب مشکل LVL ردیاب مشکل پروژه Marketlicensing گزارش‌های اشکال و مشکل مربوط به کلاس‌های کد منبع LVL و پیاده‌سازی‌های میانای آن

برای اطلاعات کلی درباره نحوه پست کردن در گروه‌های فهرست‌شده در بالا، به بخش منابع انجمن در صفحه «منابع پشتیبانی توسعه‌دهنده» مراجعه کنید.

منابع تکمیلی

برنامه نمونه‌ای که با LVL ارائه می‌شود، نمونه کاملی از نحوه شروع بررسی پروانه و مدیریت نتیجه در کلاس MainActivity ارائه می‌دهد.