Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.microg.gms.common

import org.microg.gms.checkin.DeviceConfig
import org.microg.gms.checkin.DeviceFeature

fun DeviceConfiguration.asProto(): DeviceConfig = DeviceConfig(
availableFeature = availableFeatures,
Expand All @@ -22,5 +23,6 @@ fun DeviceConfiguration.asProto(): DeviceConfig = DeviceConfig(
screenLayout = screenLayout,
sharedLibrary = sharedLibraries,
touchScreen = touchScreen,
widthPixels = widthPixels
widthPixels = widthPixels,
deviceFeatures = availableFeatures.map { name -> DeviceFeature(name, 0) }
)
7 changes: 7 additions & 0 deletions play-services-core-proto/src/main/proto/deviceconfig.proto
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,11 @@ message DeviceConfig {
optional int32 deviceClass = 16;
// unused
optional int32 maxApkDownloadSizeMb = 17;

repeated DeviceFeature deviceFeatures = 26;
}

message DeviceFeature{
optional string name = 1;
optional int32 value = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ package org.microg.vending.billing
import android.accounts.Account
import android.content.Context
import android.util.Log
import io.ktor.client.plugins.ClientRequestException
import io.ktor.utils.io.errors.IOException
import org.microg.gms.common.DeviceConfiguration
import org.microg.gms.common.asProto
import org.microg.vending.UploadDeviceConfigRequest
import org.microg.vending.billing.core.GooglePlayApi.Companion.URL_DETAILS
import org.microg.vending.billing.core.GooglePlayApi.Companion.URL_PURCHASE
import org.microg.vending.billing.core.GooglePlayApi.Companion.URL_UPLOAD_DEVICE_CONFIG
import org.microg.vending.billing.core.HeaderProvider
import org.microg.vending.billing.core.HttpClient
import org.microg.vending.billing.proto.BuyResponse
import org.microg.vending.billing.proto.GoogleApiResponse

suspend fun HttpClient.acquireFreeAppLicense(context: Context, account: Account, packageName: String): Boolean {
Expand All @@ -19,7 +25,7 @@ suspend fun HttpClient.acquireFreeAppLicense(context: Context, account: Account,
return false
}

val headers = HeaderProvider.getDefaultHeaders(authData, deviceInfo)
var headers = HeaderProvider.getDefaultHeaders(authData, deviceInfo)

// Check if app is free
val detailsResult = try {
Expand Down Expand Up @@ -60,16 +66,31 @@ suspend fun HttpClient.acquireFreeAppLicense(context: Context, account: Account,
"vc" to versionCode.toString()
)

val buyResult = try {
post(
url = URL_PURCHASE,
headers = headers,
params = parameters,
adapter = GoogleApiResponse.ADAPTER
).payload?.buyResponse
} catch (e: IOException) {
Log.e(TAG, "Unable to auto-purchase $packageName because of a network error or unexpected response during purchase", e)
return false
var buyResult : BuyResponse?
try {
buyResult = purchase(headers, parameters)
} catch (e: Exception) {
Log.w(TAG, "acquireFreeAppLicense: purchase failed!", e)
if (e is ClientRequestException && e.response.status.value == 400) {
val deviceConfigResultToken = runCatching { uploadDeviceConfig(context, headers) }.getOrNull()
if (deviceConfigResultToken == null) {
Log.e(TAG, "Unable to auto-purchase $packageName because of a network error or unexpected response during device config upload")
return false
}
deviceConfigResultToken.let {
authData.deviceConfigToken = it
headers = HeaderProvider.getDefaultHeaders(authData, deviceInfo)
}
buyResult = try {
purchase(headers, parameters)
} catch (e: Exception) {
Log.e(TAG, "Unable to auto-purchase $packageName because of a network error or unexpected response during purchase", e)
return false
}
} else {
Log.e(TAG, "Unable to auto-purchase $packageName because of a network error or unexpected response during purchase", e)
return false
}
}

if (buyResult?.deliveryToken.isNullOrBlank()) {
Expand All @@ -80,4 +101,18 @@ suspend fun HttpClient.acquireFreeAppLicense(context: Context, account: Account,
}

return true
}
}

private suspend fun HttpClient.purchase(headers: Map<String, String> = emptyMap(), parameters: Map<String, String> = emptyMap()) = post(
url = URL_PURCHASE,
headers = headers,
params = parameters,
adapter = GoogleApiResponse.ADAPTER
).payload?.buyResponse

private suspend fun HttpClient.uploadDeviceConfig(context: Context, headers: Map<String, String> = emptyMap()) = post(
url = URL_UPLOAD_DEVICE_CONFIG,
headers = headers,
payload = UploadDeviceConfigRequest(DeviceConfiguration(context).asProto()),
adapter = GoogleApiResponse.ADAPTER
).payload?.uploadDeviceConfigResponse?.deviceConfigToken
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ data class AuthData(
val authToken: String,
val gsfId: String = "",
val deviceCheckInConsistencyToken: String = "",
val deviceConfigToken: String = "",
var deviceConfigToken: String = "",
val experimentsConfigToken: String = "",
val dfeCookie: String = ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class GooglePlayApi {
const val URL_AUTH_PROOF_TOKENS = "https://www.googleapis.com/reauth/v1beta/users/me/reauthProofTokens"
const val URL_DETAILS = "$URL_FDFE/details"
const val URL_ITEM_DETAILS = "$URL_FDFE/getItems"
const val URL_UPLOAD_DEVICE_CONFIG = "$URL_FDFE/uploadDeviceConfig"
const val URL_PURCHASE = "$URL_FDFE/purchase"
const val URL_DELIVERY = "$URL_FDFE/delivery"
const val URL_ENTERPRISE_CLIENT_POLICY = "$URL_FDFE/getEnterpriseClientPolicy"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package org.microg.vending.billing.core

import android.util.Log
import org.microg.vending.billing.TAG

object HeaderProvider {
fun getBaseHeaders(authData: AuthData, deviceInfo: DeviceEnvInfo): MutableMap<String, String> {
val headers: MutableMap<String, String> = HashMap()
Expand Down Expand Up @@ -38,6 +35,9 @@ object HeaderProvider {
headers["X-DFE-Device-Checkin-Consistency-Token"] =
authData.deviceCheckInConsistencyToken
}
if (authData.deviceConfigToken.isNotBlank()) {
headers["X-DFE-Device-Config-Token"] = authData.deviceConfigToken
}
return headers
}
}