Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Menambahkan Verifikasi Lisensi Sisi Klien ke Aplikasi Anda

Peringatan: Jika aplikasi Anda melakukan proses verifikasi lisensi di sisi klien, akan lebih mudah bagi penyerang untuk mengubah atau menghapus logika yang terkait dengan proses verifikasi ini.

Karena alasan ini, kami sangat mendorong Anda untuk melakukan verifikasi lisensi sisi server.

Setelah Anda menyiapkan akun publikasi dan lingkungan pengembangan (lihat Menyiapkan Pemberian Lisensi), tambahkan verifikasi lisensi pada aplikasi Anda dengan Library Verifikasi Lisensi (LVL).

Menambahkan verifikasi lisensi dengan LVL melibatkan tugas-tugas berikut:

  1. Menambahkan izin pemberian lisensi ke manifes aplikasi Anda.
  2. Mengimplementasikan Policy — Anda dapat memilih salah satu implementasi lengkap yang disediakan di LVL atau membuatnya sendiri.
  3. Mengimplementasikan Obfuscator, jika Policy Anda akan men-cache data respons lisensi.
  4. Menambahkan kode untuk memeriksa lisensi di Activity utama aplikasi Anda.
  5. Mengimplementasikan DeviceLimiter (opsional dan tidak direkomendasikan untuk sebagian besar aplikasi).

Bagian di bawah menjelaskan tugas-tugas tersebut. Setelah menyelesaikan integrasi, Anda dapat mengompilasi aplikasi dengan benar dan dapat memulai pengujian, seperti yang dijelaskan dalam Menyiapkan Lingkungan Pengujian.

Untuk ringkasan tentang kumpulan lengkap file sumber yang disertakan dalam LVL, lihat Ringkasan Antarmuka dan Class LVL.

Menambahkan Izin Pemberian Lisensi

Untuk menggunakan aplikasi Google Play guna mengirimkan pemeriksaan lisensi ke server, aplikasi Anda harus meminta izin yang tepat, com.android.vending.CHECK_LICENSE. Jika aplikasi Anda tidak mendeklarasikan izin pemberian lisensi, tetapi mencoba untuk memulai pemeriksaan lisensi, LVL akan menampilkan pengecualian keamanan.

Untuk meminta izin pemberian lisensi dalam aplikasi Anda, deklarasikan elemen <uses-permission> sebagai turunan dari <manifest>, seperti berikut:

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

Misalnya, berikut adalah cara aplikasi contoh LVL mendeklarasikan izin:

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

Catatan: Saat ini, Anda tidak dapat mendeklarasikan izin CHECK_LICENSE dalam manifes project library LVL, karena SDK Tools tidak akan menggabungkannya ke manifes aplikasi dependen. Sebagai gantinya, Anda harus mendeklarasikan izin di setiap manifes aplikasi dependen.

Mengimplementasikan Policy

Layanan pemberi lisensi Google Play tidak dengan sendirinya menentukan apakah pengguna tertentu dengan lisensi yang diberikan harus diberi akses ke aplikasi Anda. Sebaliknya, tanggung jawab tersebut diserahkan ke implementasi Policy yang Anda berikan dalam aplikasi.

Policy adalah antarmuka yang dideklarasikan oleh LVL yang dirancang untuk menangguhkan logika aplikasi Anda guna mengizinkan atau melarang akses pengguna, berdasarkan hasil pemeriksaan lisensi. Untuk menggunakan LVL, aplikasi Anda harus menyediakan implementasi Policy.

Antarmuka Policy mendeklarasikan dua metode, allowAccess() dan processServerResponse(), yang dipanggil oleh instance LicenseChecker saat memproses respons dari server lisensi. Antarmuka ini juga mendeklarasikan enum yang disebut LicenseResponse, yang menentukan nilai respons lisensi yang diteruskan dalam panggilan ke processServerResponse().

  • processServerResponse() memungkinkan Anda memproses terlebih dahulu data respons mentah yang diterima dari server pemberian lisensi sebelum memutuskan apakah akan memberikan akses atau tidak.

    Implementasi standar akan mengekstrak beberapa atau semua kolom dari respons lisensi dan menyimpan data secara lokal ke penyimpanan persisten, seperti melalui penyimpanan SharedPreferences, untuk memastikan bahwa data dapat diakses di seluruh pemanggilan aplikasi dan penyalaan ulang perangkat. Misalnya, Policy akan mempertahankan stempel waktu pemeriksaan lisensi terakhir yang berhasil, jumlah coba lagi, periode validitas lisensi, dan informasi serupa dalam penyimpanan persisten, alih-alih menyetel ulang nilai setiap kali aplikasi diluncurkan.

    Saat menyimpan data respons secara lokal, Policy harus memastikan bahwa data disamarkan (lihat Mengimplementasikan Obfuscator, di bawah).

  • allowAccess() menentukan apakah akan memberikan pengguna akses ke aplikasi Anda, berdasarkan data respons lisensi yang tersedia (dari server pemberi lisensi atau dari cache) atau informasi khusus aplikasi lainnya. Misalnya, implementasi allowAccess() dapat memperhitungkan kriteria tambahan, seperti penggunaan atau data lain yang diambil dari server backend. Dalam kasus apa pun, implementasi allowAccess() hanya akan menampilkan true jika pengguna memiliki lisensi untuk menggunakan aplikasi, seperti yang ditentukan oleh server pemberi lisensi, atau jika terdapat masalah sistem atau jaringan sementara yang membuat pemeriksaan lisensi tidak dapat diselesaikan. Dalam situasi seperti ini, implementasi Anda dapat mempertahankan sejumlah respons coba lagi dan mengizinkan akses untuk sementara hingga pemeriksaan lisensi berikutnya selesai.

Untuk menyederhanakan proses penambahan pemberian lisensi ke aplikasi Anda dan memberikan ilustrasi tentang bagaimana Policy harus didesain, LVL menyertakan dua implementasi Policy lengkap yang dapat Anda gunakan tanpa perlu diubah atau disesuaikan dengan kebutuhan Anda:

  • ServerManagedPolicy, Policy fleksibel yang menggunakan setelan yang disediakan server dan respons yang di-cache untuk mengelola akses di berbagai kondisi jaringan, dan
  • StrictPolicy, yang tidak men-cache data respons apa pun dan mengizinkan akses hanya jika server menampilkan respons berlisensi.

Untuk sebagian besar aplikasi, penggunaan ServerManagedPolicy sangat direkomendasikan. ServerManagedPolicy adalah default LVL dan terintegrasi dengan aplikasi contoh LVL.

Panduan untuk kebijakan kustom

Dalam implementasi pemberian lisensi, Anda dapat menggunakan salah satu kebijakan lengkap yang disediakan di LVL (ServerManagedPolicy atau StrictPolicy) atau Anda dapat membuat kebijakan kustom. Untuk jenis kebijakan kustom, ada beberapa poin desain penting yang perlu dipahami dan diperhitungkan dalam implementasi Anda.

Server pemberi lisensi menerapkan batas permintaan umum untuk mencegah penggunaan resource yang berlebihan yang dapat menyebabkan denial of service. Jika aplikasi melebihi batas permintaan, server pemberi lisensi akan menampilkan respons 503, yang diteruskan ke aplikasi Anda sebagai error server umum. Artinya, tidak ada respons lisensi yang akan tersedia bagi pengguna hingga batas tersebut disetel ulang, yang dapat memengaruhi pengguna untuk periode waktu yang tidak terbatas.

Jika Anda mendesain kebijakan kustom, sebaiknya Policy:

  1. Men-cache (dan meng-obfuscate dengan benar) respons lisensi terbaru yang berhasil di penyimpanan persisten lokal.
  2. Menampilkan respons yang di-cache untuk semua pemeriksaan lisensi, selama respons yang di-cache valid, bukan membuat permintaan ke server pemberian lisensi. Menyetel validitas respons berdasarkan tambahan VT yang disediakan server sangat direkomendasikan. Lihat Tambahan Respons Server untuk mengetahui informasi selengkapnya.
  3. Menggunakan periode backoff eksponensial, jika mencoba ulang permintaan apa pun yang menghasilkan error. Perhatikan bahwa klien Google Play akan otomatis mencoba ulang permintaan yang gagal, sehingga umumnya Anda tidak perlu meminta Policy untuk mencoba ulang.
  4. Menyediakan "masa tenggang" yang memungkinkan pengguna mengakses aplikasi Anda untuk waktu atau jumlah penggunaan terbatas, sementara pemeriksaan lisensi dicoba lagi. Masa tenggang akan menguntungkan pengguna karena izin akses diberikan hingga pemeriksaan lisensi berikutnya berhasil diselesaikan, dan menguntungkan Anda dengan adanya batas yang pasti pada akses ke aplikasi Anda saat tidak ada respons lisensi valid yang tersedia.

Penting untuk mendesain Policy Anda sesuai pedoman yang tercantum di atas karena akan memastikan pengalaman yang paling baik bagi pengguna, sekaligus memberikan kontrol yang efektif atas aplikasi Anda bahkan dalam kondisi error.

Perlu diperhatikan bahwa Policy apa pun dapat menggunakan setelan yang disediakan oleh server pemberian lisensi untuk membantu mengelola validitas dan pembuatan cache, mencoba ulang masa tenggang, dan lainnya. Mengekstrak setelan yang disediakan server sangat mudah dan penggunaannya sangat direkomendasikan. Lihat implementasi ServerManagedPolicy untuk contoh cara mengekstrak dan menggunakan tambahan. Untuk daftar setelan server dan informasi tentang cara menggunakannya, lihat Tambahan Respons Server.

ServerManagedPolicy

LVL menyertakan implementasi lengkap dan direkomendasikan untuk antarmuka Policy yang disebut ServerManagedPolicy. Implementasi tersebut diintegrasikan dengan class LVL dan berfungsi sebagai Policy default di library.

ServerManagedPolicy menyediakan semua penanganan untuk respons coba lagi dan lisensi. Antarmuka ini men-cache semua data respons secara lokal dalam file SharedPreferences, menyamarkannya dengan implementasi Obfuscator aplikasi. Ini memastikan bahwa data respons lisensi aman dan bertahan di seluruh penyalaan ulang perangkat. ServerManagedPolicy menyediakan implementasi konkrit untuk metode antarmuka processServerResponse() dan allowAccess(), dan juga menyertakan sekumpulan metode serta jenis pendukung untuk mengelola respons lisensi.

Yang terpenting, fitur utama ServerManagedPolicy adalah penggunaan setelan yang disediakan server sebagai dasar untuk mengelola pemberian lisensi sepanjang periode pengembalian dana aplikasi serta melalui berbagai kondisi jaringan dan error. Saat aplikasi menghubungi server Google Play untuk pemeriksaan lisensi, server akan menambahkan beberapa setelan sebagai key-value pair dalam kolom tambahan dari jenis respons lisensi tertentu. Misalnya, server memberikan nilai yang direkomendasikan untuk periode validitas lisensi aplikasi, masa tenggang coba ulang, dan jumlah coba lagi maksimum yang diperbolehkan, di antara fitur lainnya. ServerManagedPolicy akan mengekstrak nilai dari respons lisensi dalam metode processServerResponse()-nya dan memeriksanya dalam metode allowAccess()-nya. Untuk daftar setelan yang disediakan server yang digunakan oleh ServerManagedPolicy, lihat Tambahan Respons Server.

Untuk kenyamanan, performa terbaik, dan manfaat menggunakan setelan lisensi dari server Google Play, menggunakan ServerManagedPolicy sebagai Policy pelisensian sangat direkomendasikan.

Jika khawatir dengan keamanan data respons lisensi yang disimpan secara lokal di SharedPreferences, Anda dapat menggunakan algoritme obfuscation yang lebih kuat atau merancang Policy yang lebih ketat yang tidak menyimpan data lisensi. LVL mencakup contoh seperti Policy — lihat StrictPolicy untuk mengetahui informasi selengkapnya.

Untuk menggunakan ServerManagedPolicy, cukup impor ServerManagedPolicy ke Activity Anda, buat instance, lalu teruskan referensi ke instance tersebut saat membuat LicenseChecker. Lihat Membuat instance LicenseChecker dan LicenseCheckerCallback untuk mengetahui informasi selengkapnya.

StrictPolicy

LVL mencakup implementasi lengkap alternatif dari antarmuka Policy yang disebut StrictPolicy. Implementasi StrictPolicy memberikan Policy yang lebih ketat dari ServerManagedPolicy, karena tidak mengizinkan pengguna untuk mengakses aplikasi kecuali respons lisensi diterima dari server pada saat akses menunjukkan bahwa pengguna memiliki lisensi.

Fitur utama StrictPolicy adalah bahwa kebijakan tersebut tidak menyimpan setiap data respons lisensi secara lokal, di penyimpanan persisten. Karena tidak ada data yang disimpan, permintaan coba lagi tidak dilacak dan respons yang di-cache tidak dapat digunakan untuk memenuhi pemeriksaan lisensi. Policy hanya mengizinkan akses jika:

  • Respons lisensi diterima dari server pemberi lisensi, dan
  • Respons lisensi menunjukkan bahwa pengguna memiliki lisensi untuk mengakses aplikasi.

Penggunaan StrictPolicy akan sesuai jika tujuan utama Anda adalah ingin memastikan bahwa, dalam situasi yang mungkin terjadi, tidak ada pengguna yang diizinkan untuk mengakses aplikasi kecuali mereka telah dikonfirmasi memiliki lisensi pada saat penggunaan. Selain itu, Policy menawarkan tingkat keamanan yang sedikit lebih tinggi dari ServerManagedPolicy — karena data tidak di-cache secara lokal, pengguna berbahaya tidak dapat merusak data yang di-cache dan mendapatkan akses ke aplikasi.

Pada saat yang sama, Policy ini menghadirkan tantangan bagi pengguna biasa, karena itu berarti bahwa mereka tidak akan dapat mengakses aplikasi saat tidak ada koneksi jaringan (seluler atau Wi-Fi) yang tersedia. Efek samping lainnya adalah aplikasi Anda akan mengirim permintaan pemeriksaan lisensi yang lebih banyak ke server, karena menggunakan respons yang di-cache tidak mungkin dilakukan.

Secara keseluruhan, kebijakan ini mewakili hasil tingkat kenyamanan pengguna atas keamanan mutlak dan kontrol atas akses. Pertimbangkan hasil tersebut dengan cermat sebelum menggunakan Policy ini.

Untuk menggunakan StrictPolicy, cukup impor StrictPolicy ke Activity Anda, buat instance, lalu teruskan referensi ke instance tersebut saat membuat LicenseChecker. Lihat Membuat instance LicenseChecker dan LicenseCheckerCallback untuk mengetahui informasi selengkapnya.

Implementasi Policy standar harus menyimpan data respons lisensi untuk aplikasi ke penyimpanan persisten, sehingga dapat diakses di seluruh pemanggilan aplikasi dan penyalaan ulang perangkat. Misalnya, Policy akan mempertahankan stempel waktu pemeriksaan lisensi terakhir yang berhasil, jumlah coba ulang, periode validitas lisensi, dan informasi serupa dalam penyimpanan persisten, alih-alih menyetel ulang nilai setiap kali aplikasi diluncurkan. Policy default disertakan dalam LVL, ServerManagedPolicy, menyimpan data respons lisensi di instance SharedPreferences, untuk memastikan bahwa data bersifat tetap.

Karena Policy akan menggunakan data respons lisensi yang tersimpan untuk menentukan apakah akan mengizinkan atau melarang akses ke aplikasi, kebijakan harus memastikan bahwa data apa pun yang disimpan aman dan tidak dapat digunakan kembali atau dimanipulasi oleh pengguna root di perangkat. Secara khusus, Policy harus selalu meng-obfuscate data sebelum menyimpannya, menggunakan kunci yang unik untuk aplikasi dan perangkat. Menyamarkan data menggunakan kunci yang bersifat spesifik untuk aplikasi dan perangkat sangatlah penting, karena hal ini mencegah data yang tidak jelas dibagikan di antara aplikasi dan perangkat.

LVL membantu aplikasi dengan menyimpan data respons lisensinya dengan cara yang aman dan persisten. Pertama, LVL menyediakan antarmuka Obfuscator yang memungkinkan aplikasi Anda menyediakan algoritme obfuscation pilihannya untuk data yang disimpan. Berdasarkan hal tersebut, LVL menyediakan class helper PreferensiObfuscator, yang menangani sebagian besar pekerjaan memanggil class Obfuscator aplikasi serta membaca dan menulis data yang disamarkan dalam instance SharedPreferences.

LVL menyediakan implementasi Obfuscator lengkap yang disebut AESObfuscator yang menggunakan enkripsi AES untuk meng-obfuscate data. Anda dapat menggunakan AESObfuscator di aplikasi tanpa modifikasi atau Anda dapat menyesuaikannya dengan kebutuhan Anda. Jika Anda menggunakan Policy (seperti ServerManagedPolicy) yang men-cache respons lisensi, menggunakan AESObfuscator sebagai basis untuk implementasi Obfuscator sangat direkomendasikan. Untuk informasi selengkapnya, lihat bagian berikutnya.

AESObfuscator

LVL menyertakan implementasi antarmuka Obfuscator lengkap dan direkomendasikan yang disebut AESObfuscator. Implementasi ini diintegrasikan dengan aplikasi contoh LVL dan berfungsi sebagai Obfuscator default di library.

AESObfuscator menyediakan obfuscation data yang aman menggunakan AES untuk mengenkripsi dan mendekripsi data seperti yang tertulis atau dibaca di penyimpanan. Obfuscator menerapkan enkripsi menggunakan tiga kolom data yang disediakan oleh aplikasi:

  1. Salt - array byte acak yang digunakan untuk tiap (pembatalan) obfuscation.
  2. String ID aplikasi, biasanya nama paket aplikasi.
  3. String ID perangkat, yang berasal dari jumlah sumber spesifik-perangkat sehingga ID ini bersifat unik.

Untuk menggunakan AESObfuscator, pertama-tama impor AESObfuscator ke Activity Anda. Deklarasikan array final statis pribadi untuk menampung byte salt dan menginisialisasinya ke 20 byte yang dihasilkan secara acak.

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

Selanjutnya, deklarasikan variabel untuk menampung ID perangkat dan berikan nilai untuknya dengan cara apa pun yang diperlukan. Misalnya, aplikasi contoh yang disertakan dalam LVL mengkueri setelan sistem untuk android.Settings.Secure.ANDROID_ID, yang unik untuk setiap perangkat.

Perhatikan bahwa, bergantung pada API yang Anda gunakan, aplikasi Anda mungkin perlu meminta izin tambahan untuk mendapatkan informasi khusus perangkat. Misalnya, untuk meng-kueri TelephonyManager guna mendapatkan IMEI perangkat atau data terkait, aplikasi juga perlu meminta izin android.permission.READ_PHONE_STATE dalam manifesnya.

Sebelum meminta izin baru dengan tujuan utama memperoleh informasi khusus perangkat untuk digunakan di Obfuscator, pertimbangkan bagaimana hal tersebut dapat memengaruhi aplikasi Anda atau pemfilterannya di Google Play (karena beberapa izin dapat menyebabkan alat build SDK menambahkan <uses-feature> terkait).

Terakhir, buat instance AESObfuscator, dengan meneruskan salt, ID aplikasi, dan ID perangkat. Anda dapat membuat instance secara langsung, sekaligus membuat Policy dan LicenseChecker. Contoh:

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

Untuk contoh lengkapnya, lihat MainActivity dalam aplikasi contoh LVL.

Memeriksa Lisensi dari Activity

Setelah Anda mengimplementasikan Policy untuk mengelola akses ke aplikasi Anda, langkah berikutnya adalah menambahkan pemeriksaan lisensi ke aplikasi Anda, yang memulai kueri ke server pemberi lisensi jika diperlukan dan mengelola akses ke aplikasi berdasarkan respons lisensi. Semua pekerjaan menambahkan pemeriksaan lisensi dan penanganan respons berlangsung di file sumber Activity utama.

Untuk menambahkan pemeriksaan lisensi dan menangani respons, Anda harus:

  1. Menambahkan impor
  2. Mengimplementasikan LicenseCheckerCallback sebagai class dalam pribadi
  3. Membuat Handler untuk memposting dari LicenseCheckerCallback ke UI thread
  4. Membuat instance LicenseChecker dan LicenseCheckerCallback
  5. Memanggil checkAccess() untuk memulai pemeriksaan lisensi
  6. Menyematkan kunci publik Anda untuk pemberian lisensi
  7. Memanggil metode ofDestroy() dari LicenseChecker untuk memutus koneksi IPC.

Bagian di bawah menjelaskan tugas-tugas tersebut.

Ringkasan respons dan pemeriksaan lisensi

Dalam sebagian besar kasus, Anda harus menambahkan pemeriksaan lisensi ke Activity utama aplikasi Anda, dalam metode onCreate(). Ini memastikan bahwa saat pengguna meluncurkan aplikasi Anda secara langsung, pemeriksaan lisensi akan segera dipanggil. Dalam beberapa kasus, Anda juga dapat menambahkan pemeriksaan lisensi di lokasi lain. Misalnya, jika aplikasi Anda menyertakan beberapa komponen Activity yang dapat dimulai oleh aplikasi lain dengan Intent, Anda dapat menambahkan pemeriksaan lisensi di Activity itu.

Pemeriksaan lisensi terdiri dari dua tindakan utama:

  • Panggilan ke metode untuk memulai pemeriksaan lisensi — di LVL, ini adalah panggilan untuk metode checkAccess() dari objek LicenseChecker yang Anda buat.
  • Callback yang menampilkan hasil pemeriksaan lisensi. Di LVL, ini adalah antarmuka LicenseCheckerCallback yang Anda implementasikan. Antarmuka mendeklarasikan dua metode, allow() dan dontAllow(), yang dipanggil oleh library berdasarkan hasil pemeriksaan lisensi. Anda mengimplementasikan dua metode ini dengan logika apa pun yang Anda perlukan, untuk mengizinkan atau melarang akses pengguna ke aplikasi Anda. Perhatikan bahwa metode ini tidak menentukan apakah akses akan diizinkan— penentuan tersebut adalah tanggung jawab implementasi Policy Anda. Sebaliknya, metode ini hanya memberikan aplikasi perilaku yang dapat digunakan sebagai cara untuk mengizinkan dan melarang akses (dan menangani error aplikasi).

    Metode allow() dan dontAllow() memberikan "alasan" untuk respons tersebut, yang dapat berupa salah satu dari nilai Policy, LICENSED, NOT_LICENSED, atau RETRY. Secara khusus, Anda harus menangani situasi ketika metode tersebut menerima respons RETRY untuk dontAllow() dan menyediakan tombol "Coba lagi" kepada pengguna, yang mungkin sudah terjadi karena layanan tidak tersedia selama permintaan.

Gambar 1. Ringkasan tentang interaksi pemeriksaan lisensi biasa.

Diagram di atas mengilustrasikan bagaimana pemeriksaan lisensi biasa terjadi:

  1. Kode dalam Activity utama aplikasi membuat instance objek LicenseCheckerCallback dan LicenseChecker. Saat membuat LicenseChecker, kode tersebut akan meneruskan Context, implementasi Policy yang akan digunakan, dan kunci publik akun penayang untuk pemberian lisensi sebagai parameter.
  2. Kode kemudian memanggil metode checkAccess() pada objek LicenseChecker. Implementasi metode ini akan memanggil Policy untuk menentukan apakah ada respons lisensi valid yang di-cache secara lokal dalam SharedPreferences.
    • Jika ada, implementasi checkAccess() akan memanggil allow().
    • Jika tidak, LicenseChecker akan memulai permintaan pemeriksaan lisensi yang dikirimkan ke server pemberian lisensi.

    Catatan: Server pemberian lisensi selalu menampilkan LICENSED saat Anda melakukan pemeriksaan lisensi aplikasi draf.

  3. Saat respons diterima, LicenseChecker akan membuat LicenseValidator yang memverifikasi data lisensi yang ditandatangani dan mengekstrak kolom respons, lalu meneruskannya ke Policy untuk evaluasi lebih lanjut.
    • Jika lisensi valid, Policy akan men-cache respons dalam SharedPreferences dan memberi tahu validator, yang kemudian memanggil metode allow() pada objek LicenseCheckerCallback.
    • Jika lisensi tidak valid, Policy akan memberi tahu validator, yang memanggil metode dontAllow() pada LicenseCheckerCallback.
  4. Jika terjadi error lokal atau server yang dapat dipulihkan, misalnya saat jaringan tidak tersedia untuk mengirim permintaan, LicenseChecker akan meneruskan respons RETRY ke metode processServerResponse() objek Policy Anda.

    Selain itu, metode callback allow() dan dontAllow() menerima argumen reason. Alasan metode allow() biasanya Policy.LICENSED atau Policy.RETRY dan alasan dontAllow() biasanya Policy.NOT_LICENSED atau Policy.RETRY. Nilai respons ini berguna sehingga Anda dapat menampilkan respons yang sesuai untuk pengguna, misalnya dengan memberikan tombol "Coba lagi" saat dontAllow() merespons dengan Policy.RETRY, yang mungkin disebabkan karena layanan tidak tersedia.

  5. Jika terjadi error aplikasi, seperti saat aplikasi mencoba memeriksa lisensi nama paket yang tidak valid, LicenseChecker akan meneruskan respons error ke metode applicationError() LicenseCheckerCallback.

Perhatikan bahwa, selain memulai pemeriksaan lisensi dan menangani hasilnya, yang dijelaskan dalam bagian berikut, aplikasi Anda juga perlu memberikan implementasi Policy dan, jika Policy menyimpan data respons (seperti ServerManagedPolicy), implementasi Obfuscator.

Menambahkan impor

Pertama, buka file class Activity utama aplikasi, lalu impor LicenseChecker dan LicenseCheckerCallback dari paket 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;

Jika Anda menggunakan implementasi Policy default yang disediakan dengan LVL, ServerManagedPolicy, impor juga implementasi tersebut, bersama dengan AESObfuscator. Jika Anda menggunakan Policy atau Obfuscator kustom, impor kode tersebut.

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;

Mengimplementasikan LicenseCheckerCallback sebagai class dalam pribadi

LicenseCheckerCallback adalah antarmuka yang disediakan oleh LVL untuk menangani hasil pemeriksaan lisensi. Untuk mendukung pemberian lisensi menggunakan LVL, Anda harus mengimplementasikan LicenseCheckerCallback dan metodenya untuk mengizinkan atau melarang akses ke aplikasi.

Hasil pemeriksaan lisensi selalu berupa panggilan ke salah satu metode LicenseCheckerCallback yang dibuat berdasarkan validasi payload respons, kode respons server itu sendiri, dan pemrosesan tambahan yang disediakan oleh Policy Anda. Aplikasi Anda dapat mengimplementasikan metode dengan cara apa pun yang diperlukan. Secara umum, sebaiknya jaga agar metode tetap sederhana, membatasinya untuk mengelola status UI dan akses aplikasi. Jika ingin menambahkan pemrosesan respons lisensi lebih lanjut, seperti dengan menghubungi server backend atau menerapkan pembatasan khusus, Anda harus mempertimbangkan untuk memasukkan kode tersebut ke Policy, alih-alih memasukkannya ke dalam metode LicenseCheckerCallback.

Dalam kebanyakan kasus, Anda harus mendeklarasikan implementasi LicenseCheckerCallback sebagai class pribadi di dalam class Activity utama aplikasi Anda.

Implementasikan metode allow() dan dontAllow() jika diperlukan. Untuk memulainya, Anda dapat menggunakan perilaku penanganan hasil sederhana dalam metode, seperti menampilkan hasil lisensi dalam dialog. Hal ini membantu Anda menjalankan aplikasi lebih cepat dan dapat membantu proses debug. Kemudian, setelah menentukan perilaku yang tepat, Anda dapat menambahkan penanganan yang lebih rumit.

Beberapa saran untuk menangani respons tanpa lisensi di dontAllow() meliputi:

  • Tampilkan dialog "Coba lagi" kepada pengguna, termasuk tombol untuk memulai pemeriksaan lisensi baru jika reason yang disediakan adalah Policy.RETRY.
  • Tampilkan dialog "Beli aplikasi ini", termasuk tombol deep link yang mengarahkan pengguna ke halaman detail aplikasi di Google Play, yang memungkinkan pengguna untuk membeli aplikasi tersebut. Untuk informasi selengkapnya tentang cara menyiapkan link semacam itu, lihat Menautkan ke Produk Anda.
  • Tampilkan notifikasi Toast yang menunjukkan bahwa fitur aplikasi dibatasi karena tidak berlisensi.

Contoh di bawah menunjukkan bagaimana aplikasi contoh LVL mengimplementasikan LicenseCheckerCallback, dengan metode yang menampilkan hasil pemeriksaan lisensi dalam dialog.

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

Selain itu, Anda harus mengimplementasikan metode applicationError(), yang dipanggil LVL untuk memungkinkan aplikasi Anda menangani error yang tidak dapat dicoba lagi. Untuk daftar error semacam itu, lihat Kode Respons Server dalam Referensi Pemberian Lisensi. Anda dapat mengimplementasikan metode ini dengan cara apa pun yang diperlukan. Umumnya, metode harus mencatat kode error dan memanggil dontAllow().

Membuat Handler untuk memposting dari LicenseCheckerCallback ke UI thread

Selama pemeriksaan lisensi, LVL akan meneruskan permintaan ke aplikasi Google Play, yang menangani komunikasi dengan server pemberi lisensi. LVL akan meneruskan permintaan melalui IPC asinkron (menggunakan Binder) sehingga pemrosesan dan komunikasi jaringan yang sebenarnya tidak terjadi pada thread yang dikelola oleh aplikasi Anda. Demikian pula, saat aplikasi Google Play menerima hasil, aplikasi akan memanggil metode callback melalui IPC, yang akan dijalankan dalam pool thread IPC pada proses aplikasi Anda.

Class LicenseChecker mengelola komunikasi IPC aplikasi Anda dengan aplikasi Google Play, termasuk panggilan yang mengirim permintaan dan callback yang menerima respons. LicenseChecker juga melacak permintaan lisensi terbuka dan mengelola waktu tunggu.

Agar dapat menangani waktu tunggu dengan benar dan juga memproses respons masuk tanpa memengaruhi UI thread aplikasi Anda, LicenseChecker menghasilkan thread latar belakang pada saat pembuatan instance. Di thread, class ini melakukan semua pemrosesan hasil pemeriksaan lisensi, entah hasilnya adalah respons yang diterima dari server atau error waktu tunggu habis. Pada akhir pemrosesan, LVL akan memanggil metode LicenseCheckerCallback dari thread latar belakang.

Untuk aplikasi Anda, hal ini berarti bahwa:

  1. Metode LicenseCheckerCallback pada umumnya akan dipanggil dari thread latar belakang.
  2. Metode tersebut tidak dapat mengupdate status atau memanggil pemrosesan apa pun di UI thread, kecuali Anda membuat Handler di UI thread dan menentukan agar metode callback Anda memposting ke Handler tersebut.

Jika ingin metode LicenseCheckerCallback Anda mengupdate UI thread, buat instance Handler dalam metode onCreate() Activity utama, seperti yang ditunjukkan di bawah. Dalam contoh ini, metode LicenseCheckerCallback aplikasi contoh LVL (lihat di atas) memanggil displayResult() untuk mengupdate UI thread melalui metode post() Handler.

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

Kemudian, dalam metode LicenseCheckerCallback, Anda dapat menggunakan metode Handler untuk memposting objek Runnable atau Message ke Handler. Berikut adalah cara aplikasi contoh yang disertakan dalam LVL memposting Runnable ke Handler di UI thread untuk menampilkan status lisensi.

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

Membuat instance LicenseChecker dan LicenseCheckerCallback

Pada metode onCreate() Activity utama, buat instance pribadi LicenseCheckerCallback dan LicenseChecker. Anda harus membuat instance LicenseCheckerCallback terlebih dahulu, karena Anda harus meneruskan referensi ke instance tersebut saat memanggil konstruktor untuk LicenseChecker.

Saat membuat instance LicenseChecker, Anda harus meneruskan parameter berikut:

  • Context aplikasi
  • Referensi ke implementasi Policy yang digunakan untuk pemeriksaan lisensi. Pada umumnya, Anda akan menggunakan implementasi Policy default yang disediakan oleh LVL, ServerManagedPolicy.
  • Variabel String yang menyimpan kunci publik akun publikasi untuk pemberian lisensi.

Jika menggunakan ServerManagedPolicy, Anda tidak perlu mengakses class secara langsung; Anda dapat membuat instance di konstruktor LicenseChecker, seperti dalam contoh di bawah. Perhatikan bahwa Anda perlu meneruskan referensi ke instance Obfuscator baru saat Anda membuat ServerManagedPolicy.

Contoh di bawah menunjukkan pembuatan instance LicenseChecker dan LicenseCheckerCallback dari metode onCreate() class 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.
            );
        ...
    }
}

Perlu diperhatikan bahwa LicenseChecker hanya akan memanggil metode LicenseCheckerCallback dari UI thread jika ada respons lisensi valid yang di-cache secara lokal. Jika pemeriksaan lisensi dikirim ke server, callback selalu berasal dari thread latar belakang, bahkan untuk error jaringan.

Memanggil checkAccess() untuk memulai pemeriksaan lisensi

Di Activity utama, tambahkan panggilan ke metode checkAccess() dari instance LicenseChecker. Dalam panggilan, teruskan referensi ke instance LicenseCheckerCallback sebagai parameter. Jika perlu menangani efek UI khusus atau pengelolaan status sebelum panggilan, akan lebih mudah jika Anda memanggil checkAccess() dari metode wrapper. Sebagai contoh, aplikasi contoh LVL akan memanggil checkAccess() dari metode 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);
    }

Menyematkan kunci publik Anda untuk pemberian lisensi

Untuk setiap aplikasi, layanan Google Play otomatis menghasilkan pasangan kunci umum/pribadi RSA 2048 bit yang digunakan untuk pemberian lisensi dan penagihan via Google Play. Pasangan kunci terkait secara unik dengan aplikasi. Meskipun terkait dengan aplikasi, pasangan kunci tidak sama dengan kunci yang Anda gunakan untuk menandatangani aplikasi Anda (atau diambil dari kunci tersebut).

Konsol Google Play mengekspos kunci publik untuk pemberian lisensi kepada developer mana pun yang login ke Konsol Play, tetapi Konsol Google Play menyembunyikan kunci pribadi dari semua pengguna di lokasi yang aman. Saat aplikasi meminta pemeriksaan lisensi untuk aplikasi yang dipublikasikan di akun Anda, server pemberi lisensi akan menandatangani respons lisensi menggunakan kunci pribadi dari pasangan kunci aplikasi Anda. Saat menerima respons, LVL akan menggunakan kunci publik yang disediakan oleh aplikasi untuk memverifikasi tanda tangan respons lisensi.

Untuk menambahkan pemberian lisensi ke aplikasi, Anda harus mendapatkan kunci publik aplikasi Anda untuk pemberian lisensi dan menyalinnya ke dalam aplikasi Anda. Berikut adalah cara menemukan kunci publik aplikasi Anda untuk pemberian lisensi:

  1. Buka Konsol Play Google, lalu login. Pastikan Anda login ke akun tempat aplikasi yang Anda lisensikan dipublikasikan (atau akan dipublikasikan).
  2. Pada halaman detail aplikasi, cari link Layanan & API, lalu klik link tersebut.
  3. Pada halaman Layanan & API, cari bagian Pemberian Lisensi & Penagihan via Google Play. Kunci publik untuk pemberian lisensi diberikan di kolom Kunci Lisensi Anda Untuk Aplikasi Ini.

Untuk menambahkan kunci publik ke aplikasi, cukup salin/tempel string kunci dari kolom ke dalam aplikasi Anda sebagai nilai BASE64_PUBLIC_KEY variabel String. Saat menyalin, pastikan Anda telah memilih seluruh string kunci, tanpa menghilangkan karakter apa pun.

Berikut ini contoh dari aplikasi contoh 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
    ...
}

Memanggil metode onDestroy() dari LicenseChecker untuk memutus koneksi IPC

Terakhir, untuk memungkinkan penghapusan LVL sebelum perubahan Context aplikasi Anda, tambahkan panggilan ke metode onDestroy() LicenseChecker dari implementasi onDestroy() Activity Anda. Panggilan menyebabkan LicenseChecker menutup semua koneksi IPC yang terbuka dengan benar ke ILicensingService aplikasi Google Play dan menghapus semua referensi lokal ke layanan dan handler.

Gagal memanggil metode onDestroy() LicenseChecker dapat menyebabkan masalah selama siklus proses aplikasi Anda. Misalnya, jika pengguna mengubah orientasi layar saat pemeriksaan lisensi aktif, Context aplikasi akan dimusnahkan. Jika aplikasi Anda tidak menutup koneksi IPC LicenseChecker dengan benar, aplikasi akan error saat respons diterima. Demikian pula, jika pengguna keluar dari aplikasi Anda saat pemeriksaan lisensi sedang berlangsung, aplikasi akan error saat respons diterima, kecuali jika aplikasi telah memanggil metode onDestroy() LicenseChecker untuk memutuskan sambungan dari layanan.

Berikut adalah contoh dari aplikasi contoh yang disertakan dalam LVL, dengan mChecker yang merupakan instance LicenseChecker:

Kotlin

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

Java

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

Jika memperluas atau mengubah LicenseChecker, Anda mungkin juga perlu memanggil metode finishCheck() dari LicenseChecker untuk memutus semua koneksi IPC yang masih terbuka.

Mengimplementasikan DeviceLimiter

Dalam beberapa kasus, Anda mungkin ingin Policy membatasi jumlah perangkat sebenarnya yang diizinkan untuk menggunakan lisensi tunggal. Hal ini akan mencegah pengguna memindahkan aplikasi berlisensi ke sejumlah perangkat dan menggunakan aplikasi pada perangkat tersebut dengan ID akun yang sama. Hal ini juga akan mencegah pengguna agar tidak "berbagi" aplikasi dengan memberikan informasi akun yang terkait dengan lisensi kepada individu lain, yang kemudian dapat login ke akun tersebut di perangkat mereka dan mengakses lisensi untuk aplikasi tersebut.

LVL mendukung pemberian lisensi per perangkat dengan menyediakan antarmuka DeviceLimiter, yang mendeklarasikan satu metode, allowDeviceAccess(). Saat LicenseValidator menangani respons dari server pemberi lisensi, LicenseValidator akan memanggil allowDeviceAccess(), meneruskan string ID pengguna yang diekstrak dari respons.

Jika Anda tidak ingin mendukung pembatasan perangkat, tidak ada pekerjaan yang perlu dilakukan — class LicenseChecker otomatis menggunakan implementasi default yang disebut NullDeviceLimiter. Seperti saran namanya, NullDeviceLimiter adalah class “tanpa pengoperasian” yang metode allowDeviceAccess()-nya hanya menampilkan respons LICENSED untuk semua pengguna dan perangkat.

Perhatian: Pemberian lisensi per perangkat tidak direkomendasikan untuk sebagian besar aplikasi karena:

  • Anda harus menyediakan server backend untuk mengelola pemetaan pengguna dan perangkat, dan
  • Hal ini dapat secara tidak sengaja menyebabkan pengguna tidak dapat mengakses aplikasi yang telah dibeli secara sah di perangkat lain.

Meng-obfuscate Kode Anda

Untuk memastikan keamanan aplikasi Anda, terutama untuk aplikasi berbayar yang menggunakan pemberian lisensi dan/atau batasan dan perlindungan khusus, meng-obfuscate kode aplikasi Anda sangat penting. Meng-obfuscate kode Anda dengan benar akan membuat pengguna berbahaya sulit mendekompilasi bytecode aplikasi, mengubahnya — seperti menghapus pemeriksaan lisensi — lalu mengkompilasi ulang.

Beberapa program obfuscator tersedia untuk aplikasi Android, termasuk ProGuard, yang juga menawarkan fitur pengoptimalan kode. Penggunaan ProGuard atau program serupa untuk meng-obfuscate kode Anda sangat direkomendasikan untuk semua aplikasi yang menggunakan Pemberian Lisensi Google Play.

Memublikasikan Aplikasi Berlisensi

Setelah menguji implementasi lisensi, Anda siap memublikasikan aplikasi di Google Play. Ikuti langkah-langkah seperti biasa untuk menyiapkan, menandatangani, lalu memublikasikan aplikasi.

Cara Mendapatkan Dukungan

Jika Anda memiliki pertanyaan atau mengalami masalah saat mengimplementasikan atau menerapkan publikasi dalam aplikasi, gunakan referensi dukungan yang tercantum dalam tabel di bawah. Dengan mengarahkan pertanyaan ke forum yang tepat, Anda bisa mendapatkan dukungan yang diperlukan dengan lebih cepat.

Tabel 2. Referensi dukungan developer untuk Layanan Pemberian Lisensi Google Play.

Jenis Dukungan Referensi Topik yang Dicakup
Masalah pengembangan dan pengujian Google Grup: android-developers Integrasi dan download LVL, project library, pertanyaan Policy, ide pengalaman pengguna, penanganan respons, Obfuscator, IPC, penyiapan lingkungan pengujian
Stack Overflow: http://stackoverflow.com/questions/tagged/android
Masalah akun, publikasi, dan deployment Forum Bantuan Google Play Akun publikasi, pasangan kunci pemberian lisensi, akun pengujian, respons server, respons pengujian, deployment aplikasi, dan hasil
FAQ Dukungan Pemberian Lisensi Pasar
Issue tracker LVL Pelacak masalah project marketlicensing Laporan bug dan masalah yang terkait secara khusus dengan implementasi antarmuka dan class kode sumber LVL

Untuk informasi umum tentang cara memposting ke grup yang tercantum di atas, lihat bagian Referensi Komunitas di halaman Referensi Dukungan Developer.

Referensi Tambahan

Aplikasi contoh yang disertakan dengan LVL memberikan contoh lengkap tentang cara memulai pemeriksaan lisensi dan menangani hasilnya, dalam class MainActivity.