Skip to content

Commit

Permalink
Asset Modules: Improve error handling and add compression format
Browse files Browse the repository at this point in the history
  • Loading branch information
mar-v-in committed Dec 8, 2024
1 parent 2f592dd commit eb484ba
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public final class BundleKeys {
public static PackKey<ArrayList<String>> SLICE_IDS = new PackKey.StringArrayList("slice_ids");

public static SliceKey<ArrayList<Intent>> CHUNK_INTENTS = new SliceKey.ParcelableArrayList<>("chunk_intents", Intent.class);
public static SliceKey<Integer> COMPRESSION_FORMAT = new SliceKey.Int("compression_format");
public static SliceKey<Integer> PATCH_FORMAT = new SliceKey.Int("patch_format");
public static SliceKey<@CompressionFormat Integer> COMPRESSION_FORMAT = new SliceKey.Int("compression_format");
public static SliceKey<@PatchFormat Integer> PATCH_FORMAT = new SliceKey.Int("patch_format");
public static SliceKey<String> UNCOMPRESSED_HASH_SHA256 = new SliceKey.String("uncompressed_hash_sha256");
public static SliceKey<Long> UNCOMPRESSED_SIZE = new SliceKey.Long("uncompressed_size");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,20 @@ class AssetModuleServiceImpl(

override suspend fun getSessionStates(params: GetSessionStatesParameters, packageName: String, callback: IAssetModuleServiceCallback?) {
val listBundleData: MutableList<Bundle> = mutableListOf()
packageName.takeIf { it == packageDownloadData[packageName]?.packageName }?.let {
packageDownloadData[packageName]?.moduleNames?.forEach { moduleName ->
if (moduleName in params.installedAssetModules) return@forEach

listBundleData.add(sendBroadcastForExistingFile(context, packageDownloadData[packageName]!!, moduleName, null, null))
packageDownloadData[packageName]?.moduleNames?.forEach { moduleName ->
if (moduleName in params.installedAssetModules) return@forEach

packageDownloadData[packageName]?.getModuleData(moduleName)?.chunks?.forEach { chunkData ->
val destination = chunkData.getChunkFile(context)
if (destination.exists() && destination.length() == chunkData.chunkBytesToDownload) {
sendBroadcastForExistingFile(context, packageDownloadData[packageName]!!, moduleName, chunkData, destination)
}
listBundleData.add(sendBroadcastForExistingFile(context, packageDownloadData[packageName]!!, moduleName, null, null))

packageDownloadData[packageName]?.getModuleData(moduleName)?.chunks?.forEach { chunkData ->
val destination = chunkData.getChunkFile(context)
if (destination.exists() && destination.length() == chunkData.chunkBytesToDownload) {
sendBroadcastForExistingFile(context, packageDownloadData[packageName]!!, moduleName, chunkData, destination)
}
}
}

Log.d(TAG, "getSessionStates: $listBundleData")
callback?.onGetSessionStates(listBundleData)
}
Expand Down Expand Up @@ -165,7 +165,8 @@ class AssetModuleServiceImpl(
checkSessionValid(packageName, params.sessionId)

// TODO: Implement
throw UnsupportedOperationException()
callback?.onNotifySessionFailed(bundleOf(BundleKeys.SESSION_ID to params.sessionId))
//throw UnsupportedOperationException()
}

override suspend fun keepAlive(params: KeepAliveParameters, packageName: String, callback: IAssetModuleServiceCallback?) {
Expand All @@ -176,13 +177,12 @@ class AssetModuleServiceImpl(
override suspend fun getChunkFileDescriptor(params: GetChunkFileDescriptorParameters, packageName: String, callback: IAssetModuleServiceCallback?) {
checkSessionValid(packageName, params.sessionId)

val parcelFileDescriptor = runCatching {
val downLoadFile = context.getChunkFile(params.sessionId, params.moduleName, params.sliceId, params.chunkNumber)
ParcelFileDescriptor.open(downLoadFile, ParcelFileDescriptor.MODE_READ_ONLY).also {
fileDescriptorMap[downLoadFile] = it
}
}.getOrNull()
val downLoadFile = context.getChunkFile(params.sessionId, params.moduleName, params.sliceId, params.chunkNumber)
val parcelFileDescriptor = ParcelFileDescriptor.open(downLoadFile, ParcelFileDescriptor.MODE_READ_ONLY).also {
fileDescriptorMap[downLoadFile] = it
}

Log.d(TAG, "getChunkFileDescriptor -> $parcelFileDescriptor")
callback?.onGetChunkFileDescriptor(
bundleOf(BundleKeys.CHUNK_FILE_DESCRIPTOR to parcelFileDescriptor),
bundleOf(BundleKeys.ERROR_CODE to AssetPackErrorCode.NO_ERROR)
Expand All @@ -206,6 +206,7 @@ class AssetModuleServiceImpl(
}
}
val bundleData = buildDownloadBundle(packageDownloadData[packageName]!!, params.moduleNames)
Log.d(TAG, "requestDownloadInfo -> $bundleData")
callback?.onRequestDownloadInfo(bundleData, bundleData)
}

Expand All @@ -217,6 +218,7 @@ class AssetModuleServiceImpl(

override suspend fun cancelDownloads(params: CancelDownloadsParameters, packageName: String, callback: IAssetModuleServiceCallback?) {
// TODO: Implement
throw UnsupportedOperationException()
callback?.onCancelDownloads(bundleOf(BundleKeys.ERROR_CODE to AssetPackErrorCode.NO_ERROR))
//throw UnsupportedOperationException()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package com.google.android.finsky.assetmoduleservice

import android.content.Context
import com.google.android.finsky.getChunkFile
import com.google.android.play.core.assetpacks.protocol.CompressionFormat
import java.io.File
import java.io.Serializable

Expand Down Expand Up @@ -66,6 +67,7 @@ data class ChunkData(
val chunkSourceUri: String?,
val chunkBytesToDownload: Long,
val chunkIndex: Int,
val sliceCompressionFormat: @CompressionFormat Int,
val sliceUncompressedSize: Long,
val sliceUncompressedHashSha256: String?,
val numberOfChunksInSlice: Int
Expand Down
54 changes: 25 additions & 29 deletions vending-app/src/main/kotlin/com/google/android/finsky/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.accounts.Account
import android.accounts.AccountManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.util.Log
Expand All @@ -19,6 +20,7 @@ import androidx.core.content.pm.PackageInfoCompat
import com.android.vending.licensing.AUTH_TOKEN_SCOPE
import com.android.vending.licensing.getAuthToken
import com.android.vending.licensing.getLicenseRequestHeaders
import com.google.android.finsky.assetmoduleservice.AssetPackException
import com.google.android.finsky.assetmoduleservice.DownloadData
import com.google.android.finsky.assetmoduleservice.ModuleData
import com.google.android.finsky.assetmoduleservice.ChunkData
Expand All @@ -28,7 +30,6 @@ import com.google.android.play.core.assetpacks.protocol.BroadcastConstants
import com.google.android.play.core.assetpacks.protocol.BundleKeys
import com.google.android.play.core.assetpacks.protocol.CompressionFormat
import com.google.android.play.core.assetpacks.protocol.PatchFormat
import kotlinx.coroutines.runBlocking
import org.microg.gms.auth.AuthConstants
import org.microg.vending.billing.GServices
import org.microg.vending.billing.core.HttpClient
Expand All @@ -44,7 +45,11 @@ private const val ASSET_MODULE_DELIVERY_URL = "https://play-fe.googleapis.com/fd
private const val TAG = "AssetModuleRequest"

fun getAppVersionCode(context: Context, packageName: String): Long? {
return runCatching { PackageInfoCompat.getLongVersionCode(context.packageManager.getPackageInfo(packageName, 0)) }.getOrNull()
return try {
PackageInfoCompat.getLongVersionCode(context.packageManager.getPackageInfo(packageName, 0))
} catch (e: PackageManager.NameNotFoundException) {
throw AssetPackException(AssetPackErrorCode.APP_UNAVAILABLE, e.message)
}
}

fun <T> Bundle?.get(key: BundleKeys.RootKey<T>): T? = if (this == null) null else BundleKeys.get(this, key)
Expand Down Expand Up @@ -105,14 +110,12 @@ suspend fun HttpClient.initAssetModuleData(

val androidId = GServices.getString(context.contentResolver, "android_id", "0")?.toLong() ?: 1

val moduleDeliveryInfo = runCatching {
post(
url = ASSET_MODULE_DELIVERY_URL,
headers = getLicenseRequestHeaders(oauthToken, androidId),
payload = requestPayload,
adapter = AssetModuleDeliveryResponse.ADAPTER
).wrapper?.deliveryInfo
}.getOrNull()
val moduleDeliveryInfo = post(
url = ASSET_MODULE_DELIVERY_URL,
headers = getLicenseRequestHeaders(oauthToken, androidId),
payload = requestPayload,
adapter = AssetModuleDeliveryResponse.ADAPTER
).wrapper?.deliveryInfo
Log.d(TAG, "initAssetModuleData: moduleDeliveryInfo-> $moduleDeliveryInfo")
return initModuleDownloadInfo(packageName, appVersionCode, moduleDeliveryInfo)
}
Expand Down Expand Up @@ -187,6 +190,7 @@ private fun initModuleDownloadInfo(packageName: String, appVersionCode: Long?, d
chunkSourceUri = dResource.sourceUri,
chunkBytesToDownload = dResource.bytesToDownload,
chunkIndex = chunkIndex,
sliceCompressionFormat = sliceInfo.fullDownloadInfo.compressionFormat ?: CompressionFormat.UNSPECIFIED,
sliceUncompressedSize = uncompressedSize ?: 0,
sliceUncompressedHashSha256 = uncompressedHashSha256,
numberOfChunksInSlice = numberOfChunks
Expand Down Expand Up @@ -269,34 +273,26 @@ fun sendBroadcastForExistingFile(context: Context, downloadData: DownloadData, m
downloadBundle.put(BundleKeys.PACK_BASE_VERSION, moduleName, packData.moduleVersion)
downloadBundle.put(BundleKeys.PACK_VERSION_TAG, moduleName, null)
packData.chunks.map { it.copy() }.forEach {
val sliceId = it.sliceId ?: ""
val uncompressedSize = it.sliceUncompressedSize
val uncompressedHashSha256 = it.sliceUncompressedHashSha256
val numberOfChunksInSlice = it.numberOfChunksInSlice
val chunkIntents: ArrayList<Intent?>
if (destination == null) {
chunkIntents = ArrayList(Collections.nCopies<Intent?>(numberOfChunksInSlice, null))
} else {
val uFile = Uri.parse(destination.absolutePath).path?.let { path -> File(path) }
chunkIntents = ArrayList(Collections.nCopies<Intent?>(numberOfChunksInSlice, null))
val uri = Uri.fromFile(uFile)
val sliceId = it.sliceId
val chunkIntents = ArrayList(Collections.nCopies<Intent?>(it.numberOfChunksInSlice, null))
if (chunkData != null && destination != null) {
val uri = Uri.fromFile(destination)
context.grantUriPermission(moduleName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, context.contentResolver.getType(uri))
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val resourceBlockIndex = chunkData?.chunkIndex
if (uFile?.exists() == true && chunkData?.sliceId == sliceId && resourceBlockIndex != null) {
if (chunkIntents[resourceBlockIndex] == null) {
chunkIntents[resourceBlockIndex] = intent
if (destination.exists() && chunkData.moduleName == moduleName && chunkData.sliceId == sliceId) {
if (chunkIntents[chunkData.chunkIndex] == null) {
chunkIntents[chunkData.chunkIndex] = intent
}
}
}
downloadBundle.put(BundleKeys.CHUNK_INTENTS, moduleName, sliceId, chunkIntents)
downloadBundle.put(BundleKeys.UNCOMPRESSED_SIZE, moduleName, sliceId, uncompressedSize)
downloadBundle.put(BundleKeys.COMPRESSION_FORMAT, moduleName, sliceId, 1) // TODO
downloadBundle.put(BundleKeys.UNCOMPRESSED_HASH_SHA256, moduleName, sliceId, uncompressedHashSha256)
downloadBundle.put(BundleKeys.UNCOMPRESSED_SIZE, moduleName, sliceId, it.sliceUncompressedSize)
downloadBundle.put(BundleKeys.COMPRESSION_FORMAT, moduleName, sliceId, it.sliceCompressionFormat)
downloadBundle.put(BundleKeys.UNCOMPRESSED_HASH_SHA256, moduleName, sliceId, it.sliceUncompressedHashSha256)
}
downloadBundle.put(BundleKeys.SLICE_IDS, moduleName, ArrayList(packData.chunks.mapNotNull { it.sliceId }.distinct()))
downloadBundle.put(BundleKeys.SLICE_IDS, moduleName, ArrayList(packData.chunks.map { it.sliceId }.distinct()))
sendBroadCast(context, downloadData, downloadBundle)
return downloadBundle
} catch (e: Exception) {
Expand Down
2 changes: 1 addition & 1 deletion vending-app/src/main/proto/AssetModule.proto
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ message PatchInfo {

message ChunkInfo {
optional int64 bytesToDownload = 1;
optional string unknown = 2;
optional string sha256 = 2;
optional string sourceUri = 3;
}

0 comments on commit eb484ba

Please sign in to comment.