注意:針對線上應用程式和遊戲的授權機制,建議使用 Play Integrity API。瞭解詳情

授權參考資料

Stay organized with collections Save and categorize content based on your preferences.

LVL 類別和介面

表 1 列出了 Android SDK 提供的授權驗證庫 (LVL) 中所有來源檔案。所有檔案都是 com.android.vending.licensing 套件的一部分。

表 1. LVL 程式庫類別和介面的摘要。

類別 名稱 說明
授權檢查與結果 LicenseChecker 為啟動授權檢查而執行個體化 (或子類別化) 的類別。
LicenseCheckerCallback 為處理授權檢查結果而實作的介面。
政策 政策 為依據授權回應決定是否允許存取應用程式而實作的介面。
ServerManagedPolicy 預設 Policy 實作。透過授權伺服器提供的設定管理授權資料的本機儲存、授權有效性和重試作業。
StrictPolicy 替代 Policy 實作。僅根據伺服器的直接授權回應來強制執行授權。不使用快取,也不重試要求。
資料模糊處理
(選用)
模糊處理工具 使用 Policy (例如 ServerManagedPolicy) 在永久儲存空間中快取授權回應資料時實作的介面。 透過模糊處理演算法對寫入或讀取的資料進行編碼及解碼。
AESObfuscator 預設模糊處理工具實作,使用 AES 加密/解密演算法對資料執行模糊處理/解除模糊處理。
裝置限制
(選用)
DeviceLimiter 如果您希望只有特定裝置可以使用應用程式,可以實作這個介面。從 LicenseValidator 呼叫。對於大多數應用程式而言,除非設計時十分謹慎,否則不建議實作 DeviceLimiter,因為 DeviceLimiter 需要使用後端伺服器,而且可能會導致使用者無法存取授權的應用程式。
NullDeviceLimiter 預設 DeviceLimiter 實作,此為一種免人工管理 (允許存取所有裝置)。
程式庫核心,無需整合 ResponseData 這個類別包含授權回應欄位。
LicenseValidator 這個類別解密及驗證從授權伺服器收到的回應。
ValidationException 這個類別表示在驗證模糊處理工具管理的資料完整性時所發生的錯誤。
PreferenceObfuscator 公用程式類別,用於將經模糊處理的資料寫入系統的 SharedPreferences 儲存空間或從中讀取此類資料。
ILicensingService 單向 IPC 介面,用於向 Google Play 用戶端傳遞授權檢查要求。
ILicenseResultListener 單向 IPC 回呼實作,用於讓應用程式接收來自授權伺服器的非同步回應。

伺服器回應代碼

表 2 列出了授權伺服器支援的所有授權回應代碼。一般來說,應用程式應處理所有這些回應代碼。根據預設,LVL 中的 LicenseValidator 類別可為您對這些回應代碼執行所有必要處理。

表 2. Google Play 伺服器在授權回應中傳回的回應代碼摘要。

回應代碼 說明 是否已簽署? 額外項目 注釋
LICENSED 使用者已獲得應用程式授權。使用者已購買應用程式,或是獲得授權,可以下載及安裝 Alpha 版或 Beta 版應用程式。 VTGTGR 依據 Policy 限制條件允許存取。
LICENSED_OLD_KEY 使用者已獲得應用程式授權,但有透過其他金鑰簽署的應用程式更新版本可使用。 VTGTGRUT 依據 Policy 的限制條件,可以選擇允許存取。

可能表示已安裝的應用程式版本使用的金鑰組無效或遭駭。應用程式可視需要允許存取,或告知使用者有可用的升級,並在升級前限制進一步使用應用程式。

NOT_LICENSED 使用者尚未獲得應用程式授權。 不允許存取。
ERROR_CONTACTING_SERVER 本機錯誤:Google Play 應用程式無法連線至授權伺服器,原因可能是網路可用性問題。 依據 Policy 重試限制條件,再次嘗試授權檢查。
ERROR_SERVER_FAILURE 伺服器錯誤:伺服器無法載入應用程式的授權金鑰組。 依據 Policy 重試限制條件,再次嘗試授權檢查。
ERROR_INVALID_PACKAGE_NAME 本機錯誤:應用程式要求對裝置未安裝的套件進行授權檢查。 請勿再次嘗試授權檢查,

這通常是由開發錯誤引起。

ERROR_NON_MATCHING_UID 本機錯誤:應用程式要求對套件進行授權檢查,而該套件的 UID (套件、使用者 ID 對) 與應用程式的 UID 不符。 請勿再次嘗試授權檢查,

這通常是由開發錯誤引起。

ERROR_NOT_MARKET_MANAGED 伺服器錯誤:Google Play 無法辨識應用程式 (套件名稱)。 請勿再次嘗試授權檢查,

這可能表示應用程式並非透過 Google Play 發布,或者授權實作有開發錯誤。

注意:如設定測試環境中所述,回應代碼可由應用程式開發人員和所有已註冊的測試使用者透過 Google Play 管理中心手動覆寫。

注意:您之前可以上傳未發布的「草稿」版本,並藉此測試應用程式,但我們已不再支援這項功能,因此您必須改為將應用程式發布至 Alpha 或 Beta 版發行管道。詳情請參閱不再支援草稿應用程式

伺服器回應額外項目

為協助應用程式在整個應用程式退款期內管理應用程式的存取權並提供其他資訊,授權伺服器會在授權回應中提供多項資訊。具體來說,這項服務針對應用程式的授權有效期、重試寬限期、允許重試次數上限以及其他設定提供建議值。如果您的應用程式使用 APK 擴充檔案,回應還會包含檔案名稱、大小和網址。伺服器會在授權回應的「額外項目」欄位中附加鍵/值組合形式的設定。

任何 Policy 實作都可以從授權回應擷取額外項目設定,並視需要使用這些設定。LVL 的預設 Policy 實作 (ServerManagedPolicy) 會做為運作實作使用,並用於說明如何取得、儲存及使用這項設定。

表 3. Google Play 伺服器在授權回應中提供的授權管理設定摘要。

額外項目說明
VT 授權有效期時間戳記。指定目前 (快取) 授權回應過期而且必須在授權伺服器上重新檢查的日期/時間。請參閱下文有關授權有效期的部分。
GT 寬限期時間戳記。指定政策可能允許存取應用程式的期限何時結束 (即使回應狀態是 RETRY)。

這個值由伺服器管理,但一般值為 5 天或更長時間。請參閱下文有關重試期限和重試次數上限的部分。

GR 重試次數上限。指定在系統拒絕使用者存取應用程式前,Policy 應允許連續執行多少次 RETRY 授權檢查。

這個值由伺服器管理,但一般值為「10」或更高。請參閱下文有關重試期限和重試次數上限的部分。

UT 更新時間戳記。指定上傳與發布這個應用程式最近更新的日期/時間。

伺服器只會為 LICENSED_OLD_KEYS 回應傳回這個額外項目,藉此讓 Policy 能確定在使用新的授權金鑰發布更新後,到系統拒絕使用者存取應用程式前經過了多久時間。

FILE_URL1FILE_URL2 擴充檔案的網址 (1 代表主要檔案,2 代表修補型檔案)。使用這個項目就能透過 HTTP 下載檔案。
FILE_NAME1FILE_NAME2 擴充檔案的名稱 (1 代表主要檔案,2 代表修補型檔案)。在裝置上儲存檔案時,您必須使用這個名稱。
FILE_SIZE1FILE_SIZE2 以位元組為單位的檔案大小 (1 代表主要檔案,2 代表修補型檔案)。使用這個項目可幫助您下載,並且確保在下載之前裝置的共用儲存位置有足夠的可用空間。

授權有效期

Google Play 授權伺服器會為所有下載的應用程式設定授權有效期。這個有效期表示一段時間間隔,期間應用程式的授權狀態應視為在應用程式中未變更,並且可由授權 Policy 快取。授權伺服器會在所有授權檢查的回應中納入有效期,並在鍵 VT 之下以額外項目的形式,將有效期結束時間戳記附加到回應中。Policy 可擷取 VT 鍵值,並使用這個值有條件地允許存取應用程式而無須重新檢查授權,直到有效期到期。

如果授權 Policy 必須透過授權伺服器重新檢查授權狀態,授權有效期會向這個授權發出信號,目的「不是」為了表明應用程式是否已獲得使用授權。也就是說,如果應用程式的授權有效期到期,並不代表應用程式不再具有使用授權,而是表示 Policy 必須透過伺服器重新檢查授權狀態。因此,只要授權有效期未到期,Policy 就可以在本機快取初始授權狀態並傳回快取的授權狀態,而不是向伺服器傳送新的授權檢查。

授權伺服器透過管理有效期,協助應用程式在 Google Play 為付費應用程式提供的退款期內正確執行授權。授權伺服器會依據應用程式是否已被購買 (以及如果已有人購買應用程式,是多久前購買) 來設定有效期。具體來說,伺服器會按照以下做法設定有效期:

  • 如果是付費應用程式,伺服器會設定初始授權有效期,如此一來,只要應用程式仍在可退款期內,授權回應就仍會有效。應用程式中的授權 Policy 可以快取初始授權檢查的結果,而且在有效期結束前不需要重新檢查授權。
  • 應用程式過了可退款期後,伺服器會設定較長的有效期 (通常為幾天)。
  • 如果是免費應用程式,伺服器會將有效期設為非常高的值 (long.MAX_VALUE)。這樣可確保只要 Policy 在本機快取有效期時間戳記,日後就不需重新檢查應用程式的授權狀態。

ServerManagedPolicy 實作使用擷取的時間戳記 (mValidityTimestamp) 做為主要條件,藉此判斷是否要先透過伺服器重新檢查授權狀態,再允許使用者存取應用程式。

重試期限和重試次數上限

在某些情況下,系統或網路狀態會讓應用程式的授權檢查無法傳送至授權伺服器,或導致無法將伺服器的回應傳送至 Google Play 用戶端應用程式。例如,使用者可能在沒有行動網路或數據連線 (例如搭乘飛機),或網路連線不穩定/行動網路訊號微弱時啟動應用程式。

當網路問題導致授權檢查無法執行或中斷時,Google Play 用戶端會向 PolicyprocessServerResponse() 方法傳回 RETRY 回應代碼,藉此通知應用程式。如果發生系統問題,例如應用程式無法與 Google Play 的 ILicensingService 實作繫結,LicenseChecker 程式庫會自行使用 RETRY 回應代碼呼叫政策 processServerResonse() 方法。

一般來說,RETRY 回應代碼是向應用程式傳送的信號,表示系統發生錯誤,導致授權檢查無法完成。

Google Play 伺服器透過設定重試「寬限期」和建議的重試次數上限,協助應用程式在發生錯誤時管理授權。伺服器會在所有授權檢查回應中納入這些值,並透過額外項目的形式將這些值附加到鍵 GTGR 之下。

應用程式 Policy 可以擷取 GTGR 額外項目,並使用這些項目有條件地允許存取應用程式,如下所示:

  • 如果授權檢查的結果是 RETRY 回應,則 Policy 應快取 RETRY 回應代碼,並增加一次 RETRY 回應的計數。
  • 如果重試寬限期仍有效,或者未達到重試次數上限,則 Policy 應允許使用者存取應用程式。

ServerManagedPolicy 使用伺服器提供的上述 GTGR 值。以下範例顯示了 allow() 方法中重試回應的有條件處理。RETRY 回應的計數在 processServerResponse() 方法中保持不變,並且未顯示。

Kotlin

fun allowAccess(): Boolean {
    val ts = System.currentTimeMillis()
    return when(lastResponse) {
        LICENSED -> {
            // Check if the LICENSED response occurred within the validity timeout.
            ts <= validityTimestamp  // Cached LICENSED response is still valid.
        }
        RETRY -> {
            ts < lastResponseTime + MILLIS_PER_MINUTE &&
                    // Only allow access if we are within the retry period
                    // or we haven't used up our max retries.
                    (ts <= retryUntil || retryCount <= maxRetries)
        }
        else -> false
    }
}

Java

public boolean allowAccess() {
    long ts = System.currentTimeMillis();
    if (lastResponse == LicenseResponse.LICENSED) {
        // Check if the LICENSED response occurred within the validity timeout.
        if (ts <= validityTimestamp) {
            // Cached LICENSED response is still valid.
            return true;
        }
    } else if (lastResponse == LicenseResponse.RETRY &&
                ts < lastResponseTime + MILLIS_PER_MINUTE) {
        // Only allow access if we are within the retry period
        // or we haven't used up our max retries.
        return (ts <= retryUntil || retryCount <= maxRetries);
    }
    return false;
}