Thêm quy trình xác minh giấy phép phía máy khách vào ứng dụng của bạn

Cảnh báo: Khi ứng dụng thực hiện quy trình xác minh giấy phép phía máy khách, những kẻ tấn công có thể dễ dàng sửa đổi hoặc xoá logic được liên kết với quy trình xác minh này.

Vì lý do đó, chúng tôi đặc biệt khuyên bạn nên thực hiện quy trình xác minh giấy phép phía máy chủ.

Sau khi thiết lập tài khoản nhà xuất bản và môi trường phát triển (xem phần Thiết lập để cấp phép), bạn có thể thêm quy trình xác minh giấy phép vào ứng dụng bằng Thư viện xác minh giấy phép (LVL).

Để thêm quy trình xác minh giấy phép (LVL):

  1. Thêm quyền cấp phép trong tệp kê khai của ứng dụng.
  2. Triển khai Chính sách — bạn có thể chọn một trong các phương pháp triển khai đầy đủ được cung cấp trong LVL hoặc tạo phương thức triển khai của riêng bạn.
  3. Triển khai Trình làm rối (Obfuscator) nếu Policy của bạn sẽ lưu mọi dữ liệu phản hồi giấy phép vào bộ nhớ đệm.
  4. Thêm mã để kiểm tra giấy phép trong lớp Activity chính của ứng dụng.
  5. Triển khai DeviceLimiter (không bắt buộc và không nên dùng cho hầu hết ứng dụng).

Các phần dưới đây mô tả những việc cần làm này. Sau khi hoàn tất việc tích hợp, bạn sẽ có thể biên dịch thành công ứng dụng và có thể bắt đầu kiểm thử như được mô tả trong phần Thiết lập môi trường kiểm thử.

Để biết thông tin tổng quan về bộ tệp nguồn đầy đủ có trong LVL, hãy xem phần Tóm tắt về các lớp và giao diện LVL.

Thêm quyền cấp phép

Để dùng ứng dụng Google Play cho việc gửi yêu cầu kiểm tra giấy phép đến máy chủ, ứng dụng của bạn phải yêu cầu quyền thích hợp, com.android.vending.CHECK_LICENSE. Nếu ứng dụng của bạn không khai báo quyền cấp phép nhưng cố gắng bắt đầu kiểm tra giấy phép, thì LVL sẽ gửi ra một trường hợp ngoại lệ về bảo mật.

Để yêu cầu quyền cấp phép trong ứng dụng của bạn, hãy khai báo phần tử <uses-permission> làm phần tử con của <manifest> như sau:

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

Ví dụ dưới đây là cách ứng dụng mẫu LVL khai báo quyền:

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

Lưu ý: Hiện tại, bạn không thể khai báo quyền CHECK_LICENSE trong tệp kê khai của dự án thư viện LVL vì Bộ công cụ SDK sẽ không hợp nhất quyền này vào tệp kê khai của các ứng dụng phụ thuộc. Thay vào đó, bạn phải khai báo quyền trong tệp kê khai của từng ứng dụng phụ thuộc.

Triển khai chính sách

Dịch vụ cấp phép của Google Play không tự xác định liệu một người dùng cụ thể với giấy phép cụ thể có nên được cấp quyền truy cập vào ứng dụng của bạn hay không. Thay vào đó, quy trình triển khai Policy mà bạn cung cấp trong ứng dụng sẽ thực hiện trách nhiệm này.

Chính sách là một giao diện do LVL khai báo được thiết kế để giữ lại logic của ứng dụng nhằm cho phép hoặc không cho phép người dùng truy cập, dựa trên kết quả kiểm tra giấy phép. Để sử dụng LVL, ứng dụng của bạn phải đưa ra một quy trình triển khai của Policy.

Giao diện Policy khai báo hai phương thức allowAccess()processServerResponse(). Các phương thức này được một thực thể LicenseChecker gọi khi xử lý phản hồi của máy chủ cấp phép. Phương thức này cũng khai báo một enum có tên là LicenseResponse, trong đó chỉ định giá trị phản hồi giấy phép được truyền trong các lệnh gọi đến processServerResponse().

  • processServerResponse() cho phép bạn xử lý trước dữ liệu phản hồi thô nhận được từ máy chủ cấp phép trước khi xác định việc có cấp quyền truy cập hay không.

    Quy trình triển khai thông thường sẽ trích xuất một số hoặc tất cả trường trong nội dung phản hồi giấy phép và lưu trữ dữ liệu cục bộ trên một cửa hàng cố định, chẳng hạn như thông qua bộ nhớ SharedPreferences, để đảm bảo rằng dữ liệu có thể truy cập được thông qua các lệnh gọi ứng dụng và chu kỳ nguồn của thiết bị. Ví dụ: Policy sẽ duy trì dấu thời gian của lần kiểm tra giấy phép thành công mới nhất, số lần thử lại, thời hạn hiệu lực của giấy phép và các thông tin tương tự trong một cửa hàng cố định, thay vì đặt lại các giá trị đó mỗi khi chạy ứng dụng.

    Khi lưu trữ cục bộ dữ liệu phản hồi, Policy phải đảm bảo rằng dữ liệu đó đã được làm rối mã nguồn (hãy xem bài viết Triển khai Trình làm rối mã nguồn bên dưới).

  • allowAccess() sẽ xác định việc có cấp cho người dùng quyền truy cập vào ứng dụng hay không, dựa trên mọi dữ liệu phản hồi giấy phép hiện có (từ máy chủ cấp phép hoặc từ bộ nhớ đệm) hoặc các thông tin cụ thể về ứng dụng khác. Ví dụ: quy trình triển khai allowAccess() có thể tính đến các tiêu chí bổ sung như mức sử dụng hoặc dữ liệu khác được truy xuất từ máy chủ phụ trợ. Trong mọi trường hợp, quy trình triển khai allowAccess() sẽ chỉ trả về true nếu người dùng được cấp phép sử dụng ứng dụng, như được xác định bởi máy chủ cấp phép hoặc nếu có sự cố tạm thời về mạng hoặc hệ thống khiến quá trình kiểm tra giấy phép không thể hoàn tất. Trong những trường hợp như vậy, quy trình triển khai của bạn có thể duy trì số lượng phản hồi thử lại và tạm thời cho phép truy cập cho đến khi quá trình kiểm tra giấy phép tiếp theo hoàn tất.

Để đơn giản hoá quá trình thêm giấy phép vào ứng dụng cũng như minh hoạ cách nên thiết kế một Policy, LVL có 2 cách triển khai Policy đầy đủ để bạn có thể điều chỉnh theo nhu cầu của mình hoặc dùng ngay mà không cần sửa đổi:

  • ServerManagedPolicy, một Policy linh hoạt sử dụng các chế độ cài đặt do máy chủ cung cấp và phản hồi được lưu vào bộ nhớ đệm để quản lý quyền truy cập trên nhiều điều kiện mạng, và
  • StrictPolicy không lưu bất kỳ dữ liệu phản hồi nào vào bộ nhớ đệm và chỉ cho phép truy cập nếu máy chủ trả về một phản hồi đã được cấp phép.

Đối với hầu hết ứng dụng, bạn nên dùng ServerManagedPolicy. ServerManagedPolicy là LVL mặc định và được tích hợp với ứng dụng mẫu LVL.

Nguyên tắc áp dụng chính sách tuỳ chỉnh

Trong quá trình triển khai việc cấp phép, bạn có thể dùng một trong các chính sách hoàn chỉnh được cung cấp trong LVL (ServerManagedPolicy hay StrictPolicy) hoặc bạn có thể tạo một chính sách tuỳ chỉnh. Bất kỳ loại chính sách tuỳ chỉnh nào cũng có một số điểm thiết kế quan trọng để bạn có thể hiểu và điều chỉnh trong quá trình triển khai.

Máy chủ cấp phép áp dụng các giới hạn yêu cầu chung để chống lại việc lạm dụng các tài nguyên có thể dẫn đến sự cố từ chối dịch vụ. Khi một ứng dụng vượt quá giới hạn yêu cầu, máy chủ cấp phép sẽ trả về phản hồi 503. Phản hồi này sẽ được truyền cho ứng dụng của bạn dưới dạng lỗi máy chủ thông thường. Điều đó có nghĩa là người dùng sẽ không nhận được phản hồi giấy phép nào cho đến khi giới hạn được đặt lại, dẫn đến việc họ có thể sẽ bị ảnh hưởng trong thời gian không xác định.

Nếu đang thiết kế một chính sách tuỳ chỉnh thì bạn nên làm như sau với Policy:

  1. Lưu vào bộ nhớ đệm (và làm rối mã nguồn đúng cách) nội dung phản hồi giấy phép thành công gần đây nhất trong bộ nhớ cục bộ ổn định.
  2. Trả về nội dung phản hồi đã lưu vào bộ nhớ đệm đối với mọi quá trình kiểm tra giấy phép thay vì gửi yêu cầu đến máy chủ cấp phép, miễn là nội dung phản hồi được lưu vào bộ nhớ đệm đó là hợp lệ. Bạn nên đặt thời hạn phản hồi theo VT bổ sung do máy chủ cung cấp. Hãy xem phần Thông tin bổ sung về phản hồi của máy chủ để biết thêm thông tin.
  3. Sử dụng khoảng thời gian đợi luỹ thừa nếu có lỗi xảy ra khi thử lại bất kỳ yêu cầu nào. Xin lưu ý rằng ứng dụng Google Play tự động thử lại các yêu cầu không thành công, vì vậy trong hầu hết trường hợp, Policy của bạn không cần phải thử lại các yêu cầu này.
  4. Cung cấp một "thời gian gia hạn" cho phép người dùng truy cập vào ứng dụng trong một khoảng thời gian hoặc số lần sử dụng hạn chế trong khi ứng dụng đang thử lại quá trình kiểm tra giấy phép. Thời gian gia hạn sẽ mang lại lợi ích cho người dùng bằng cách cho phép họ truy cập cho đến khi hoàn tất thành công bước kiểm tra giấy phép tiếp theo. Việc này cũng có lợi cho bạn nhờ việc đặt một giới hạn cố định về quyền truy cập vào ứng dụng khi không có phản hồi giấy phép hợp lệ nào.

Bạn cần thiết kế Policy theo các nguyên tắc nêu trên để đảm bảo trải nghiệm tốt nhất có thể cho người dùng, đồng thời có thể kiểm soát hiệu quả ứng dụng ngay cả trong các điều kiện lỗi.

Xin lưu ý rằng mọi Policy đều có thể dùng chế độ cài đặt do máy chủ cấp phép cung cấp để giúp quản lý tính hợp lệ và thao tác lưu vào bộ nhớ đệm, thời gian gia hạn thử lại và nhiều tác vụ khác. Việc trích xuất các chế độ cài đặt do máy chủ cung cấp rất đơn giản mà bạn nên tận dụng triệt để. Hãy xem cách triển khai ServerManagedPolicy để biết ví dụ về cách trích xuất và sử dụng các tính năng bổ sung. Để biết danh sách các chế độ cài đặt máy chủ và thông tin về cách sử dụng, hãy xem phần Thông tin bổ sung về phản hồi của máy chủ.

ServerManagedPolicy

LVL bao gồm một quy trình triển khai đầy đủ và được đề xuất của giao diện Policy có tên là ServerManagedPolicy. Quá trình triển khai được tích hợp với các lớp LVL và đóng vai trò là Policy mặc định trong thư viện.

ServerManagedPolicy sẽ cung cấp tất cả lượt xử lý phản hồi giấy phép và thử lại. Bộ nhớ đệm này lưu trữ cục bộ tất cả dữ liệu phản hồi vào bộ nhớ đệm trong tệp SharedPreferences, gây khó khăn cho quá trình triển khai Obfuscator của ứng dụng. Điều này đảm bảo rằng dữ liệu phản hồi giấy phép sẽ được bảo mật và duy trì trên các chu kỳ nguồn của thiết bị. ServerManagedPolicy sẽ cung cấp những cách triển khai cụ thể của các phương thức giao diện processServerResponse()allowAccess(), đồng thời bao gồm một tập hợp các phương thức và loại hỗ trợ để quản lý phản hồi của giấy phép.

Quan trọng hơn, một đặc điểm chính của ServerManagedPolicy là sử dụng các chế độ cài đặt do máy chủ cung cấp làm cơ sở quản lý việc cấp phép trong suốt thời hạn hoàn tiền của ứng dụng cũng như thông qua các điều kiện lỗi và mạng khác nhau. Khi một ứng dụng liên hệ với máy chủ Google Play để kiểm tra giấy phép, máy chủ sẽ thêm một số tuỳ chọn cài đặt dưới dạng cặp khoá-giá trị trong trường bổ sung của một số loại nội dung phản hồi giấy phép nhất định. Ví dụ: máy chủ cung cấp các giá trị được đề xuất cho thời hạn hiệu lực của giấy phép, thời gian gia hạn thử lại và số lần thử lại tối đa được phép cùng nhiều giá trị khác. ServerManagedPolicy sẽ trích xuất các giá trị từ nội dung phản hồi giấy phép trong phương thức processServerResponse() và kiểm tra các giá trị đó trong phương thức allowAccess(). Để biết danh sách các chế độ cài đặt do máy chủ cung cấp được ServerManagedPolicy sử dụng, hãy xem phần Thông tin bổ sung về phản hồi của máy chủ.

Để việc sử dụng chế độ cài đặt giấy phép của máy chủ Google Play được thuận tiện, đạt hiệu suất tốt nhất và lợi ích đi kèm, chúng tôi đặc biệt khuyên bạn dùng ServerManagedPolicy để cấp phép Policy.

Nếu lo ngại về tính bảo mật của việc dữ liệu phản hồi giấy phép được lưu trữ cục bộ trong SharedPreferences, thì bạn có thể sử dụng thuật toán làm rối mã nguồn mạnh hơn hoặc thiết kế một Policy nghiêm ngặt hơn mà không lưu trữ dữ liệu giấy phép. LVL bao gồm một ví dụ về Policy như vậy — hãy xem phần StrictPolicy để biết thêm thông tin.

Để sử dụng ServerManagedPolicy, bạn chỉ cần nhập thuộc tính này vào Activity, tạo một thực thể và truyền nội dung tham chiếu đến thực thể đó khi tạo LicenseChecker. Hãy xem bài viết Tạo thực thể LicenseChecker và LicenseCheckerCallback để biết thêm thông tin.

StrictPolicy

LVL bao gồm một phương thức triển khai đầy đủ thay thế cho giao diện Policy có tên là StrictPolicy. Việc triển khai StrictPolicy cung cấp một Chính sách hạn chế hơn so với ServerManagedPolicy, trong đó chính sách không cho phép người dùng truy cập vào ứng dụng trừ khi máy chủ nhận được phản hồi giấy phép tại thời điểm truy cập cho biết rằng người dùng được cấp phép.

Đặc điểm chính của StrictPolicy là không lưu trữ cục bộ bất kỳ dữ liệu phản hồi giấy phép nào trong một cửa hàng cố định. Vì không có dữ liệu nào được lưu trữ, nên bạn không thể theo dõi yêu cầu thử lại và không thể sử dụng các phản hồi đã lưu vào bộ nhớ đệm để thực hiện kiểm tra giấy phép. Policy chỉ cho phép truy cập nếu:

  • Máy chủ cấp phép nhận được phản hồi giấy phép, và
  • Nội dung phản hồi giấy phép cho biết rằng người dùng được cấp phép để truy cập vào ứng dụng.

Việc sử dụng StrictPolicy là phù hợp nếu mối quan tâm chính của bạn là đảm bảo rằng trong mọi trường hợp có thể, sẽ không có người dùng nào được phép truy cập vào ứng dụng trừ khi người dùng được xác nhận là đã được cấp phép tại thời điểm sử dụng. Ngoài ra, tính bảo mật của Chính sách này có phần cao hơn so với ServerManagedPolicy — vì không có dữ liệu nào được lưu cục bộ vào bộ nhớ đệm nên người dùng đáng ngờ tuyệt đối không thể can thiệp vào dữ liệu này để có được quyền truy cập vào ứng dụng.

Đồng thời, Policy này cũng gây trở ngại đối với người dùng thông thường. Tính bảo mật cao hơn có nghĩa là họ sẽ không thể truy cập vào ứng dụng khi không có sẵn kết nối mạng (di động hoặc Wi-Fi). Một tác dụng phụ khác là ứng dụng sẽ gửi thêm yêu cầu kiểm tra giấy phép đến máy chủ, vì không thể sử dụng nội dung phản hồi đã lưu vào bộ nhớ đệm.

Nhìn chung, có thể thấy rằng chính sách này thoả hiệp một phần sự tiện lợi của người dùng để đổi lấy sự bảo mật và kiểm soát tuyệt đối với quyền truy cập. Hãy cân nhắc kỹ sự đánh đổi trước khi sử dụng Policy này.

Để sử dụng StrictPolicy, bạn chỉ cần nhập thuộc tính này vào Activity, tạo một thực thể và chuyển nội dung tham chiếu đến thực thể đó khi tạo LicenseChecker. Hãy xem bài viết về Tạo thực thể LicenseChecker và LicenseCheckerCallback để biết thêm thông tin.

Cách triển khai Policy thông thường cần lưu dữ liệu phản hồi giấy phép của một ứng dụng vào cửa hàng cố định để bạn có thể truy cập vào dữ liệu này qua các lệnh gọi ứng dụng và chu kỳ nguồn của thiết bị. Ví dụ: Policy sẽ duy trì dấu thời gian của lần kiểm tra giấy phép thành công gần đây nhất, số lần thử lại, thời hạn hiệu lực của giấy phép và thông tin tương tự trong một cửa hàng cố định, thay vì đặt lại các giá trị đó mỗi khi chạy ứng dụng. Policy mặc định có trong LVL, ServerManagedPolicy, sẽ lưu trữ dữ liệu phản hồi giấy phép trong một thực thể SharedPreferences, để đảm bảo dữ liệu luôn ổn định.

Policy sẽ dùng dữ liệu phản hồi giấy phép đã lưu trữ để xác định liệu có cho phép hay không cho phép truy cập vào ứng dụng, nên phải đảm bảo rằng mọi dữ liệu được lưu trữ đều an toàn và không để người dùng gốc sử dụng lại hoặc làm giả trên một thiết bị. Cụ thể, Policy phải luôn làm rối mã nguồn trước khi lưu trữ bằng cách sử dụng khoá dành riêng cho ứng dụng và thiết bị. Việc làm rối mã nguồn bằng cách sử dụng một khoá cho cả ứng dụng và thiết bị cụ thể là rất quan trọng vì việc này sẽ ngăn việc chia sẻ dữ liệu xáo trộn giữa các ứng dụng và thiết bị.

LVL hỗ trợ ứng dụng lưu trữ dữ liệu phản hồi giấy phép một cách an toàn, ổn định. Trước tiên, ứng dụng này cung cấp giao diện Obfuscator cho phép ứng dụng cung cấp thuật toán làm rối mã nguồn cho lựa chọn của dữ liệu đã lưu trữ. Dựa trên đó, LVL cung cấp lớp trợ giúp PreferenceObfuscator, lớp này xử lý hầu hết công việc gọi lớp Obfuscator của ứng dụng cũng như đọc và ghi dữ liệu bị làm rối mã nguồn trong một thực thể SharedPreferences.

LVL triển khai đầy đủ Obfuscator gọi là AESObfuscator, sử dụng phương thức mã hoá AES để làm rối dữ liệu. Bạn có thể dùng AESObfuscator trong ứng dụng mà không cần sửa đổi hoặc bạn có thể điều chỉnh theo nhu cầu. Nếu bạn đang dùng một Policy (chẳng hạn như ServerManagedPolicy) để lưu dữ liệu phản hồi giấy phép vào bộ nhớ đệm, thì chúng tôi khuyên bạn nên dùng AESObfuscator làm cơ sở cho hoạt động triển khai Obfuscator Để biết thêm thông tin, hãy xem phần tiếp theo.

AESObfuscator

LVL bao gồm một cách triển khai đầy đủ và được đề xuất của giao diện Obfuscator có tên là AESObfuscator. Hoạt động triển khai được tích hợp với ứng dụng mẫu LVL và đóng vai trò là Obfuscator mặc định trong thư viện.

AESObfuscator cung cấp tính năng làm rối mã nguồn dữ liệu một cách an toàn bằng cách sử dụng AES để mã hoá và giải mã dữ liệu khi dữ liệu được ghi hoặc đọc từ bộ nhớ. Obfuscator đã chạy quá trình mã hoá bằng ba trường dữ liệu do ứng dụng cung cấp:

  1. Dữ liệu ngẫu nhiên — một chuỗi các byte ngẫu nhiên để sử dụng cho mỗi trường hợp làm rối (hoặc gỡ rối).
  2. Chuỗi giá trị nhận dạng ứng dụng, thường là tên gói của ứng dụng.
  3. Chuỗi giá trị nhận dạng thiết bị, được lấy từ nhiều nguồn dành riêng cho thiết bị nhất có thể nhằm giúp chuỗi trở nên độc nhất.

Để sử dụng AESObfuscator, trước tiên, hãy nhập AESObscscator vào Activity. Khai báo một mảng static final riêng tư để giữ các byte dữ liệu ngẫu nhiên và khởi động mảng đó qua 20 byte được tạo ngẫu nhiên.

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

Tiếp theo, hãy khai báo biến để giữ một giá trị nhận dạng thiết bị và tạo giá trị cho biến theo cách cần thiết. Ví dụ: ứng dụng mẫu có trong LVL truy vấn các chế độ cài đặt hệ thống cho android.Settings.Secure.ANDROID_ID, mỗi chế độ là duy nhất trên mỗi thiết bị.

Xin lưu ý rằng tuỳ thuộc vào các API bạn dùng, ứng dụng của bạn có thể cần yêu cầu các quyền bổ sung để có được thông tin dành riêng cho thiết bị. Ví dụ: để truy vấn TelephonyManager để lấy IMEI của thiết bị hoặc dữ liệu có liên quan, ứng dụng cũng cần yêu cầu quyền android.permission.READ_PHONE_STATE trong tệp kê khai.

Trước khi yêu cầu các quyền mới cho mục đích duy nhất là thu thập thông tin về thiết bị cụ thể để sử dụng trong Obfuscator, hãy cân nhắc tác động của việc này lên ứng dụng của bạn hoặc việc lọc ứng dụng trên Google Play (vì một số quyền có thể khiến các công cụ xây dựng SDK thêm <uses-feature> liên quan).

Cuối cùng, hãy tạo một thực thể của AESObfuscator, chuyển dữ liệu ngẫu nhiên, giá trị nhận dạng ứng dụng và giá trị nhận dạng thiết bị. Bạn có thể tạo trực tiếp thực thể đó trong khi tạo PolicyLicenseChecker. Ví dụ:

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

Để biết ví dụ hoàn chỉnh, hãy xem MainActivity trong ứng dụng mẫu LVL.

Kiểm tra Giấy phép của Activity

Sau khi đã triển khai Policy để quản lý quyền truy cập vào ứng dụng, bạn hãy thêm chế độ kiểm tra giấy phép vào ứng dụng của bạn. Công cụ này sẽ bắt đầu một truy vấn đối với máy chủ cấp phép nếu cần thiết và quản lý quyền truy cập vào ứng dụng dựa trên dữ liệu phản hồi giấy phép. Bạn cần thực hiện mọi quy trình để kiểm tra giấy phép và xử lý phản hồi trong tệp nguồn Activity chính.

Để thêm chế độ kiểm tra giấy phép và xử lý phản hồi, bạn phải:

  1. Thêm các lệnh nhập
  2. Triển khai LicenseCheckerCallback dưới dạng lớp bên trong riêng tư
  3. Tạo Trình xử lý để đăng từ LicenseCheckerCallback lên luồng (thread) giao diện người dùng
  4. Tạo thực thể LicenseChecker và LicenseCheckerCallback
  5. Gọi CheckAccess() để bắt đầu kiểm tra giấy phép
  6. Nhúng khoá công khai để cấp phép
  7. Gọi phương thức của onDestroy() LicenseChecker để đóng các kết nối IPC.

Các phần dưới đây mô tả những việc cần làm này.

Tổng quan về quy trình kiểm tra và phản hồi giấy phép

Trong hầu hết trường hợp, bạn nên thêm chế độ kiểm tra giấy phép vào Activity chính của ứng dụng, trong phương thức onCreate(). Việc này đảm bảo rằng khi người dùng chạy trực tiếp ứng dụng của bạn, quá trình kiểm tra giấy phép sẽ được gọi ngay lập tức. Trong một số trường hợp, bạn cũng có thể thêm chế độ kiểm tra giấy phép ở các vị trí khác. Ví dụ: nếu ứng dụng của bạn bao gồm nhiều thành phần Activity mà các ứng dụng khác có thể bắt đầu trước Intent, thì bạn có thể thêm các bước kiểm tra giấy phép trong các Activity đó.

Quy trình kiểm tra giấy phép bao gồm hai hành động chính:

  • Lệnh gọi đến một phương thức để bắt đầu kiểm tra giấy phép — trong LVL, đây là lệnh gọi đến phương thức checkAccess() của đối tượng LicenseChecker mà bạn tạo.
  • Lệnh gọi lại trong đó trả về kết quả kiểm tra giấy phép. Trong LVL, đây là giao diện LicenseCheckerCallback mà bạn triển khai. Giao diện khai báo hai phương thức, allow()dontAllow(), được thư viện gọi dựa trên kết quả kiểm tra giấy phép. Bạn triển khai hai phương thức này với bất kỳ logic nào bạn cần để cho phép hoặc không cho phép người dùng truy cập vào ứng dụng của bạn. Xin lưu ý rằng các phương thức này không xác định việc có cho phép truy cập hay không. Việc xác định đó là trách nhiệm triển khai Policy của bạn. Thay vào đó, các phương thức này chỉ cung cấp hành vi của ứng dụng để làm thế nào cho phép và không cho phép truy cập (và xử lý các lỗi của ứng dụng).

    Phương thức allow()dontAllow() cung cấp "lý do" cho nội dung phản hồi, có thể là một trong các giá trị Policy, LICENSED, NOT_LICENSED hoặc RETRY. Cụ thể, bạn cần xử lý trường hợp mà phương thức này nhận được phản hồi RETRY cho dontAllow() và cung cấp nút "Thử lại" cho người dùng. Trường hợp này có thể đã xảy ra vì dịch vụ không sẵn có trong quá trình yêu cầu.

Hình 1. Tổng quan về một lượt tương tác kiểm tra giấy phép thông thường.

Sơ đồ trên cho thấy cách thức hoạt động của quy trình kiểm tra giấy phép thông thường:

  1. Mã trong mục Activity chính của ứng dụng tạo bản sao của các đối tượng LicenseCheckerCallbackLicenseChecker. Khi tạo LicenseChecker, mã sẽ chuyển vào Context, phương thức triển khai Policy để sử dụng và khoá công khai của tài khoản nhà xuất bản để cấp phép dưới dạng tham số.
  2. Sau đó, mã này sẽ gọi phương thức checkAccess() trên đối tượng LicenseChecker. Việc triển khai phương thức này sẽ gọi Policy để xác định liệu có phản hồi giấy phép hợp lệ được lưu vào bộ nhớ đệm cục bộ trong SharedPreferences hay không.
    • Nếu có thì quá trình triển khai checkAccess() sẽ gọi allow().
    • Nếu không thì LicenseChecker sẽ bắt đầu một yêu cầu kiểm tra giấy phép được gửi đến máy chủ cấp phép.

    Lưu ý: Máy chủ cấp phép luôn trả về LICENSED khi bạn kiểm tra giấy phép của một ứng dụng nháp.

  3. Khi nhận được phản hồi, LicenseChecker sẽ tạo một LicenseValidator để xác minh dữ liệu giấy phép đã ký và trích xuất các trường của phản hồi, sau đó chuyển các trường đó đến Policy của bạn để đánh giá thêm.
    • Nếu giấy phép hợp lệ thì Policy sẽ lưu nội dung phản hồi trong SharedPreferences vào bộ nhớ đệm và thông báo cho trình xác thực. Sau đó, trình xác thực này sẽ gọi phương thức allow() trên đối tượng LicenseCheckerCallback.
    • Nếu giấy phép không hợp lệ thì Policy sẽ thông báo cho trình xác thực. Trình xác thực sẽ gọi phương thức dontAllow() trên LicenseCheckerCallback.
  4. Trong trường hợp lỗi cục bộ hoặc lỗi máy chủ có thể khôi phục, chẳng hạn như khi không có mạng để gửi yêu cầu, LicenseChecker sẽ chuyển phản hồi RETRY đến phương thức processServerResponse() của đối tượng Policy.

    Ngoài ra, cả hai phương thức gọi lại allow()dontAllow() đều nhận được một đối số reason. Lý do của phương thức allow() thường là Policy.LICENSED hoặc Policy.RETRY và lý do dontAllow() thường là Policy.NOT_LICENSED hoặc Policy.RETRY. Các giá trị phản hồi này hữu ích để bạn có thể hiện một phản hồi thích hợp cho người dùng, chẳng hạn như bằng cách cung cấp một nút "Thử lại" khi dontAllow() phản hồi bằngPolicy.RETRY, điều này có thể là do dịch vụ không hoạt động.

  5. Trong trường hợp xảy ra lỗi ứng dụng, chẳng hạn như khi ứng dụng cố gắng kiểm tra giấy phép của tên gói không hợp lệ, LicenseChecker sẽ chuyển phản hồi lỗi tới phương thức applicationError() của LicenseCheckerCallback.

Xin lưu ý rằng ngoài việc bắt đầu kiểm tra giấy phép và xử lý kết quả, được mô tả trong phần bên dưới, ứng dụng còn cần cung cấp Quy trình triển khai chính sách, nếu Policy lưu trữ dữ liệu phản hồi (ví dụ: ServerManagedPolicy), và cách triển khai Trình làm rối mã nguồn.

Thêm các lệnh nhập

Trước tiên, hãy mở tệp lớp Activity chính của ứng dụng và nhập LicenseCheckerLicenseCheckerCallback từ gói 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;

Nếu bạn đang dùng cách triển khai Policy mặc định được cung cấp cùng với LVL, ServerManagedPolicy, hãy nhập tệp này cùng với AESObfuscator. Nếu bạn đang dùng Policy hoặc Obfuscator tuỳ chỉnh, hãy nhập các thông tin này.

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;

Triển khai LicenseCheckerCallback dưới dạng lớp bên trong riêng tư

LicenseCheckerCallback là giao diện do LVL cung cấp để xử lý kết quả kiểm tra giấy phép. Để hỗ trợ cấp phép bằng LVL, bạn phải triển khai LicenseCheckerCallback và các phương thức của nó để cho phép hoặc không cho phép truy cập vào ứng dụng.

Kết quả của việc kiểm tra giấy phép luôn là lệnh gọi đến một trong các phương thức LicenseCheckerCallback, được thực hiện dựa trên xác thực tải trọng phản hồi, chính mã phản hồi máy chủ và bất kỳ quá trình xử lý bổ sung nào khác do Policy cung cấp. Ứng dụng của bạn có thể triển khai các phương thức theo bất kỳ cách nào cần thiết. Nhìn chung, tốt nhất là bạn nên duy trì các phương thức đơn giản, giới hạn các phương thức đó trong việc quản lý trạng thái giao diện người dùng và quyền truy cập vào ứng dụng. Nếu bạn muốn thêm quy trình xử lý các phản hồi giấy phép, chẳng hạn như bằng cách liên hệ với một máy chủ phụ trợ hoặc áp dụng các ràng buộc tuỳ chỉnh, bạn nên cân nhắc việc kết hợp mã đó vào Policy, thay vì nhập mã đó vào các phương thức LicenseCheckerCallback.

Trong hầu hết trường hợp, bạn nên khai báo quá trình triển khai LicenseCheckerCallback dưới dạng một lớp riêng tư bên trong lớp Activity chính của ứng dụng.

Triển khai các phương thức allow()dontAllow() nếu cần. Để bắt đầu, bạn có thể dùng các hành vi xử lý kết quả đơn giản trong các phương thức, chẳng hạn như hiện kết quả cấp phép trong hộp thoại. Điều này giúp bạn chạy ứng dụng sớm hơn và có thể hỗ trợ gỡ lỗi. Sau khi đã xác định được các hành vi chính xác mà bạn mong muốn thì bạn có thể thêm cách xử lý phức tạp hơn.

Dưới đây là một số đề xuất để xử lý các phản hồi không được cấp phép trong dontAllow():

  • Hiện hộp thoại "Thử lại" cho người dùng, kể cả nút để bắt đầu kiểm tra giấy phép mới nếu reason được cung cấp là Policy.RETRY.
  • Hiện hộp thoại "Mua ứng dụng này", bao gồm nút liên kết sâu người dùng đến trang chi tiết của ứng dụng trên Google Play, từ đó người dùng có thể mua ứng dụng. Để biết thêm thông tin về cách thiết lập các đường liên kết như vậy, hãy xem bài viết Liên kết với sản phẩm của bạn.
  • Hiện thông báo Toast cho biết các tính năng của ứng dụng bị hạn chế vì chưa được cấp phép.

Ví dụ bên dưới cho thấy cách ứng dụng mẫu LVL triển khai LicenseCheckerCallback với các phương thức hiện kết quả kiểm tra giấy phép trong hộp thoại.

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

Ngoài ra, bạn nên triển khai phương thức applicationError() mà LVL gọi để cho phép ứng dụng xử lý các lỗi không thể thử lại. Để biết danh sách các lỗi như vậy, hãy xem Mã phản hồi của máy chủ trong Tài liệu tham khảo về hoạt động cấp phép. Bạn có thể triển khai phương thức này theo bất kỳ cách nào cần thiết. Trong hầu hết trường hợp, phương thức này sẽ ghi lại mã lỗi và gọi dontAllow().

Tạo Trình xử lý để đăng từ LicenseCheckerCallback lên luồng giao diện người dùng

Trong quá trình kiểm tra giấy phép, LVL chuyển yêu cầu đến ứng dụng Google Play, ứng dụng này sẽ xử lý thông tin giao tiếp với máy chủ cấp phép. LVL chuyển yêu cầu qua IPC không đồng bộ (dùng Binder) để việc xử lý thực tế và giao tiếp qua mạng không diễn ra trên một luồng do ứng dụng của bạn quản lý. Tương tự, khi nhận được kết quả, ứng dụng Google Play sẽ gọi một phương thức gọi lại qua IPC. Phương thức này sẽ thực thi trong một nhóm luồng IPC trong quá trình xử lý ứng dụng.

Lớp LicenseChecker quản lý giao tiếp IPC của ứng dụng với ứng dụng Google Play, bao gồm cả lệnh gọi gửi yêu cầu và lệnh gọi lại nhận phản hồi. LicenseChecker cũng theo dõi các yêu cầu giấy phép mở và quản lý thời gian chờ của các yêu cầu này.

Để ứng dụng có thể xử lý thời gian chờ đúng cách đồng thời xử lý các phản hồi nhận được mà không ảnh hưởng đến chuỗi giao diện người dùng của ứng dụng, LicenseChecker sẽ tạo một luồng trong nền khi tạo thực thể. Trong luồng, thực thể này thực hiện tất cả quy trình xử lý kết quả kiểm tra giấy phép, cho dù kết quả là phản hồi nhận được từ máy chủ hay lỗi hết thời gian chờ. Khi kết thúc quá trình xử lý, LVL gọi các phương thức LicenseCheckerCallback từ luồng trong nền.

Đối với ứng dụng, điều này có nghĩa là:

  1. Trong nhiều trường hợp, hệ thống sẽ gọi các phương thức LicenseCheckerCallback từ một luồng trong nền.
  2. Các phương thức đó sẽ không thể cập nhật trạng thái hoặc gọi bất kỳ quá trình nào trong luồng giao diện người dùng, trừ khi bạn tạo một Trình xử lý trong luồng giao diện người dùng và đăng các phương thức gọi lại của bạn đăng lên Trình xử lý.

Nếu bạn muốn các phương thức LicenseCheckerCallback cập nhật luồng giao diện người dùng, hãy tạo thực thể Handler trong phương thức onCreate() của Activity chính, như minh hoạ bên dưới. Trong ví dụ này, các phương thức LicenseCheckerCallback của ứng dụng mẫu LVL (xem ở trên) sẽ gọi displayResult() để cập nhật chuỗi giao diện người dùng thông qua phương thức post() của Trình xử lý.

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

Sau đó, trong các phương thức LicenseCheckerCallback, bạn có thể dùng các phương thức Trình xử lý để đăng các đối tượng Runnable hoặc Message lên Trình xử lý. Dưới đây là cách ứng dụng mẫu có trong bài viết LVL đăng Runnable lên một Trình xử lý trong luồng giao diện người dùng để hiện trạng thái giấy phép.

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

Tạo thực thể LicenseChecker và LicenseCheckerCallback

Trong phương thức onCreate() của Activity chính, hãy tạo các thực thể riêng tư của LicenseCheckerCallback và LicenseChecker. Trước tiên, bạn phải tạo thực thể LicenseCheckerCallback vì bạn cần chuyển tệp tham chiếu đến thực thể đó khi gọi hàm khởi tạo cho LicenseChecker.

Khi tạo thực thể LicenseChecker, bạn cần chuyển các tham số sau:

  • Ứng dụng Context
  • Giá trị tham chiếu đến hoạt động triển khai Policy để sử dụng cho việc kiểm tra giấy phép. Trong hầu hết trường hợp, bạn sẽ dùng phương thức triển khai Policy do LVL cung cấp, ServerManagedPolicy.
  • Biến Chuỗi giữ khoá công khai của tài khoản nhà xuất bản để cấp phép.

Nếu dùng ServerManagedPolicy, bạn sẽ không cần truy cập trực tiếp vào lớp, do đó, bạn có thể tạo thực thể của lớp này trong hàm khởi tạo LicenseChecker như trong ví dụ sau. Xin lưu ý rằng bạn cần chuyển tệp tham chiếu đến một thực thể mới của Trình làm rối mã nguồn khi bạn tạo ServerManagedPolicy.

Ví dụ bên dưới cho biết cách tạo thực thể của LicenseCheckerLicenseCheckerCallback từ phương thức onCreate() của một lớp 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.
            );
        ...
    }
}

Xin lưu ý rằng LicenseChecker chỉ gọi các phương thức LicenseCheckerCallback từ luồng giao diện người dùng nếu có phản hồi giấy phép hợp lệ được lưu vào bộ nhớ đệm cục bộ. Nếu yêu cầu kiểm tra giấy phép được gửi tới máy chủ, thì các lệnh gọi lại luôn xuất phát từ luồng trong nền, ngay cả đối với lỗi mạng.

Gọi CheckAccess() để bắt đầu kiểm tra giấy phép

Trong phần Activity chính của bạn, hãy thêm một lệnh gọi vào phương thức checkAccess() của thực thể LicenseChecker. Trong lệnh gọi, hãy truyền một tệp tham chiếu đến thực thể LicenseCheckerCallback dưới dạng tham số. Nếu bạn cần xử lý bất kỳ hiệu ứng giao diện người dùng đặc biệt nào hoặc quản lý trạng thái trước lệnh gọi, thì bạn có thể thấy sự hữu ích khi gọi checkAccess() từ phương thức trình bao bọc. Ví dụ: ứng dụng mẫu LVL gọi checkAccess() từ phương thức trình bao bọc 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);
    }

Nhúng khoá công khai để cấp phép

Đối với mỗi ứng dụng, dịch vụ Google Play sẽ tự động tạo một cặp khoá công khai/riêng tư RSA 2048-bit dùng để cấp phép và thanh toán trong ứng dụng. Cặp khoá này được liên kết duy nhất với ứng dụng. Mặc dù liên kết với ứng dụng, cặp khoá này không giống với khoá bạn dùng để ký ứng dụng (hoặc bắt nguồn từ ứng dụng).

Google Play Console tiết lộ khoá công khai để cấp phép cho tất cả nhà phát triển đã đăng nhập vào Play Console, nhưng khoá riêng tư sẽ được ẩn ở một vị trí an toàn khỏi tất cả người dùng. Khi một ứng dụng yêu cầu kiểm tra giấy phép của ứng dụng được xuất bản trong tài khoản của bạn, máy chủ cấp phép sẽ ký phản hồi của giấy phép bằng cách sử dụng khoá riêng tư của cặp khoá trong ứng dụng. Khi LVL nhận được phản hồi, ứng dụng này sẽ dùng khoá công khai do ứng dụng cung cấp để xác minh chữ ký của phản hồi giấy phép.

Để thêm giấy phép vào ứng dụng, bạn phải lấy khoá công khai của ứng dụng để cấp phép và sao chép khoá đó vào ứng dụng. Dưới đây là cách tìm khoá công khai của ứng dụng để cấp phép:

  1. Truy cập vào Google Play Console rồi đăng nhập. Đảm bảo rằng bạn đăng nhập vào tài khoản mà tại đó ứng dụng bạn đang cấp phép được xuất bản (hoặc sẽ được xuất bản).
  2. Trong trang chi tiết về ứng dụng, hãy tìm đường liên kết Dịch vụ và API rồi nhấp vào đường liên kết đó.
  3. Trong trang Dịch vụ và API, hãy tìm phần Cấp phép và Thanh toán trong ứng dụng. Khoá công khai để cấp phép được cung cấp cho bạn trong trường Khoá cấp phép cho ứng dụng này.

Để thêm khoá công khai vào ứng dụng, bạn chỉ cần sao chép/dán chuỗi khoá từ trường đó vào ứng dụng dưới dạng giá trị của biến Chuỗi BASE64_PUBLIC_KEY. Khi sao chép, hãy đảm bảo rằng bạn đã chọn toàn bộ chuỗi khoá mà không bỏ qua bất kỳ ký tự nào.

Dưới đây là ví dụ của ứng dụng mẫu 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
    ...
}

Gọi phương thức onDestroy () của LicenseChecker để đóng các kết nối IPC

Cuối cùng, để LVL dọn dẹp trước khi ứng dụng Context thay đổi, hãy thêm một lệnh gọi vào phương thức onDestroy() của LicenseChecker trong quá trình triển khai onDestroy() của Activity. Lệnh gọi này sẽ khiến LicenseChecker đóng mọi kết nối IPC đang mở với Dịch vụ cấp phép của ứng dụng Google Play và xoá mọi tệp tham chiếu cục bộ đến dịch vụ và trình xử lý.

Việc không thể gọi phương thức onDestroy() của LicenseChecker có thể gây ra các sự cố trong vòng đời (lifecycle) của ứng dụng. Ví dụ: nếu người dùng thay đổi hướng của màn hình trong khi yêu cầu kiểm tra giấy phép đang hoạt động, thì ứng dụng Context sẽ bị huỷ. Nếu ứng dụng không đóng đúng cách kết nối IPC của LicenseChecker, thì ứng dụng của bạn sẽ gặp sự cố khi nhận được phản hồi. Tương tự, nếu người dùng thoát khỏi ứng dụng trong đang tiến hành kiểm tra giấy phép, thì ứng dụng sẽ gặp sự cố khi nhận được phản hồi, trừ khi ứng dụng đó đã gọi đúng phương thức onDestroy() của LicenseChecker để ngắt kết nối khỏi dịch vụ.

Dưới đây là ví dụ từ ứng dụng mẫu có trong LVL, trong đó mChecker là thực thể LicenseChecker:

Kotlin

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

Java

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

Nếu bạn đang mở rộng hoặc sửa đổi LicenseChecker, thì bạn cũng có thể cần phải gọi phương thức finishCheck() của LicenseChecker để xoá mọi kết nối IPC mở.

Triển khai DeviceLimiter

Trong một số trường hợp, bạn có thể muốn Policy giới hạn số lượng thiết bị thực tế được phép dùng một giấy phép. Điều này sẽ ngăn người dùng di chuyển một ứng dụng được cấp phép sang một số thiết bị và dùng ứng dụng trên các thiết bị đó bằng cùng một mã tài khoản. Việc này cũng sẽ ngăn người dùng "chia sẻ" ứng dụng bằng cách cung cấp thông tin tài khoản liên kết với giấy phép đó cho các cá nhân khác. Sau đó, những cá nhân này có thể đăng nhập vào tài khoản đó trên thiết bị của họ và truy cập vào giấy phép ứng dụng.

LVL hỗ trợ việc cấp phép cho từng thiết bị bằng cách cung cấp một giao diện DeviceLimiter. Giao diện này khai báo một phương thức duy nhất là allowDeviceAccess(). Khi thực hiện phản hồi từ máy chủ cấp phép, LicenseValidator sẽ gọi allowDeviceAccess() và truyền một chuỗi mã nhận dạng người dùng được trích xuất từ phản hồi.

Nếu bạn không muốn hỗ trợ giới hạn thiết bị, bạn không cần làm gì cả — lớp LicenseChecker sẽ tự động dùng cách triển khai mặc định có tên là NullDevicelimiter. Như đã thể hiện trong tên gọi, NullDevice Limiter là một lớp "không hoạt động" có phương thức allowDeviceAccess() chỉ trả về phản hồi LICENSED cho tất cả người dùng và thiết bị.

Thận trọng: Bạn không nên dùng giấy phép cho mỗi thiết bị đối với hầu hết các ứng dụng vì:

  • Phương thức này yêu cầu bạn cung cấp một máy chủ phụ trợ để quản lý người dùng và các mối liên kết của thiết bị, cũng như
  • Điều này có thể vô tình dẫn đến việc người dùng bị từ chối truy cập vào một ứng dụng mà họ đã mua hợp pháp trên thiết bị khác.

Làm rối mã nguồn của bạn

Để đảm bảo tính bảo mật của ứng dụng, đặc biệt là đối với một ứng dụng trả phí sử dụng tính năng cấp phép và/hoặc biện pháp giới hạn và bảo vệ tuỳ chỉnh, điều rất quan trọng là cần làm rối mã nguồn xử lý ứng dụng của bạn. Việc làm rối mã nguồn đúng cách sẽ khiến người dùng gặp khó khăn hơn trong việc dịch ngược mã byte của ứng dụng, sửa đổi mã đó (chẳng hạn như xoá mã kiểm tra giấy phép), rồi biên dịch lại.

Một số chương trình làm rối mã nguồn có sẵn đối cho các ứng dụng Android, bao gồm ProGuard. Ứng dụng này cũng cung cấp các tính năng tối ưu hoá mã. Bạn nên dùng ProGuard hoặc chương trình tương tự để làm rối mã nguồn cho tất cả ứng dụng sử dụng Dịch vụ cấp phép của Google Play.

Xuất bản ứng dụng được cấp phép

Khi hoàn tất quá trình kiểm thử việc triển khai giấy phép, bạn hiện có thể phát hành ứng dụng trên Google Play. Làm theo các bước thông thường để chuẩn bị, , sau đó phát hành ứng dụng.

Nơi yêu cầu hỗ trợ

Nếu bạn có thắc mắc hoặc gặp vấn đề khi triển khai hoạt động xuất bản trong các ứng dụng của mình, vui lòng sử dụng tài nguyên hỗ trợ được liệt kê trong bảng dưới đây. Bằng cách chuyển truy vấn đến đúng diễn đàn, bạn có thể nhận được sự hỗ trợ cần thiết nhanh hơn.

Bảng 2. Tài nguyên hỗ trợ nhà phát triển cho Dịch vụ cấp phép của Google Play.

Loại hỗ trợ Tài nguyên Phạm vi chủ đề
Các vấn đề về việc phát triển và kiểm thử Google Groups: nhà phát triển Android Tải LVL xuống và quá trình tích hợp, dự án thư viện, câu hỏi về Policy, ý tưởng trải nghiệm người dùng, xử lý phản hồi, Obfuscator, IPC, thiết lập môi trường kiểm thử
Stack Overflow: http://stackoverflow.com/questions/tagged/android
Các vấn đề về tài khoản, phát hành và triển khai Diễn đàn trợ giúp Google Play Tài khoản của nhà xuất bản, cặp khoá cấp phép, tài khoản kiểm thử, phản hồi máy chủ, phản hồi kiểm thử, triển khai ứng dụng và kết quả
Câu hỏi thường gặp về việc hỗ trợ Marketlicensing
Công cụ theo dõi lỗi LVL Công cụ theo dõi lỗi dự án Marketlicensing Báo cáo lỗi và vấn đề liên quan cụ thể đến lớp mã nguồn LVL và quá trình triển khai giao diện

Để biết thông tin chung về cách đăng bài lên các nhóm được liệt kê ở trên, hãy xem phần Tài nguyên cộng đồng trên trang Tài nguyên hỗ trợ dành cho nhà phát triển.

Tài nguyên khác

Ứng dụng mẫu đi kèm với LVL cung cấp một ví dụ đầy đủ về cách bắt đầu kiểm tra giấy phép và xử lý kết quả trong lớp MainActivity.