Skip to content

Commit

Permalink
insert apps into blobdb without predownload
Browse files Browse the repository at this point in the history
  • Loading branch information
crc-32 committed Aug 14, 2024
1 parent 37dafd6 commit ef03381
Show file tree
Hide file tree
Showing 35 changed files with 1,020 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ class BackgroundAppInstallBridge @Inject constructor(
return true
}

suspend fun downloadPbw(uuid: String): String? {
val appInstallCallbacks = getAppInstallCallbacks() ?: error("No app install callbacks")

return awaitPigeonMethod { reply ->
appInstallCallbacks.downloadPbw(uuid, reply)
}
}

private suspend fun getAppInstallCallbacks(): Pigeons.BackgroundAppInstallCallbacks? {
val cachedAppInstallCallbacks = cachedAppInstallCallbacks
if (cachedAppInstallCallbacks != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,24 @@ import io.rebble.cobble.middleware.getBestVariant
import io.rebble.cobble.pigeons.BooleanWrapper
import io.rebble.cobble.pigeons.NumberWrapper
import io.rebble.cobble.pigeons.Pigeons
import io.rebble.cobble.pigeons.Pigeons.NumberWrapper
import io.rebble.cobble.util.*
import io.rebble.libpebblecommon.disk.PbwBinHeader
import io.rebble.libpebblecommon.metadata.WatchHardwarePlatform
import io.rebble.libpebblecommon.metadata.pbw.appinfo.PbwAppInfo
import io.rebble.libpebblecommon.packets.AppOrderResultCode
import io.rebble.libpebblecommon.packets.AppReorderRequest
import io.rebble.libpebblecommon.packets.blobdb.AppMetadata
import io.rebble.libpebblecommon.packets.blobdb.BlobCommand
import io.rebble.libpebblecommon.packets.blobdb.BlobResponse
import io.rebble.libpebblecommon.services.AppReorderService
import io.rebble.libpebblecommon.services.blobdb.BlobDBService
import io.rebble.libpebblecommon.structmapper.SUInt
import io.rebble.libpebblecommon.structmapper.SUUID
import io.rebble.libpebblecommon.structmapper.StructMapper
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.first
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import okio.buffer
Expand Down Expand Up @@ -143,17 +147,10 @@ class AppInstallFlutterBridge @Inject constructor(
}
}

override fun insertAppIntoBlobDb(arg: Pigeons.StringWrapper, result: Pigeons.Result<Pigeons.NumberWrapper>) {
override fun insertAppIntoBlobDb(app: Pigeons.LockerAppPigeon, result: Pigeons.Result<NumberWrapper>) {
coroutineScope.launchPigeonResult(result, Dispatchers.IO) {
NumberWrapper(try {
val appUuid = arg.value!!

val appFile = getAppPbwFile(context, appUuid)
if (!appFile.exists()) {
error("PBW file ${appFile.absolutePath} missing")
}
Timber.d("Len: ${appFile.length()}")

val resp = try {
val appUuid = app.uuid

// Wait some time for metadata to become available in case this has been called
// Right after watch has been connected
Expand All @@ -170,47 +167,66 @@ class AppInstallFlutterBridge @Inject constructor(
?.watchType
?: error("Unknown hardware platform $hardwarePlatformNumber")

val appInfo = requirePbwAppInfo(appFile)

val targetWatchType = getBestVariant(connectedWatchType, appInfo.targetPlatforms)
val targetWatchType = getBestVariant(connectedWatchType, app.supportedHardware)
?: error("Watch $connectedWatchType is not compatible with app $appUuid. " +
"Compatible apps: ${appInfo.targetPlatforms}")


val manifest = requirePbwManifest(appFile, targetWatchType)

Timber.d("Manifest %s", manifest)
"Compatible apps: ${app.supportedHardware}")

val appBlob = requirePbwBinaryBlob(appFile, targetWatchType, manifest.application.name)
val flags = app.processInfoFlags[connectedWatchType.codename]?.value ?: 0L

val headerData = appBlob
.buffer().use { it.readByteArray(PbwBinHeader.SIZE.toLong()) }
val appVersionMajor = try {
app.version.split(".")?.getOrNull(0)?.toUByte() ?: 0u
} catch (e: NumberFormatException) {
0u
}
val appVersionMinor = try {
app.version.split(".")?.getOrNull(1)?.toUByte() ?: 0u
} catch (e: NumberFormatException) {
0u
}

val parsedHeader = PbwBinHeader.parseFileHeader(headerData.toUByteArray())
val sdkVersionMajor = app.sdkVersions[connectedWatchType.codename]?.split(".")?.get(0)?.toUByte() ?: 0u
val sdkVersionMinor = app.sdkVersions[connectedWatchType.codename]?.split(".")?.getOrNull(1)?.toUByte() ?: 0u

val insertResult = blobDBService.send(
BlobCommand.InsertCommand(
Random.nextInt(0, UShort.MAX_VALUE.toInt()).toUShort(),
BlobCommand.BlobDatabase.App,
parsedHeader.uuid.toBytes(),
parsedHeader.toBlobDbApp().toBytes()
SUUID(StructMapper(), UUID.fromString(appUuid)).toBytes(),
AppMetadata().also {
it.uuid.set(UUID.fromString(appUuid))
it.flags.set(flags.toString().toUInt())
it.icon.set(0u)
it.appVersionMajor.set(appVersionMajor)
it.appVersionMinor.set(appVersionMinor)
it.sdkVersionMajor.set(sdkVersionMajor)
it.sdkVersionMinor.set(sdkVersionMinor)
it.appName.set(app.shortName)
}.toBytes()
)
)

insertResult.responseValue.value.toInt()
insertResult.responseValue.value.toLong()
} catch (e: Exception) {
if (e is SerializationException) {
throw e
}
Timber.e(e, "Failed to send PBW file to the BlobDB")
BlobResponse.BlobStatus.GeneralFailure.value.toInt()
})
BlobResponse.BlobStatus.GeneralFailure.value.toLong()
}
NumberWrapper.Builder()
.setValue(resp)
.build()
}
}

override fun beginAppDeletion(arg: Pigeons.StringWrapper,
result: Pigeons.Result<Pigeons.BooleanWrapper>) {
coroutineScope.launchPigeonResult(result) {
getAppPbwFile(context, arg.value!!).delete()

BooleanWrapper(backgroundAppInstallBridge.deleteApp(arg))
val resp = withContext(Dispatchers.Main) {
backgroundAppInstallBridge.deleteApp(arg)
}
BooleanWrapper(resp)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package io.rebble.cobble.handlers

import android.content.Context
import android.net.Uri
import androidx.core.net.toFile
import io.rebble.cobble.bridges.background.BackgroundAppInstallBridge
import io.rebble.cobble.datasources.WatchMetadataStore
import io.rebble.cobble.middleware.PutBytesController
import io.rebble.cobble.middleware.getBestVariant
import io.rebble.cobble.pigeons.Pigeons
import io.rebble.cobble.shared.handlers.CobbleHandler
import io.rebble.cobble.util.getAppPbwFile
import io.rebble.cobble.util.requirePbwAppInfo
Expand All @@ -12,19 +16,22 @@ import io.rebble.libpebblecommon.packets.AppFetchRequest
import io.rebble.libpebblecommon.packets.AppFetchResponse
import io.rebble.libpebblecommon.packets.AppFetchResponseStatus
import io.rebble.libpebblecommon.services.AppFetchService
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import timber.log.Timber
import java.io.File
import javax.inject.Inject

class AppInstallHandler @Inject constructor(
coroutineScope: CoroutineScope,
private val context: Context,
private val appFetchService: AppFetchService,
private val putBytesController: PutBytesController,
private val watchMetadataStore: WatchMetadataStore
private val watchMetadataStore: WatchMetadataStore,
private val backgroundInstallBridge: BackgroundAppInstallBridge
) : CobbleHandler {
init {
coroutineScope.launch {
Expand All @@ -39,12 +46,21 @@ class AppInstallHandler @Inject constructor(
private suspend fun onNewAppFetchRequest(message: AppFetchRequest) {
try {
val appUuid = message.uuid.get().toString()
val appFile = getAppPbwFile(context, appUuid)
var appFile = getAppPbwFile(context, appUuid)

if (!appFile.exists()) {
respondFetchRequest(AppFetchResponseStatus.INVALID_UUID)
Timber.e("Watch requested nonexistent app data (%s)", appUuid)
return
val uri = backgroundInstallBridge.downloadPbw(appUuid)?.let { Uri.parse(it) }
if (uri == null) {
Timber.e("Failed to download app %s", appUuid)
respondFetchRequest(AppFetchResponseStatus.NO_DATA)
return
}
appFile = uri.toFile()
if (!appFile.exists()) {
Timber.e("Downloaded app file does not exist")
respondFetchRequest(AppFetchResponseStatus.NO_DATA)
return
}
}

if (putBytesController.status.value.state != PutBytesController.State.IDLE) {
Expand Down
Loading

0 comments on commit ef03381

Please sign in to comment.