From 7409ebec955ac9d4b896e7ff896d54a53725ba91 Mon Sep 17 00:00:00 2001 From: lennoard Date: Tue, 3 Mar 2026 21:41:51 -0300 Subject: [PATCH 1/7] build: upgrade to JDK 21 --- app/build.gradle.kts | 10 ++++++---- common/design/build.gradle.kts | 10 ++++++---- common/utils/build.gradle.kts | 10 ++++++---- data/build.gradle.kts | 14 ++++++++------ domain/build.gradle.kts | 10 ++++++---- gradle/gradle-daemon-jvm.properties | 13 +++++++++++++ settings.gradle.kts | 5 +++++ 7 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 gradle/gradle-daemon-jvm.properties diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 75ed966..894ffda 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -91,12 +91,14 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } +} - kotlinOptions { - jvmTarget = "17" +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) } } diff --git a/common/design/build.gradle.kts b/common/design/build.gradle.kts index 0abefde..de9ae7b 100644 --- a/common/design/build.gradle.kts +++ b/common/design/build.gradle.kts @@ -31,12 +31,14 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } +} - kotlinOptions { - jvmTarget = "17" +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) } } diff --git a/common/utils/build.gradle.kts b/common/utils/build.gradle.kts index 2949dc6..e7dd12c 100644 --- a/common/utils/build.gradle.kts +++ b/common/utils/build.gradle.kts @@ -25,12 +25,14 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } +} - kotlinOptions { - jvmTarget = "17" +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) } } diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 6f5f791..c159b6e 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -30,12 +30,8 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - - kotlinOptions { - jvmTarget = "17" + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } sourceSets { @@ -43,6 +39,12 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) + } +} + dependencies { implementation(project(":common:utils")) implementation(project(":domain")) diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index ff440bc..0332f24 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -15,12 +15,14 @@ android { } compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } +} - kotlinOptions { - jvmTarget = "17" +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) } } diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000..6b90469 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3426ffcaa54c3f62406beb1f1ab8b179/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/f636c800fdb3f9ae33f019dfa048ba72/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1e91f45234d88a64dafb961c93ddc75a/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/552c7bffe0370c66410a51c55985b511/redirect +toolchainVendor=ADOPTIUM +toolchainVersion=21 diff --git a/settings.gradle.kts b/settings.gradle.kts index 8228a9a..0d1ca6d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,6 +13,11 @@ pluginManagement { gradlePluginPortal() } } + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { From 36d52fabc9f669b4ad6463fc81c68eac68e7b0bd Mon Sep 17 00:00:00 2001 From: lennoard Date: Thu, 5 Mar 2026 21:57:42 -0300 Subject: [PATCH 2/7] build: update Gradle, AGP, and Kotlin versions - Upgrade Gradle to 9.3.1 and AGP to 9.1.0. - Upgrade Kotlin to 2.3.10 and KSP to 2.3.6. - Simplify plugin declarations using new aliases for serialization and parcelize. - Remove `androidx.multidex` dependency and migrate `SysctlGuiApp` to `Application`. - Clean up redundant `sourceSets` and unused plugin applications. - Update `gradle.properties` with new Android Gradle plugin flags. --- app/build.gradle.kts | 12 ++---------- .../com/androidvip/sysctlgui/SysctlGuiApp.kt | 4 ++-- build.gradle.kts | 7 ++++--- common/design/build.gradle.kts | 3 +-- common/utils/build.gradle.kts | 1 - data/build.gradle.kts | 7 +------ domain/build.gradle.kts | 1 - gradle.properties | 13 ++++++++++++- gradle/libs.versions.toml | 14 ++++++-------- gradle/wrapper/gradle-wrapper.properties | 12 ++++++------ 10 files changed, 34 insertions(+), 40 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 894ffda..22bc46c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -2,12 +2,11 @@ import java.util.Properties plugins { alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.kotlin.parcelize) alias(libs.plugins.ksp) - alias(libs.plugins.jetbrains.kotlin.serialization) alias(libs.plugins.stability.analyzer) - id("kotlin-parcelize") } android { @@ -70,12 +69,6 @@ android { compose = true } - sourceSets { - maybeCreate("main").java.srcDir("src/main/kotlin") - // Adds exported schema location as test app assets. - getByName("androidTest").assets.srcDir("$projectDir/schemas") - } - packaging { resources.excludes.addAll( arrayOf( @@ -116,7 +109,6 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.glance.appwidget) - implementation(libs.androidx.multidex) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.window) implementation(libs.androidx.work.runtime.ktx) diff --git a/app/src/main/kotlin/com/androidvip/sysctlgui/SysctlGuiApp.kt b/app/src/main/kotlin/com/androidvip/sysctlgui/SysctlGuiApp.kt index 0fb9ede..1b909b8 100644 --- a/app/src/main/kotlin/com/androidvip/sysctlgui/SysctlGuiApp.kt +++ b/app/src/main/kotlin/com/androidvip/sysctlgui/SysctlGuiApp.kt @@ -1,6 +1,6 @@ package com.androidvip.sysctlgui -import androidx.multidex.MultiDexApplication +import android.app.Application import com.androidvip.sysctlgui.data.di.dataModules import com.androidvip.sysctlgui.di.presentationModule import com.androidvip.sysctlgui.domain.di.domainModule @@ -10,7 +10,7 @@ import org.koin.android.ext.android.get import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin -class SysctlGuiApp : MultiDexApplication() { +class SysctlGuiApp : Application() { override fun onCreate() { super.onCreate() diff --git a/build.gradle.kts b/build.gradle.kts index c894863..df2a432 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,11 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false - alias(libs.plugins.kotlin.android) apply false - alias(libs.plugins.kotlin.compose) apply false - alias(libs.plugins.jetbrains.kotlin.jvm) apply false alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.jvm) apply false + alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.kotlin.parcelize) apply false + alias(libs.plugins.kotlin.serialization) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.stability.analyzer) apply false } \ No newline at end of file diff --git a/common/design/build.gradle.kts b/common/design/build.gradle.kts index de9ae7b..e691bb8 100644 --- a/common/design/build.gradle.kts +++ b/common/design/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) } @@ -57,7 +56,7 @@ dependencies { api(libs.material) - androidTestApi(platform(libs.androidx.compose.bom)) + androidTestImplementation(platform(libs.androidx.compose.bom)) if (AppConfig.devCycle) { api(libs.androidx.compose.ui.tooling) diff --git a/common/utils/build.gradle.kts b/common/utils/build.gradle.kts index e7dd12c..ffe03ab 100644 --- a/common/utils/build.gradle.kts +++ b/common/utils/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) } android { diff --git a/data/build.gradle.kts b/data/build.gradle.kts index c159b6e..e344752 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -1,7 +1,6 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.jetbrains.kotlin.serialization) + alias(libs.plugins.kotlin.serialization) alias(libs.plugins.ksp) } @@ -33,10 +32,6 @@ android { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } - - sourceSets { - maybeCreate("main").java.srcDir("src/main/kotlin") - } } kotlin { diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 0332f24..92b8652 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) } android { diff --git a/gradle.properties b/gradle.properties index 9c751af..571581a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,4 +21,15 @@ kotlin.code.style=official # Configuration cache org.gradle.configuration-cache=true android.nonTransitiveRClass=true -android.nonFinalResIds=false +android.disallowKotlinSourceSets=false +#android.nonFinalResIds=false +#android.defaults.buildfeatures.resvalues=true +#android.sdk.defaultTargetSdkToCompileSdkIfUnset=false +#android.enableAppCompileTimeRClass=false +#android.usesSdkInManifest.disallowed=false +#android.uniquePackageNames=false +#android.dependency.useConstraints=true +#android.r8.strictFullModeForKeepRules=false +#android.r8.optimizedResourceShrinking=false +#android.builtInKotlin=false +#android.newDsl=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ca9c573..c055f85 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] activityCompose = "1.11.0" -agp = "8.12.0" +agp = "9.1.0" appcompat = "1.7.1" composeAnimationGraphics = "1.10.0" composeBom = "2025.09.01" @@ -12,16 +12,15 @@ glanceAppwidget = "1.1.1" jsoup = "1.21.2" junit = "4.13.2" koin = "4.1.1" -kotlin = "2.2.0" +kotlin = "2.3.10" kotlinxCoroutinesAndroid = "1.10.2" kotlinxSerializationCore = "1.9.0" -ksp = "2.2.0-2.0.2" +ksp = "2.3.6" ktor = "3.3.0" libsu = "6.0.0" lifecycle = "2.9.4" material = "1.13.0" mockkAndroid = "1.14.5" -multidex = "2.0.1" navigationCompose = "2.9.4" preference = "1.2.1" room = "2.8.0" @@ -47,7 +46,6 @@ androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecyc androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" } androidx-lifecycle-viewmodel-savedstate = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "lifecycle" } -androidx-multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-preference = { module = "androidx.preference:preference", version.ref = "preference" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } @@ -79,9 +77,9 @@ libsu = ["libsu-core", "libsu-nio"] [plugins] android-application = { id = "com.android.application", version.ref = "agp" } android-library = { id = "com.android.library", version.ref = "agp" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } stability-analyzer = { id = "com.github.skydoves.compose.stability.analyzer", version = "0.6.6" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 24dffe3..9a0b039 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Feb 21 17:02:43 BRT 2021 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip +#Sun Feb 21 17:02:43 BRT 2021 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip From e98d307350f8f05bad497b32e14f6b6dce4ae7bf Mon Sep 17 00:00:00 2001 From: BlockXe Date: Thu, 16 Jul 2026 17:41:59 +0800 Subject: [PATCH 3/7] Fix unable to delete startup parameters & Add Simplified Chinese (zh-rCN) translation --- app/build.gradle.kts | 2 +- .../sysctlgui/ui/user/UserParamsViewModel.kt | 6 +- .../main/res/values-zh-rCN/params_info.xml | 58 +++++++++ app/src/main/res/values-zh-rCN/strings.xml | 119 ++++++++++++++++++ app/src/main/res/xml/locales_config.xml | 1 + .../androidvip/sysctlgui/data/db/ParamDao.kt | 3 + .../data/repository/UserRepositoryImpl.kt | 2 +- data/src/main/res/values-zh-rCN/strings.xml | 43 +++++++ .../domain/usecase/RemoveUserParamUseCase.kt | 4 +- 9 files changed, 233 insertions(+), 5 deletions(-) create mode 100644 app/src/main/res/values-zh-rCN/params_info.xml create mode 100644 app/src/main/res/values-zh-rCN/strings.xml create mode 100644 data/src/main/res/values-zh-rCN/strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 22bc46c..b1dd04c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,7 +21,7 @@ android { versionName = "3.1.0" vectorDrawables.useSupportLibrary = true androidResources { - localeFilters += listOf("en", "de", "pt-rBR", "tr") + localeFilters += listOf("en", "de", "pt-rBR", "tr", "zh-rCN") } javaCompileOptions { annotationProcessorOptions { diff --git a/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt b/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt index 90ed643..87f7758 100644 --- a/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt +++ b/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt @@ -1,5 +1,6 @@ package com.androidvip.sysctlgui.ui.user +import android.util.Log import androidx.lifecycle.viewModelScope import com.androidvip.sysctlgui.domain.usecase.GetUserParamsUseCase import com.androidvip.sysctlgui.domain.usecase.RemoveUserParamUseCase @@ -47,11 +48,14 @@ class UserParamsViewModel( private fun removeParam(param: UiKernelParam) { viewModelScope.launch { runCatching { - removeParam.invoke(param) + var count = removeParam.invoke(param) + require(count>0) }.onSuccess { setState { copy(userParams = userParams - param) } setEffect { UserParamsViewEffect.ShowUndoSnackBar(param) } mostRecentlyRemovedParam = param + }.onFailure { + } } } diff --git a/app/src/main/res/values-zh-rCN/params_info.xml b/app/src/main/res/values-zh-rCN/params_info.xml new file mode 100644 index 0000000..400d30d --- /dev/null +++ b/app/src/main/res/values-zh-rCN/params_info.xml @@ -0,0 +1,58 @@ + + + + 系统中为拥有 cap_sys_admin 权限的用户保留的空闲内存量(KB)。 + 当设置为非零值时,启用块 I/O 调试。 + 仅在启用 CONFIG_COMPACTION 时可用。向该文件写入 1 时,系统将对所有内存域(Zone)进行规整(Compaction),以便尽可能将空闲内存合并为连续的内存块。 + 仅在启用 CONFIG_COMPACTION 时可用。设置为 1 时,允许内存在规整时检查不可回收的 LRU 链表(如被 mlock 锁定的页面)以寻找可规整的页面。设置为 0 则阻止规整功能移动不可回收的页面。默认值为 1。 + 触发内核后台刷新线程(flusher threads)开始回写(Writeback)的脏内存量(字节)。 + 触发内核后台刷新线程开始回写脏数据的页面数量占总可用内存(含空闲页面和可回收页面)的百分比。 + 产生磁盘写入的进程自身开始回写脏数据时的脏内存量。注意:dirty_bytes 与 dirty_ratio 互为对应参数,一次只能指定其中一个。 + 定义脏数据在内存中驻留多长时间后,才允许被内核刷新线程写入磁盘(单位:百分之一秒)。当刷新线程下次唤醒时,在内存中变脏时间超过该间隔的数据将被写回磁盘。 + 产生磁盘写入的进程自身开始写回脏数据时,脏页数量占总可用内存(含空闲页面和可回收页面)的百分比。 + 定义脏 Inode(索引节点)在内存中驻留多长时间后,才允许被内核刷新线程回写。当一个 lazytime Inode 的页面不断变脏时,更新了时间戳的 Inode 将永远没有机会被写回。 + 内核刷新线程会定期唤醒并将“旧”数据写回磁盘。此参数表示这些唤醒之间的时间间隔(单位:百分之一秒)。设置为 0 将完全禁用定期回写。 + 向此文件写入 1、2 或 3 将促使内核释放干净的缓存(Clean Caches)以及可回收的 slab 对象(如 dentries 和 inodes)。释放后,其占用的内存将变为可用空闲内存。 + 此参数影响内核在满足高阶(High-order)分配请求时,是进行内存规整还是直接进行内存回收。数值越接近 0,意味着分配更可能因内存不足而失败;数值越接近 1000,意味着分配失败主要是由内存碎片引起的;而 -1 则意味着只要满足水位线(Watermarks),分配就会成功。\n\n默认值:500。 + 控制是否将高端内存(High Memory)计入脏数据写作者的限流(Throttling)考量中。 + 控制“笔记本模式(Laptop Mode)”的开关。笔记本模式用于尽可能减少磁盘启动旋转的次数,以节省电池电量。 + 如果非零,此 sysctl 将禁用新的 32 位 mmap 布局 —— 内核将对所有进程使用传统的(Linux 2.4)布局。 + 一个进程可以拥有的最大内存映射区域(VMA)数量。虽然大多数应用程序需要的映射少于 1000 个,但某些程序(特别是 malloc 调试器)可能会消耗大量映射(例如每次分配需要一到两个映射)。默认值为 65536。 + 用于强制 Linux 虚拟内存(VM)保留的最小空闲内存量(KB)。设置得过高会使系统立即发生 OOM(内存溢出崩溃)。 + 此文件指示限制用户进程进行 mmap 映射的最低地址空间大小。 + 在支持调整地址空间布局随机化(ASLR)的架构上,此值用于选择在使用 mmap 分配 VMA 区域时,用于确定基地址随机偏移量的位数。该值会受到系统架构支持的最小值和最大值的限制。 + 在支持调整地址空间布局随机化(ASLR)的架构上,此值用于选择在兼容模式下运行的应用程序使用 mmap 分配 VMA 区域时,用于确定基地址随机偏移量的位数。该值会受到系统架构支持的最小值和最大值的限制。 + 启用后,当内核执行 OOM 强杀(OOM-killing)时,会输出系统范围的任务信息转储(不包括内核线程)。如果设置为 0,则不显示这些信息。如果设置为非零值,只要 OOM Killer 杀死了占用大量内存的任务,就会显示这些信息。 + 启用或禁用在内存不足(OOM)时直接杀死触发 OOM 的任务。如果设置为非零值,OOM Killer 会直接杀死触发内存不足条件的那个任务,从而避免耗时的任务列表(tasklist)扫描。 + 当 overcommit_memory 设置为 2 时,已提交的虚拟地址空间不允许超过交换空间(Swap)加上此处的物理内存量(KB)。注意:overcommit_kbytes 与 overcommit_ratio 互为对应参数,一次只能指定其中一个。 + 此值包含启用内存超发(Overcommit)的标志。\n\n当此标志为 0 时,当用户空间请求更多内存时,内核会尝试估算剩余的空闲内存量。\n\n当此标志为 1 时,内核会假定始终有足够的内存,直到内存实际耗尽。\n\n当此标志为 2 时,内核采用“绝不超发”策略,尝试阻止任何内存超发行为。 + 当 overcommit_memory 设置为 2 时,已提交的虚拟地址空间不允许超过交换空间(Swap)加上此百分比的物理内存。 + 控制单次尝试中从交换空间(Swap)中连续读取的最大页面数量。这是交换空间对应的页缓存预读(Readahead)机制。它是一个对数数值 —— 设置为 0 意味着 “1 页”,设置为 1 意味着 “2 页”,设置为 2 意味着 “4 页”,依此类推。\n设置为 0 将完全禁用交换预读。 + 启用或禁用在发生内存不足(OOM)时触发系统崩溃(Kernel Panic)的功能。 + 为每个 CPU 的页面列表分配的、每个内存域(Zone)中最大页面比例的分数。此值的最小值是 8。 + 更新虚拟内存统计信息(VM Statistics)的时间间隔。默认值为 1 秒。 + 用于定义内核使用交换分区(Swap)的积极程度。值越高,使用 Swap 的倾向越强;值越低,Swap 的使用越少。默认值:60。 + + + 唯一标识当前系统启动的 ID。 + 当前可用的熵(比特数)。 + 熵池(Entropy Pool)可容纳的最大比特数限制。 + 允许从随机设备接口(如 /dev/random)读取数据前,必须达到的最小可用熵(比特数)。 + 触发写入熵池的 entropy_avail 阈值。当可用熵低于此值时,会唤醒阻塞的设备向熵池中写入熵。 + + + 套接字(Socket)读取的低延迟繁忙轮询(Busy Poll)超时时间。(需要启用 CONFIG_NET_RX_BUSY_POLL)即在设备队列上忙循环等待数据包的近似时间(微秒)。此项设置 SO_BUSY_POLL 套接字选项的默认值。默认值:0(关闭)。 + poll 和 select 的低延迟繁忙轮询(Busy Poll)超时时间。(需要启用 CONFIG_NET_RX_BUSY_POLL)即忙循环等待事件的近似时间(微秒)。推荐值取决于你轮询的套接字数量:对于几个套接字建议设为 50,数百个则设为 100。默认值:0(关闭)。 + 网络设备默认使用的队列规则(Queuing Discipline)。这允许使用其他规则来替代默认的 pfifo_fast。默认值:pfifo_fast。 + 内核在一次 NAPI 中断中可以处理的最大数据包数量,这是一个 Per-CPU(单 CPU 独立)变量。 + message_burst 与 message_cost 用于限制网络代码向内核日志中写入警告信息的频率。它们通过强制实施速率限制来防止遭受拒绝服务(DOS)攻击。message_cost 系数越高,写入的日志信息就越少。message_burst 控制何时丢弃这些信息。默认设置限制警告信息每 5 秒最多输出一次。 + message_burst 与 message_cost 用于限制网络代码向内核日志中写入警告信息的频率。它们通过强制实施速率限制来防止遭受拒绝服务(DOS)攻击。message_cost 系数越高,写入的日志信息就越少。message_burst 控制何时丢弃这些信息。默认设置限制警告信息每 5 秒最多输出一次。 + [已废弃] 此参数曾用于控制由于网络问题(如 IP 地址冲突或错误的校验和)而在控制台打印的来自网络协议栈的警告消息。 + 在单次轮询循环(NAPI poll)中,能从所有接口中获取的最大数据包数量。 + 每个套接字(Socket)允许的最大辅助缓冲区大小。辅助数据是由 cmsghdr 结构体和附加数据组成的序列。 + 套接字(Socket)接收缓冲区的默认大小(单位:字节)。 + 套接字接收缓冲区的最大大小(单位:字节)。 + 套接字发送缓冲区的默认大小(单位:字节)。 + 套接字发送缓冲区的最大大小(单位:字节)。 + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..6022553 --- /dev/null +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,119 @@ + + 检查 root 权限 + 检查 busybox + 错误 + 设置 + 关于 + 退出 + 内核参数 + 浏览变量 + 浏览 /proc/sys 下的内核参数 + 编辑参数 + 完成 + 无法获取 root 权限。你必须拥有 root 权限才能编辑内核参数。 + 没有关于此参数的文档 + 搜索 + 正在应用内核参数设定 + 正在应用你的内核参数设定 + 将在 %d 秒后应用你的内核参数设定… + 自启动通知 + 正在应用你的内核参数设定 + 导入参数设定失败 + 无法导入参数设定:空文件 + 无法导入参数设定:文件中包含格式错误。 + 编辑 + 移除 + 没有已收藏的参数 + Tasker 列表 + 主列表 + 次级列表 + 主 Tasker 列表 + 次级 Tasker 列表 + 收藏列表 + 启动时应用的列表 + SysctlGUI:Tasker 配置文件 #%d 已应用 + 选择一个任务列表 + 选择一个与 Tasker 配合使用的列表 + 一旦由 Tasker 触发,该插件将立即重新应用所选列表中所有参数及其各自的数值。 + 任务设置 + 撤销 + 正在启动 Sysctl GUI + 启动时自动运行 Sysctl GUI + 需要 root 权限 + 以获得 root 权限 + 文件类型错误 + 由于 IO 错误,参数设定导出失败 + 没有可以导出的参数设定 + 导出参数设定 + 导入、导出或备份内核参数设定 + 浏览 + 导出 + 重试 + 应用 + 恢复 + 参数 + + 收藏 + 预设 + 文件创建失败或已取消 + 文件选取失败或已取消 + 打开文件时出现错误 + 处理文件时出现错误 + 已移除参数设定:%s + 返回 + 查阅有关 \"%1$s\" 的文档 + 目录:%1$s + 参数:%1$s + 参数图标 + 导航到目录 + 添加到收藏 + 切换 %1$s + 收藏 + 切换任务参数 + 参数值设置成功 + 复制到剪贴板 + 长按以复制 + 任务列表: %1$s + 要设定的新参数值 + 更多 + 文档 + 参数值 + 应用预设 + 找到 %1$d 个参数 + 导入 + 加载预设… + 成功 + 预设已成功导入 + 从文件导入预设 + 导出预设到文件 + 搜索内核参数 + 没有建议参数 + 没有找到有关 \"%1$s\" 的结果 + 输入内容以搜索内核参数 + 搜索图标 + 清空搜索 + 最近搜索 + 历史条目 + 清除搜索历史 + 导出完成 + 导入参数设定失败 + 当前不允许设置参数为空值 + 当前提交模式下不接受该值 + 命令执行失败 + 建议 + 已收藏内核参数 + 历史记录已清除 + 启动时的参数设定 + Sysctl 用于在运行时修改内核参数。 可用的参数在 /proc/sys 下已列出,这个应用提供一个图形界面以方便编辑它们。 + 许可条款 + 我们需要你同意以下内容以继续。 一经同意,此应用可用做到和不可做到的内容如下:\n\t + ❌ 执行和 sysctl 工具无关的命令\n\t + ❌ 在没有用户发起操作或许可的情况下执行命令\n\t + ❌ 留下永久的内核参数修改\n\t + ✅ 对选定的参数执行 sysctl 命令\n\t + ✅ 撤销所有修改 + + 我同意以上条款 + 继续 + 由于内核的特性,运行时完成的更改可以通过简单的设备重启来恢复。你可以在应用设置中找到恢复选项。 + diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml index 8914394..661724c 100644 --- a/app/src/main/res/xml/locales_config.xml +++ b/app/src/main/res/xml/locales_config.xml @@ -4,4 +4,5 @@ + diff --git a/data/src/main/java/com/androidvip/sysctlgui/data/db/ParamDao.kt b/data/src/main/java/com/androidvip/sysctlgui/data/db/ParamDao.kt index 968166c..ecf7ee8 100644 --- a/data/src/main/java/com/androidvip/sysctlgui/data/db/ParamDao.kt +++ b/data/src/main/java/com/androidvip/sysctlgui/data/db/ParamDao.kt @@ -28,6 +28,9 @@ interface ParamDao { @Delete suspend fun delete(param: KernelParamDTO): Int + @Query("DELETE FROM $PARAMS_TABLE_NAME where name=:name and path=:path") + suspend fun deleteSingle(name:String,path:String): Int + @Query("DELETE FROM $PARAMS_TABLE_NAME") suspend fun clearTable() } diff --git a/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt b/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt index a75d123..eef17a1 100644 --- a/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt +++ b/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt @@ -53,7 +53,7 @@ class UserRepositoryImpl( } override suspend fun removeUserParam(param: KernelParam) = withContext(coroutineContext) { - paramDao.delete(KernelParamDTO.fromKernelParam(param)) + paramDao.deleteSingle(KernelParamDTO.fromKernelParam(param).name,KernelParamDTO.fromKernelParam(param).path) } override suspend fun clearUserParams() = withContext(coroutineContext) { diff --git a/data/src/main/res/values-zh-rCN/strings.xml b/data/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..937ef65 --- /dev/null +++ b/data/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,43 @@ + + + 常规 + 文件夹优先 + 使用内核参数浏览器时,优先列出文件夹 + 推断输入类型 + 尝试根据内核参数的值,为软键盘设置最匹配的输入类型 + 使用在线文档 + 在显示参数描述时,尝试使用在线文档 + 主题 + 强制暗色模式 + 在可用时强制启用暗色主题 + 动态色彩 + 在可用时使用动态色彩(Material You) + 对比度级别 + 主题颜色的对比度级别 + 操作 + 提交模式 (%s) + 应用参数值时使用的命令 + 使用 Busybox + 如果可用,使用 Busybox 来执行相关命令 + 允许空值 + 开机自启 + 开机自动应用 + 允许应用程序在系统启动时自动应用内核参数 + 自启延迟 + 系统启动后,延迟指定时间(秒)再应用参数 + 管理参数 + 管理将在系统启动时自动应用的参数 + 显示 Tasker 气泡提示 + 当应用 Tasker 参数时显示气泡提示(Toast) + 搜索历史 + 点击清空搜索历史记录 + 其他 + 源代码 + 在 GitHub 上查看本项目源码! + 贡献者 + 翻译贡献 + 协助我们翻译此应用程序 + 恢复修改 + 撤销该应用作出的所有修改 + 受限于 Linux 内核的固有特性,运行时所作的修改只需通过简单地重启设备即可完全恢复。应用将不再尝试在下次系统启动后重新应用这些参数。 + \ No newline at end of file diff --git a/domain/src/main/java/com/androidvip/sysctlgui/domain/usecase/RemoveUserParamUseCase.kt b/domain/src/main/java/com/androidvip/sysctlgui/domain/usecase/RemoveUserParamUseCase.kt index f592deb..d536765 100644 --- a/domain/src/main/java/com/androidvip/sysctlgui/domain/usecase/RemoveUserParamUseCase.kt +++ b/domain/src/main/java/com/androidvip/sysctlgui/domain/usecase/RemoveUserParamUseCase.kt @@ -4,7 +4,7 @@ import com.androidvip.sysctlgui.domain.models.KernelParam import com.androidvip.sysctlgui.domain.repository.UserRepository class RemoveUserParamUseCase(private val repository: UserRepository) { - suspend operator fun invoke(param: KernelParam) { - repository.removeUserParam(param) + suspend operator fun invoke(param: KernelParam): Int { + return repository.removeUserParam(param) } } From 62ea2e3c3cc606aa03b102ae120db379947d4d3f Mon Sep 17 00:00:00 2001 From: BlockXe Date: Thu, 16 Jul 2026 20:03:12 +0800 Subject: [PATCH 4/7] update UserRepositoryImplTest --- .../sysctlgui/data/repository/UserRepositoryImplTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/src/test/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImplTest.kt b/data/src/test/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImplTest.kt index 4290dcc..65e1011 100644 --- a/data/src/test/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImplTest.kt +++ b/data/src/test/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImplTest.kt @@ -46,6 +46,6 @@ class UserRepositoryImplTest { // Then val expectedDto = KernelParamDTO.fromKernelParam(paramToRemove) - coVerify { paramDao.delete(expectedDto) } + coVerify { paramDao.deleteSingle(expectedDto.name,expectedDto.path) } } } From 6f62f7d2ee291aaf3ef413ee13df85cc8de6fe06 Mon Sep 17 00:00:00 2001 From: BlockXe Date: Thu, 16 Jul 2026 20:12:35 +0800 Subject: [PATCH 5/7] Resolve other issues --- .../com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt | 2 +- .../androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt b/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt index 87f7758..3c0abe4 100644 --- a/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt +++ b/app/src/main/kotlin/com/androidvip/sysctlgui/ui/user/UserParamsViewModel.kt @@ -48,7 +48,7 @@ class UserParamsViewModel( private fun removeParam(param: UiKernelParam) { viewModelScope.launch { runCatching { - var count = removeParam.invoke(param) + val count = removeParam.invoke(param) require(count>0) }.onSuccess { setState { copy(userParams = userParams - param) } diff --git a/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt b/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt index eef17a1..2c99ded 100644 --- a/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt +++ b/data/src/main/java/com/androidvip/sysctlgui/data/repository/UserRepositoryImpl.kt @@ -53,7 +53,8 @@ class UserRepositoryImpl( } override suspend fun removeUserParam(param: KernelParam) = withContext(coroutineContext) { - paramDao.deleteSingle(KernelParamDTO.fromKernelParam(param).name,KernelParamDTO.fromKernelParam(param).path) + val dto = KernelParamDTO.fromKernelParam(param) + paramDao.deleteSingle(dto.name, dto.path) } override suspend fun clearUserParams() = withContext(coroutineContext) { From 69f23b3833f64443d5ac4fe33282d287061881cc Mon Sep 17 00:00:00 2001 From: Lennoard Date: Sun, 16 Aug 2026 12:57:40 -0300 Subject: [PATCH 6/7] release: [3.2.0] versionCode 24 Signed-off-by: Lennoard --- app/build.gradle.kts | 4 ++-- .../sysctlgui/data/repository/AppSettingsRepositoryImpl.kt | 2 +- fastlane/metadata/android/en-US/changelogs/24.txt | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/24.txt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b1dd04c..4507185 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -17,8 +17,8 @@ android { applicationId = AppConfig.appId minSdk = AppConfig.minSdkVersion targetSdk = AppConfig.targetSdkVersion - versionCode = 23 - versionName = "3.1.0" + versionCode = 24 + versionName = "3.2.0" vectorDrawables.useSupportLibrary = true androidResources { localeFilters += listOf("en", "de", "pt-rBR", "tr", "zh-rCN") diff --git a/data/src/main/java/com/androidvip/sysctlgui/data/repository/AppSettingsRepositoryImpl.kt b/data/src/main/java/com/androidvip/sysctlgui/data/repository/AppSettingsRepositoryImpl.kt index 221e4a2..b72412d 100644 --- a/data/src/main/java/com/androidvip/sysctlgui/data/repository/AppSettingsRepositoryImpl.kt +++ b/data/src/main/java/com/androidvip/sysctlgui/data/repository/AppSettingsRepositoryImpl.kt @@ -197,7 +197,7 @@ class AppSettingsRepositoryImpl( value = "$SOURCE_CODE_URL/graphs/contributors", category = context.getString(R.string.prefs_category_others), title = context.getString(R.string.pref_contributors_title), - description = "free-bots, mikropsoft (Holi)", + description = "free-bots, mikropsoft (Holi), BlockXe", iconResource = R.drawable.ic_group, type = SettingItemType.Text, ), diff --git a/fastlane/metadata/android/en-US/changelogs/24.txt b/fastlane/metadata/android/en-US/changelogs/24.txt new file mode 100644 index 0000000..a268ac4 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/24.txt @@ -0,0 +1,4 @@ +- bugfix: Fixed unable to delete startup parameters (by BlockXe) +- feature: Added Simplified Chinese translations (zh-rCN) (by BlockXe) +- build: Updated Gradle, AGP, and Kotlin versions +- build: Upgraded to JDK 21 From 042d7b0bf2cb3c67f966a8ca1597b070a28048a4 Mon Sep 17 00:00:00 2001 From: Lennoard Date: Sun, 16 Aug 2026 13:24:34 -0300 Subject: [PATCH 7/7] build: add initial CI and release APK workflows --- .github/workflows/ci.yml | 39 ++++++++++++ .github/workflows/release-apk.yml | 98 +++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release-apk.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d371186 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + build: + name: Build (Temurin JDK 21) + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Temurin JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }} + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Build + run: ./gradlew assembleDebug --no-daemon + + - name: Run unit tests + run: ./gradlew test --no-daemon diff --git a/.github/workflows/release-apk.yml b/.github/workflows/release-apk.yml new file mode 100644 index 0000000..a73bac9 --- /dev/null +++ b/.github/workflows/release-apk.yml @@ -0,0 +1,98 @@ +name: Release APK + +on: + push: + tags: + - 'v*' + workflow_dispatch: + +jobs: + build-release: + name: Build and publish unsigned APK + runs-on: ubuntu-24.04 + env: + TAG: ${{ github.ref_name }} + REPO: ${{ github.repository }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Temurin JDK 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + cache: 'gradle' + + - name: Set up Android SDK (platform + build-tools) + uses: r0adkll/setup-android@v2 + with: + api-level: 36 + build-tools: '36.0.0' + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Assemble release APK (unsigned) + run: ./gradlew :app:assembleRelease --no-daemon -x validateSigningRelease + + - name: Find release APK + id: find_apk + run: | + set -e + APK=$(find app/build/outputs -type f -name "*release*.apk" | head -n1) + if [ -z "$APK" ]; then echo "No APK found"; exit 1; fi + echo "apk_path=$APK" >> $GITHUB_OUTPUT + + - name: Copy & rename APK + id: rename + run: | + mkdir -p release + OUT=release/sysctlgui-${{ env.TAG }}.apk + cp "${{ steps.find_apk.outputs.apk_path }}" "$OUT" + echo "out_path=$OUT" >> $GITHUB_OUTPUT + + - name: Prepare release body (changelog + compare link) + id: changelog + run: | + set -e + git fetch --tags + TAG=${{ env.TAG }} + REPO=${{ env.REPO }} + PREV_TAG=$(git tag --sort=-v:refname | sed -n '2p' || true) + CHANGELOG_FILE=$(ls -t fastlane/metadata/android/en-US/changelogs/*.txt 2>/dev/null | head -n1 || true) + CHANGELOG="" + if [ -n "$CHANGELOG_FILE" ]; then CHANGELOG=$(cat "$CHANGELOG_FILE"); fi + if [ -n "$PREV_TAG" ]; then + COMP="[${PREV_TAG}...${TAG}](https://github.com/${REPO}/compare/${PREV_TAG}...${TAG})" + else + COMP="" + fi + BODY="[${TAG}](https://github.com/${REPO}/releases/tag/${TAG}) [Latest](https://github.com/${REPO}/releases/latest)\n\n## What's Changed\n\n${CHANGELOG}\n\n**Full Changelog**: ${COMP}" + echo "release_body<> $GITHUB_OUTPUT + echo "$BODY" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.TAG }} + release_name: ${{ env.TAG }} + body: ${{ steps.changelog.outputs.release_body }} + draft: false + prerelease: false + + - name: Upload APK to Release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ${{ steps.rename.outputs.out_path }} + asset_name: sysctlgui-${{ env.TAG }}.apk + asset_content_type: application/vnd.android.package-archive