Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.baidaidai.rootless_store.application.environment

import com.baidaidai.rootless_store.core.util.formatAsMultilineString
import com.baidaidai.rootless_store.data.fileSystem.gateway.AndroidFileSystemDefaultOperatorGatewayImpl
import com.baidaidai.rootless_store.data.environment.gateway.EnvironmentGatewayImpl
import com.baidaidai.rootless_store.data.environment.repository.EnvironmentRepositoryImpl
import com.baidaidai.rootless_store.data.environment.repository.EnvironmentStatusRepositoryImpl
Expand All @@ -14,10 +15,20 @@ import javax.inject.Inject
import java.io.File

class InstallEnvironmentUseCase @Inject constructor(
private val androidFileSystemDefaultOperatorGatewayImpl: AndroidFileSystemDefaultOperatorGatewayImpl,
private val environmentGatewayImpl: EnvironmentGatewayImpl,
private val environmentRepositoryImpl: EnvironmentRepositoryImpl,
private val environmentStatusRepositoryImpl: EnvironmentStatusRepositoryImpl
) {

suspend operator fun invoke(): Result<Unit, PluginError> {
val packageDirectory = androidFileSystemDefaultOperatorGatewayImpl
.getExternalAppPackageCacheDirectoryFile()
val localPackageFile = File(packageDirectory, "package.zip")

return invoke(localPackageFile)
}

suspend operator fun invoke(packageFile: File): Result<Unit, PluginError> {
val environmentManifest: EnvironmentManifest = environmentGatewayImpl
.parseEnvironmentManifest(packageFile)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.baidaidai.rootless_store.application.plugin

import com.baidaidai.rootless_store.core.util.formatAsMultilineString
import com.baidaidai.rootless_store.data.fileSystem.gateway.AndroidFileSystemDefaultOperatorGatewayImpl
import com.baidaidai.rootless_store.data.plugin.gateway.PluginGatewayImpl
import com.baidaidai.rootless_store.data.plugin.repository.PluginRepositoryImpl
import com.baidaidai.rootless_store.data.plugin.repository.PluginStatusRepositoryImpl
Expand All @@ -15,11 +16,21 @@ import javax.inject.Inject
import java.io.File

class InstallPluginUseCase @Inject constructor(
private val androidFileSystemDefaultOperatorGatewayImpl: AndroidFileSystemDefaultOperatorGatewayImpl,
private val pluginRepositoryImpl: PluginRepositoryImpl,
private val pluginStatusRepositoryImpl: PluginStatusRepositoryImpl,
private val pluginGatewayImpl: PluginGatewayImpl,
private val installShellPluginUseCase: InstallShellPluginUseCase
) {

suspend operator fun invoke(): Result<Unit, PluginError> {
val packageDirectory = androidFileSystemDefaultOperatorGatewayImpl
.getExternalAppPackageCacheDirectoryFile()
val localPackageFile = File(packageDirectory, "package.zip")

return invoke(localPackageFile)
}

suspend operator fun invoke(packageFile: File): Result<Unit, PluginError> {

val pluginManifest: PluginManifest = pluginGatewayImpl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.util.Log
import com.baidaidai.rootless_store.data.R
import com.baidaidai.rootless_store.data.fileSystem.gateway.AndroidFileSystemReadOperatorGatewayImpl
import com.baidaidai.rootless_store.data.signature.repository.CaCertificateRepositoryImpl
import com.baidaidai.rootless_store.domain.install.model.LocalPackageType
import com.baidaidai.rootless_store.domain.install.model.PackageSignatureInfo
import com.baidaidai.rootless_store.domain.install.model.PackageSignatureStatus
import dagger.hilt.android.qualifiers.ApplicationContext
Expand Down Expand Up @@ -84,11 +85,8 @@ class PackageSignatureGatewayImpl @Inject constructor(
*
* @since 2026.10.26
* @version 0.1.0
*
*
*
*/
private suspend fun hasTrustedContentSignature(
internal suspend fun hasTrustedContentSignature(
packageBytes: ByteArray,
signatureBytes: ByteArray
): Boolean {
Expand Down Expand Up @@ -170,8 +168,6 @@ class PackageSignatureGatewayImpl @Inject constructor(
* 那就直接跳出去吧
*
* 这一步只浅筛,是否有有效签名者,不进行内容计算
*
* Holder只是指针引用,保存了 Cert 的原始记录
*/
val signerCertificateHolder = cmsCertificateList
.firstOrNull { certificateHolder ->
Expand All @@ -185,7 +181,6 @@ class PackageSignatureGatewayImpl @Inject constructor(
signerCertificateHolder
)

// 签名者必须是有效期内的非 CA 证书,并允许数字签名用途。
signerCertificate.checkValidity()

// 验证 CMS 内容签名
Expand Down Expand Up @@ -239,7 +234,7 @@ class PackageSignatureGatewayImpl @Inject constructor(
* @since 2026.10.26
* @version 0.1.0
*/
private suspend fun loadTrustedCaCertificates(): Set<X509Certificate> {
internal suspend fun loadTrustedCaCertificates(): Set<X509Certificate> {
Log.d(logTag, "Loading built-in and enabled custom CA certificates")

val trustedCaCertificates = mutableSetOf<X509Certificate>()
Expand Down Expand Up @@ -290,8 +285,19 @@ class PackageSignatureGatewayImpl @Inject constructor(

}

// 将 CMS 携带的中间证书链构建到任一已启用的 CA 信任锚。
private fun hasTrustedCertificatePath(
/**
* 检查签名者证书能否构建到受信任的 CA 锚点。
*
* 使用 Bouncy Castle 的 PKIX CertPathBuilder,以签名者证书作为目标,
* 尝试从提供的信任锚集合中构建证书路径;此处不执行证书吊销检查。
*
* @param signerCertificate CMS 签名者证书
* @param trustedCaCertificates 可作为信任锚的 CA 证书集合
* @return 成功构建证书路径时为 true,否则为 false
* @since 2026.10.26
* @version 0.1.0
*/
internal fun hasTrustedCertificatePath(
signerCertificate: X509Certificate,
trustedCaCertificates: Set<X509Certificate>
): Boolean {
Expand Down Expand Up @@ -338,6 +344,7 @@ class PackageSignatureGatewayImpl @Inject constructor(

if (pluginManifest != null) {
return PackageSignatureInfo(
packageType = LocalPackageType.Plugin,
displayName = pluginManifest.pluginRenderingName,
author = pluginManifest.author,
validSigner = signatureStatus == PackageSignatureStatus.Trusted,
Expand All @@ -354,6 +361,7 @@ class PackageSignatureGatewayImpl @Inject constructor(
?: error("Signed package did not contain a supported Plugin or Environment manifest")

return PackageSignatureInfo(
packageType = LocalPackageType.Environment,
displayName = environmentManifest.environmentRenderingName,
author = environmentManifest.author,
validSigner = signatureStatus == PackageSignatureStatus.Trusted,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.baidaidai.rootless_store.domain.install.model

data class PackageSignatureInfo(
val packageType: LocalPackageType,
val displayName: String,
val author: String,
val validSigner: Boolean = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import com.baidaidai.rootless_store.domain.install.model.LocalPackageType
import com.baidaidai.rootless_store.domain.install.model.PackageSignatureInfo
import com.baidaidai.rootless_store.ui.R
import com.baidaidai.rootless_store.ui.components.common.VerticalSpacer
import com.baidaidai.rootless_store.ui.theme.ColorFactory
import com.baidaidai.rootless_store.ui.theme.SuccessColorSet
import com.baidaidai.rootless_store.ui.theme.WarningColorSet
Expand Down Expand Up @@ -197,9 +199,7 @@ fun PluginBottomSheetContent(
)
}

androidx.compose.foundation.layout.Spacer(
modifier = Modifier.height(12.dp)
)
VerticalSpacer(20.dp)

Text(
text = packageDescription,
Expand All @@ -209,9 +209,7 @@ fun PluginBottomSheetContent(
fontWeight = FontWeight(440)
)

androidx.compose.foundation.layout.Spacer(
modifier = Modifier.height(20.dp)
)
VerticalSpacer(20.dp)

Row(
horizontalArrangement = Arrangement.spacedBy(12.dp),
Expand Down Expand Up @@ -263,6 +261,7 @@ fun PluginBottomSheetContent(
private fun _preview_() {
PluginBottomSheetContent(
packageSignatureInfo = PackageSignatureInfo(
packageType = LocalPackageType.Plugin,
displayName = "Demo Plugin",
author = "Rootless Store",
validSigner = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ import com.baidaidai.rootless_store.application.environment.ObserveEnvironmentsU
import com.baidaidai.rootless_store.application.environment.ObserveEnvironmentStatusUseCase
import com.baidaidai.rootless_store.application.environment.DisableEnvironmentUseCase
import com.baidaidai.rootless_store.application.environment.EnableEnvironmentUseCase
import com.baidaidai.rootless_store.application.environment.InstallEnvironmentUseCase
import com.baidaidai.rootless_store.application.plugin.DisablePluginUseCase
import com.baidaidai.rootless_store.application.plugin.EnablePluginUseCase
import com.baidaidai.rootless_store.application.environment.UninstallEnvironmentUseCase
import com.baidaidai.rootless_store.application.plugin.InstallPluginUseCase
import com.baidaidai.rootless_store.application.plugin.ResolvePluginShareUriUseCase
import com.baidaidai.rootless_store.application.plugin.ResolvePluginWebUiUriUseCase
import com.baidaidai.rootless_store.domain.install.model.LocalPackageType
import com.baidaidai.rootless_store.domain.plugin.manifest.PluginManifest
import com.baidaidai.rootless_store.domain.plugin.model.PluginStatus
import com.baidaidai.rootless_store.ui.uistate.RootlessStorePluginScreenUiState
Expand All @@ -49,6 +52,8 @@ class RootlessStorePluginScreenViewModel @Inject constructor(
private val observeEnvironmentStatusUseCase: ObserveEnvironmentStatusUseCase,
private val observePluginStatusUseCase: ObservePluginStatusUseCase,
private val installLocalPackageUseCase: InstallLocalPackageUseCase,
private val installPluginUseCase: InstallPluginUseCase,
private val installEnvironmentUseCase: InstallEnvironmentUseCase,
private val enablePluginUseCase: EnablePluginUseCase,
private val disablePluginUseCase: DisablePluginUseCase,
private val enableEnvironmentUseCase: EnableEnvironmentUseCase,
Expand Down Expand Up @@ -132,9 +137,17 @@ class RootlessStorePluginScreenViewModel @Inject constructor(
}

fun confirmLocalPackageInstall() {
val packageSignatureInfo = _pluginScreenUiState.value.packageSignatureInfo
clearLocalPackageInstallConfirmation()
installLocalPackage()

viewModelScope.launch {
when (packageSignatureInfo!!.packageType) {
LocalPackageType.Plugin -> installPluginUseCase()
else -> installEnvironmentUseCase()
}
}
}

fun dismissLocalPackageInstallConfirmation() {
clearLocalPackageInstallConfirmation()
viewModelScope.launch {
Expand Down
Loading