diff --git a/.idea/dictionaries/user.xml b/.idea/dictionaries/user.xml
index 2317866a..db7d2d2c 100644
--- a/.idea/dictionaries/user.xml
+++ b/.idea/dictionaries/user.xml
@@ -7,6 +7,7 @@
ejectable
hasher
hkdf
+ launchable
restorable
seedvault
snowden
diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt
index c36e00c1..3f39150c 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/Metadata.kt
@@ -26,7 +26,7 @@ data class BackupMetadata(
internal var d2dBackup: Boolean = false,
internal val packageMetadataMap: PackageMetadataMap = PackageMetadataMap(),
) {
- val size: Long?
+ val size: Long
get() = packageMetadataMap.values.sumOf { m ->
(m.size ?: 0L) + (m.splits?.sumOf { it.size ?: 0L } ?: 0L)
}
@@ -85,7 +85,9 @@ data class PackageMetadata(
internal var state: PackageState = UNKNOWN_ERROR,
internal var backupType: BackupType? = null,
internal var size: Long? = null,
+ internal var name: CharSequence? = null,
internal val system: Boolean = false,
+ internal val isLaunchableSystemApp: Boolean = false,
internal val version: Long? = null,
internal val installer: String? = null,
internal val splits: List? = null,
@@ -110,7 +112,9 @@ internal const val JSON_PACKAGE_TIME = "time"
internal const val JSON_PACKAGE_BACKUP_TYPE = "backupType"
internal const val JSON_PACKAGE_STATE = "state"
internal const val JSON_PACKAGE_SIZE = "size"
+internal const val JSON_PACKAGE_APP_NAME = "name"
internal const val JSON_PACKAGE_SYSTEM = "system"
+internal const val JSON_PACKAGE_SYSTEM_LAUNCHER = "systemLauncher"
internal const val JSON_PACKAGE_VERSION = "version"
internal const val JSON_PACKAGE_INSTALLER = "installer"
internal const val JSON_PACKAGE_SPLITS = "splits"
diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt
index daf7ebb0..c9c6dc39 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataManager.kt
@@ -23,6 +23,7 @@ import com.stevesoltys.seedvault.encodeBase64
import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.metadata.PackageState.APK_AND_DATA
import com.stevesoltys.seedvault.settings.SettingsManager
+import com.stevesoltys.seedvault.transport.backup.PackageService
import com.stevesoltys.seedvault.transport.backup.isSystemApp
import java.io.FileNotFoundException
import java.io.IOException
@@ -41,6 +42,7 @@ internal class MetadataManager(
private val crypto: Crypto,
private val metadataWriter: MetadataWriter,
private val metadataReader: MetadataReader,
+ private val packageService: PackageService,
private val settingsManager: SettingsManager,
) {
@@ -63,7 +65,11 @@ internal class MetadataManager(
return field
}
- val backupSize: Long? get() = metadata.size
+ val backupSize: Long get() = metadata.size
+
+ private val launchableSystemApps by lazy {
+ packageService.launchableSystemApps.map { it.activityInfo.packageName }.toSet()
+ }
/**
* Call this when initializing a new device.
@@ -111,8 +117,11 @@ internal class MetadataManager(
val oldPackageMetadata = metadata.packageMetadataMap[packageName]
?: PackageMetadata()
modifyCachedMetadata {
+ val isSystemApp = packageInfo.isSystemApp()
metadata.packageMetadataMap[packageName] = oldPackageMetadata.copy(
- system = packageInfo.isSystemApp(),
+ name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
+ system = isSystemApp,
+ isLaunchableSystemApp = isSystemApp && launchableSystemApps.contains(packageName),
version = packageMetadata.version,
installer = packageMetadata.installer,
splits = packageMetadata.splits,
@@ -144,12 +153,16 @@ internal class MetadataManager(
metadata.time = now
metadata.d2dBackup = settingsManager.d2dBackupsEnabled()
metadata.packageMetadataMap.getOrPut(packageName) {
+ val isSystemApp = packageInfo.isSystemApp()
PackageMetadata(
time = now,
state = APK_AND_DATA,
backupType = type,
size = size,
- system = packageInfo.isSystemApp(),
+ name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
+ system = isSystemApp,
+ isLaunchableSystemApp = isSystemApp &&
+ launchableSystemApps.contains(packageName),
)
}.apply {
time = now
@@ -157,6 +170,10 @@ internal class MetadataManager(
backupType = type
// don't override a previous K/V size, if there were no K/V changes
if (size != null) this.size = size
+ // update name, if none was set, yet (can happen while migrating to storing names)
+ if (this.name == null) {
+ this.name = packageInfo.applicationInfo?.loadLabel(context.packageManager)
+ }
}
}
}
@@ -178,11 +195,15 @@ internal class MetadataManager(
check(packageState != APK_AND_DATA) { "Backup Error with non-error package state." }
modifyMetadata(metadataOutputStream) {
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
+ val isSystemApp = packageInfo.isSystemApp()
PackageMetadata(
time = 0L,
state = packageState,
backupType = backupType,
- system = packageInfo.isSystemApp()
+ name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
+ system = isSystemApp,
+ isLaunchableSystemApp = isSystemApp &&
+ launchableSystemApps.contains(packageInfo.packageName),
)
}.state = packageState
}
@@ -201,12 +222,22 @@ internal class MetadataManager(
packageState: PackageState,
) = modifyCachedMetadata {
metadata.packageMetadataMap.getOrPut(packageInfo.packageName) {
+ val isSystemApp = packageInfo.isSystemApp()
PackageMetadata(
time = 0L,
state = packageState,
- system = packageInfo.isSystemApp(),
+ name = packageInfo.applicationInfo?.loadLabel(context.packageManager),
+ system = isSystemApp,
+ isLaunchableSystemApp = isSystemApp &&
+ launchableSystemApps.contains(packageInfo.packageName),
)
- }.state = packageState
+ }.apply {
+ state = packageState
+ // update name, if none was set, yet (can happen while migrating to storing names)
+ if (this.name == null) {
+ this.name = packageInfo.applicationInfo?.loadLabel(context.packageManager)
+ }
+ }
}
/**
diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt
index b0a10173..b5eaee76 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataModule.kt
@@ -9,7 +9,7 @@ import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val metadataModule = module {
- single { MetadataManager(androidContext(), get(), get(), get(), get(), get()) }
+ single { MetadataManager(androidContext(), get(), get(), get(), get(), get(), get()) }
single { MetadataWriterImpl(get()) }
single { MetadataReaderImpl(get()) }
}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt
index 8f77bcc4..98ffa9cb 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataReader.kt
@@ -126,6 +126,7 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
else -> null
}
val pSize = p.optLong(JSON_PACKAGE_SIZE, -1L)
+ val pName = p.optString(JSON_PACKAGE_APP_NAME)
val pSystem = p.optBoolean(JSON_PACKAGE_SYSTEM, false)
val pVersion = p.optLong(JSON_PACKAGE_VERSION, 0L)
val pInstaller = p.optString(JSON_PACKAGE_INSTALLER)
@@ -143,7 +144,9 @@ internal class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader {
state = pState,
backupType = pBackupType,
size = if (pSize < 0L) null else pSize,
+ name = if (pName == "") null else pName,
system = pSystem,
+ isLaunchableSystemApp = p.optBoolean(JSON_PACKAGE_SYSTEM_LAUNCHER, false),
version = if (pVersion == 0L) null else pVersion,
installer = if (pInstaller == "") null else pInstaller,
splits = getSplits(p),
diff --git a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt
index dcfdbe7c..49e3c348 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/metadata/MetadataWriter.kt
@@ -57,8 +57,14 @@ internal class MetadataWriterImpl(private val crypto: Crypto) : MetadataWriter {
if (packageMetadata.size != null) {
put(JSON_PACKAGE_SIZE, packageMetadata.size)
}
+ if (packageMetadata.name != null) {
+ put(JSON_PACKAGE_APP_NAME, packageMetadata.name)
+ }
if (packageMetadata.system) {
- put(JSON_PACKAGE_SYSTEM, packageMetadata.system)
+ put(JSON_PACKAGE_SYSTEM, true)
+ }
+ if (packageMetadata.isLaunchableSystemApp) {
+ put(JSON_PACKAGE_SYSTEM_LAUNCHER, true)
}
packageMetadata.version?.let { put(JSON_PACKAGE_VERSION, it) }
packageMetadata.installer?.let { put(JSON_PACKAGE_INSTALLER, it) }
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt
index b03a27d0..bb268c79 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt
@@ -7,11 +7,7 @@ package com.stevesoltys.seedvault.settings
import android.annotation.StringRes
import android.content.Context
-import android.content.Intent
-import android.content.Intent.ACTION_MAIN
-import android.content.Intent.CATEGORY_LAUNCHER
import android.content.pm.PackageManager
-import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.annotation.WorkerThread
@@ -84,10 +80,6 @@ internal class AppListRetriever(
Pair(PACKAGE_NAME_CALL_LOG, R.string.backup_call_log),
Pair(PACKAGE_NAME_CONTACTS, R.string.backup_contacts)
)
- // filter intent for apps with a launcher activity
- val i = Intent(ACTION_MAIN).apply {
- addCategory(CATEGORY_LAUNCHER)
- }
return specialPackages.map { (packageName, stringId) ->
val metadata = metadataManager.getPackageMetadata(packageName)
val status = if (packageName == PACKAGE_NAME_CONTACTS && metadata?.state == null) {
@@ -105,7 +97,7 @@ internal class AppListRetriever(
status = status,
isSpecial = true
)
- } + context.packageManager.queryIntentActivities(i, MATCH_SYSTEM_ONLY).map {
+ } + packageService.launchableSystemApps.map {
val packageName = it.activityInfo.packageName
val metadata = metadataManager.getPackageMetadata(packageName)
AppStatus(
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/PackageService.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/PackageService.kt
index 39109bf2..c1fb8618 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/PackageService.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/PackageService.kt
@@ -7,6 +7,9 @@ package com.stevesoltys.seedvault.transport.backup
import android.app.backup.IBackupManager
import android.content.Context
+import android.content.Intent
+import android.content.Intent.ACTION_MAIN
+import android.content.Intent.CATEGORY_LAUNCHER
import android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
import android.content.pm.ApplicationInfo.FLAG_STOPPED
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
@@ -16,6 +19,8 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.GET_INSTRUMENTATION
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
+import android.content.pm.PackageManager.MATCH_SYSTEM_ONLY
+import android.content.pm.ResolveInfo
import android.os.RemoteException
import android.os.UserHandle
import android.util.Log
@@ -147,6 +152,16 @@ internal class PackageService(
}
}
+ val launchableSystemApps: List
+ @WorkerThread
+ get() {
+ // filter intent for apps with a launcher activity
+ val i = Intent(ACTION_MAIN).apply {
+ addCategory(CATEGORY_LAUNCHER)
+ }
+ return packageManager.queryIntentActivities(i, MATCH_SYSTEM_ONLY)
+ }
+
fun getVersionName(packageName: String): String? = try {
packageManager.getPackageInfo(packageName, 0).versionName
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/app/src/test/java/com/stevesoltys/seedvault/TestApp.kt b/app/src/test/java/com/stevesoltys/seedvault/TestApp.kt
index 160dee86..78e9333a 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/TestApp.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/TestApp.kt
@@ -16,8 +16,10 @@ import com.stevesoltys.seedvault.metadata.metadataModule
import com.stevesoltys.seedvault.plugins.saf.storagePluginModuleSaf
import com.stevesoltys.seedvault.restore.install.installModule
import com.stevesoltys.seedvault.settings.SettingsManager
+import com.stevesoltys.seedvault.transport.backup.PackageService
import com.stevesoltys.seedvault.transport.backup.backupModule
import com.stevesoltys.seedvault.transport.restore.restoreModule
+import io.mockk.mockk
import org.koin.android.ext.koin.androidContext
import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
@@ -33,9 +35,11 @@ class TestApp : App() {
single { KeyManagerTestImpl() }
single { CryptoImpl(get(), get(), get()) }
}
+ private val packageService: PackageService = mockk()
private val appModule = module {
single { Clock() }
single { SettingsManager(this@TestApp) }
+ single { packageService }
}
override fun startKoin(): KoinApplication {
diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt
index 0b1ff816..f3d0ee49 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt
@@ -7,11 +7,13 @@ package com.stevesoltys.seedvault.metadata
import android.content.Context
import android.content.Context.MODE_PRIVATE
+import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.stevesoltys.seedvault.Clock
@@ -27,6 +29,7 @@ import com.stevesoltys.seedvault.metadata.PackageState.QUOTA_EXCEEDED
import com.stevesoltys.seedvault.metadata.PackageState.UNKNOWN_ERROR
import com.stevesoltys.seedvault.metadata.PackageState.WAS_STOPPED
import com.stevesoltys.seedvault.settings.SettingsManager
+import com.stevesoltys.seedvault.transport.backup.PackageService
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
@@ -64,6 +67,7 @@ class MetadataManagerTest {
private val crypto: Crypto = mockk()
private val metadataWriter: MetadataWriter = mockk()
private val metadataReader: MetadataReader = mockk()
+ private val packageService: PackageService = mockk()
private val settingsManager: SettingsManager = mockk()
private val manager = MetadataManager(
@@ -72,9 +76,12 @@ class MetadataManagerTest {
crypto = crypto,
metadataWriter = metadataWriter,
metadataReader = metadataReader,
- settingsManager = settingsManager
+ packageService = packageService,
+ settingsManager = settingsManager,
)
+ private val packageManager: PackageManager = mockk()
+
private val time = 42L
private val token = Random.nextLong()
private val packageName = getRandomString()
@@ -162,6 +169,7 @@ class MetadataManagerTest {
signatures = listOf("sig")
)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
expectModifyMetadata(initialMetadata)
@@ -185,12 +193,23 @@ class MetadataManagerTest {
signatures = listOf("sig")
)
+ every { context.packageManager } returns packageManager
+ every { packageService.launchableSystemApps } returns listOf(
+ ResolveInfo().apply {
+ activityInfo = ActivityInfo().apply {
+ packageName = this@MetadataManagerTest.packageName
+ }
+ }
+ )
expectReadFromCache()
expectModifyMetadata(initialMetadata)
manager.onApkBackedUp(packageInfo, packageMetadata)
- assertEquals(packageMetadata.copy(system = true), manager.getPackageMetadata(packageName))
+ assertEquals(
+ packageMetadata.copy(system = true, isLaunchableSystemApp = true),
+ manager.getPackageMetadata(packageName),
+ )
verify {
cacheInputStream.close()
@@ -214,6 +233,7 @@ class MetadataManagerTest {
signatures = listOf("sig foo")
)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
expectWriteToCache(initialMetadata)
@@ -236,6 +256,7 @@ class MetadataManagerTest {
signatures = listOf("sig")
)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
expectWriteToCache(initialMetadata)
val oldState = UNKNOWN_ERROR
@@ -295,6 +316,7 @@ class MetadataManagerTest {
signatures = listOf("sig")
)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
assertNull(manager.getPackageMetadata(packageName))
@@ -330,6 +352,8 @@ class MetadataManagerTest {
val packageMetadata = PackageMetadata(time)
updatedMetadata.packageMetadataMap[packageName] = packageMetadata
+ every { context.packageManager } returns packageManager
+ every { packageService.launchableSystemApps } returns emptyList()
expectReadFromCache()
every { clock.time() } returns time
expectModifyMetadata(initialMetadata)
@@ -342,6 +366,7 @@ class MetadataManagerTest {
backupType = BackupType.FULL,
size = size,
system = true,
+ isLaunchableSystemApp = false,
),
manager.getPackageMetadata(packageName)
)
@@ -361,6 +386,7 @@ class MetadataManagerTest {
expectModifyMetadata(initialMetadata)
every { settingsManager.d2dBackupsEnabled() } returns true
+ every { context.packageManager } returns packageManager
manager.onPackageBackedUp(packageInfo, BackupType.FULL, 0L, storageOutputStream)
assertTrue(initialMetadata.d2dBackup)
@@ -382,6 +408,7 @@ class MetadataManagerTest {
updatedMetadata.packageMetadataMap[packageName] =
PackageMetadata(updateTime, APK_AND_DATA, BackupType.KV, size)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
every { clock.time() } returns updateTime
every { metadataWriter.write(updatedMetadata, storageOutputStream) } throws IOException()
@@ -414,6 +441,7 @@ class MetadataManagerTest {
PackageMetadata(time, state = APK_AND_DATA)
expectReadFromCache()
+ every { context.packageManager } returns packageManager
every { clock.time() } returns time
expectModifyMetadata(updatedMetadata)
@@ -437,6 +465,7 @@ class MetadataManagerTest {
val updatedMetadata = initialMetadata.copy()
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = NOT_ALLOWED)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
expectWriteToCache(updatedMetadata)
@@ -454,6 +483,7 @@ class MetadataManagerTest {
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = WAS_STOPPED)
initialMetadata.packageMetadataMap.remove(packageName)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
expectWriteToCache(updatedMetadata)
@@ -482,6 +512,7 @@ class MetadataManagerTest {
updatedMetadata.packageMetadataMap[packageName] = PackageMetadata(state = WAS_STOPPED)
initialMetadata.packageMetadataMap.remove(packageName)
+ every { context.packageManager } returns packageManager
expectReadFromCache()
expectModifyMetadata(updatedMetadata)
diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt
index 24aad1cb..b1b1cb99 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataWriterDecoderTest.kt
@@ -63,6 +63,10 @@ internal class MetadataWriterDecoderTest {
time = Random.nextLong(),
state = APK_AND_DATA,
backupType = BackupType.FULL,
+ size = Random.nextLong(0, Long.MAX_VALUE),
+ name = getRandomString(),
+ system = Random.nextBoolean(),
+ isLaunchableSystemApp = Random.nextBoolean(),
version = Random.nextLong(),
installer = getRandomString(),
splits = listOf(
@@ -94,6 +98,7 @@ internal class MetadataWriterDecoderTest {
time = Random.nextLong(),
state = QUOTA_EXCEEDED,
backupType = BackupType.FULL,
+ name = null,
size = Random.nextLong(0..Long.MAX_VALUE),
system = Random.nextBoolean(),
version = Random.nextLong(),
@@ -108,6 +113,7 @@ internal class MetadataWriterDecoderTest {
state = NO_DATA,
backupType = BackupType.KV,
size = null,
+ name = getRandomString(),
system = Random.nextBoolean(),
version = Random.nextLong(),
installer = getRandomString(),
@@ -121,6 +127,7 @@ internal class MetadataWriterDecoderTest {
state = NOT_ALLOWED,
size = 0,
system = Random.nextBoolean(),
+ isLaunchableSystemApp = Random.nextBoolean(),
version = Random.nextLong(),
installer = getRandomString(),
sha256 = getRandomString(),
@@ -138,10 +145,11 @@ internal class MetadataWriterDecoderTest {
private fun getMetadata(
packageMetadata: HashMap = HashMap(),
): BackupMetadata {
+ val version = Random.nextBytes(1)[0]
return BackupMetadata(
- version = Random.nextBytes(1)[0],
+ version = version,
token = Random.nextLong(),
- salt = getRandomBase64(32),
+ salt = if (version != 0.toByte()) getRandomBase64(32) else "",
time = Random.nextLong(),
androidVersion = Random.nextInt(),
androidIncremental = getRandomString(),