diff --git a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt index 192bedfb31..93b5660b8d 100644 --- a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt +++ b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt @@ -7,6 +7,7 @@ import flank.corellium.client.core.getProjectInstancesList import flank.corellium.client.core.startInstance import flank.corellium.client.core.waitUntilInstanceIsReady import flank.corellium.client.data.Instance +import flank.corellium.client.data.Instance.BootOptions.AdditionalTags.GPU import kotlinx.coroutines.channels.SendChannel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.channelFlow @@ -20,23 +21,24 @@ private const val SCREEN = "720x1280:280" fun invokeAndroidDevices( projectName: String, -) = AndroidInstance.Invoke { (amount) -> +) = AndroidInstance.Invoke { config -> channelFlow { val projectId = getProjectId(projectName) - val instances = getCreatedInstances(projectId, amount) + val instances = getCreatedInstances(projectId, config.amount) startNotRunningInstances(instances) val ids = instances.map(Instance::id) + let { // When existing instances size match required amount // there is not needs for creating more instances. - if (instances.size == amount) emptyList() + if (instances.size == config.amount) emptyList() // Otherwise is required to create some additional instances else createInstances( projectId = projectId, + gpuAcceleration = config.gpuAcceleration, indexes = calculateAdditionalIndexes( current = instances, - requiredAmount = amount - ) + requiredAmount = config.amount + ), ) } @@ -87,7 +89,8 @@ private suspend fun startNotRunningInstances( */ private suspend fun createInstances( projectId: String, - indexes: List + indexes: List, + gpuAcceleration: Boolean, ) = indexes .apply { println("Creating additional ${indexes.size} instances. Connecting to the agents may take longer.") } .map { index -> @@ -98,7 +101,10 @@ private suspend fun createInstances( flavor = FLAVOUR, os = OS, bootOptions = Instance.BootOptions( - screen = SCREEN + screen = SCREEN, + additionalTags = listOfNotNull( + GPU.takeIf { gpuAcceleration } + ) ) ) ) diff --git a/corellium/api/src/main/kotlin/flank/corellium/api/AndroidInstance.kt b/corellium/api/src/main/kotlin/flank/corellium/api/AndroidInstance.kt index 7956b85941..b21e6fcb5e 100644 --- a/corellium/api/src/main/kotlin/flank/corellium/api/AndroidInstance.kt +++ b/corellium/api/src/main/kotlin/flank/corellium/api/AndroidInstance.kt @@ -12,10 +12,12 @@ object AndroidInstance { /** * Configuration for devices to invoke. * - * @property amount the amount of devices to invoke. + * @property amount The amount of devices to invoke. + * @property gpuAcceleration Enables gpu acceleration for virtual devices. */ data class Config( - val amount: Int + val amount: Int, + val gpuAcceleration: Boolean ) /** @@ -23,7 +25,7 @@ object AndroidInstance { * * After successful invoke, the devices specified in th [Config] should be running and ready for use. * - * @return list of invoked device ids. + * @return List of invoked device ids. */ fun interface Invoke : (Config) -> Flow } diff --git a/corellium/cli/src/main/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommand.kt b/corellium/cli/src/main/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommand.kt index 17ea51c5eb..4032a4cdce 100644 --- a/corellium/cli/src/main/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommand.kt +++ b/corellium/cli/src/main/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommand.kt @@ -90,6 +90,17 @@ class RunTestCorelliumAndroidCommand : ] ) var obfuscate: Boolean? by data + + @set:CommandLine.Option( + names = ["--gpu-acceleration"], + description = [ + "Enable cloud GPU acceleration (Extra costs incurred)." + + "Currently this option only works for newly devices created." + + "To create new device pool with gpu-acceleration, remove old devices manually and let Flank recreate the pool." + ] + ) + @set:JsonProperty("gpu-acceleration") + var gpuAcceleration: Boolean? by data } @CommandLine.Mixin @@ -119,6 +130,7 @@ private fun defaultConfig() = Config().apply { maxTestShards = 1 localResultsDir = null obfuscate = false + gpuAcceleration = true } private fun RunTestCorelliumAndroidCommand.yamlConfig(): Config = @@ -130,4 +142,5 @@ private fun RunTestCorelliumAndroidCommand.createArgs() = Args( maxShardsCount = config.maxTestShards!!, outputDir = config.localResultsDir ?: Args.DefaultOutputDir.new, obfuscateDumpShards = config.obfuscate!!, + gpuAcceleration = config.gpuAcceleration!! ) diff --git a/corellium/cli/src/test/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommandTest.kt b/corellium/cli/src/test/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommandTest.kt index c0597cdabd..50a47caec4 100644 --- a/corellium/cli/src/test/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommandTest.kt +++ b/corellium/cli/src/test/kotlin/flank/corellium/cli/RunTestCorelliumAndroidCommandTest.kt @@ -34,6 +34,7 @@ class RunTestCorelliumAndroidCommandTest { maxTestShards = Int.MAX_VALUE localResultsDir = "test_result_dir" obfuscate = true + gpuAcceleration = false } /** @@ -57,6 +58,7 @@ class RunTestCorelliumAndroidCommandTest { "--max-test-shards=$maxTestShards", "--local-result-dir=$localResultsDir", "--obfuscate=$obfuscate", + "--gpu-acceleration=$gpuAcceleration", ) } @@ -75,6 +77,7 @@ apks: max-test-shards: $maxTestShards local-result-dir: $localResultsDir obfuscate: $obfuscate +gpu-acceleration: $gpuAcceleration """.trimIndent() } @@ -136,7 +139,8 @@ obfuscate: $obfuscate apks = apks!!, maxShardsCount = maxTestShards!!, outputDir = localResultsDir!!, - obfuscateDumpShards = obfuscate!! + obfuscateDumpShards = obfuscate!!, + gpuAcceleration = gpuAcceleration!!, ) } diff --git a/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt b/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt index b3a4e7c8de..efa1ca2f74 100644 --- a/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt +++ b/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt @@ -38,14 +38,29 @@ data class Instance( val info: String = "" ) + /** + * @param udid Predefined Unique Device ID (UDID) for iOS device + * @param screen Change the screen metrics for Ranchu devices `XxY[:DPI]`, e.g. `720x1280:280` + * @param additionalTags features to utilize for the device, valid options include. Check [AdditionalTags] + */ @Serializable data class BootOptions( val bootArgs: String = "", val restoreBootArgs: String = "", val udid: String = "", val ecid: String = "", - val screen: String = "" - ) + val screen: String = "", + val additionalTags: List = emptyList(), + ) { + /** + * @property GPU Enable cloud GPU acceleration (Extra costs incurred, cloud only). + * @property KALLOC Enable kalloc/kfree trace access via GDB (Enterprise only). + */ + object AdditionalTags { + const val GPU = "gpu" + const val KALLOC = "kalloc" + } + } } @Serializable diff --git a/corellium/domain/src/main/kotlin/flank/corellium/domain/RunTestCorelliumAndroid.kt b/corellium/domain/src/main/kotlin/flank/corellium/domain/RunTestCorelliumAndroid.kt index 9d0d03285a..9d4fe3776e 100644 --- a/corellium/domain/src/main/kotlin/flank/corellium/domain/RunTestCorelliumAndroid.kt +++ b/corellium/domain/src/main/kotlin/flank/corellium/domain/RunTestCorelliumAndroid.kt @@ -49,6 +49,7 @@ object RunTestCorelliumAndroid { * @param maxShardsCount Maximum amount of shards to create. For each shard Flank is invoking dedicated device instance, so do not use values grater than maximum number available instances in the Corellium account. * @param obfuscateDumpShards Obfuscate the test names in shards before dumping to file. * @param outputDir Set output dir. Default value is [DefaultOutputDir.new] + * @param gpuAcceleration Enable gpu acceleration for newly created virtual devices. */ data class Args( val credentials: Authorization.Credentials, @@ -56,6 +57,7 @@ object RunTestCorelliumAndroid { val maxShardsCount: Int, val obfuscateDumpShards: Boolean = false, val outputDir: String = DefaultOutputDir.new, + val gpuAcceleration: Boolean = true, ) { /** * Default output directory scheme. diff --git a/corellium/domain/src/main/kotlin/flank/corellium/domain/run/test/android/step/InvokeDevices.kt b/corellium/domain/src/main/kotlin/flank/corellium/domain/run/test/android/step/InvokeDevices.kt index 3c10705d56..3a59ea6040 100644 --- a/corellium/domain/src/main/kotlin/flank/corellium/domain/run/test/android/step/InvokeDevices.kt +++ b/corellium/domain/src/main/kotlin/flank/corellium/domain/run/test/android/step/InvokeDevices.kt @@ -16,5 +16,9 @@ import kotlinx.coroutines.flow.toList */ internal fun RunTestCorelliumAndroid.Context.invokeDevices() = RunTestCorelliumAndroid.step { println("* Invoking devices") - copy(ids = api.invokeAndroidDevices(AndroidInstance.Config(shards.size)).toList()) + val config = AndroidInstance.Config( + amount = shards.size, + gpuAcceleration = args.gpuAcceleration + ) + copy(ids = api.invokeAndroidDevices(config).toList()) }