Skip to content

Commit

Permalink
Fix ShellOps not being kept active by tools that use it
Browse files Browse the repository at this point in the history
`UserManager2.allUsers()`  uses `ShellOps` internally to get current user info from the shell.
Tools (like AppCleaner) and core classes like DataAreaFactory and PkgOps should keep ShellOps alive if they use it.

This prevents shell sessions from being repeatedly started meaning faster execution.
  • Loading branch information
d4rken committed Jan 30, 2025
1 parent 86507b5 commit 715acc3
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import eu.darken.sdmse.common.sharedresource.HasSharedResource
import eu.darken.sdmse.common.sharedresource.SharedResource
import eu.darken.sdmse.common.sharedresource.keepResourcesAlive
import eu.darken.sdmse.common.user.UserHandle2
import eu.darken.sdmse.common.user.UserManager2
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.plus
import java.util.UUID
Expand All @@ -67,7 +66,6 @@ class PkgOps @Inject constructor(
dispatcherProvider: DispatcherProvider,
@ApplicationContext private val context: Context,
private val ipcFunnel: IPCFunnel,
private val userManager: UserManager2,
private val rootManager: RootManager,
private val adbManager: AdbManager,
private val usageStatsManager: UsageStatsManager,
Expand Down Expand Up @@ -161,7 +159,6 @@ class PkgOps @Inject constructor(
@Suppress("NewApi")
packageManager.getInstalledPackages(PackageInfoFlags.of(flags))
} else {
@Suppress("DEPRECATION")
packageManager.getInstalledPackages(flags.toInt())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import eu.darken.sdmse.common.flow.replayingShare
import eu.darken.sdmse.common.pkgs.features.Installed
import eu.darken.sdmse.common.pkgs.pkgops.PkgOps
import eu.darken.sdmse.common.pkgs.sources.NormalPkgsSource
import eu.darken.sdmse.common.sharedresource.closeAll
import eu.darken.sdmse.common.shell.ShellOps
import eu.darken.sdmse.common.user.UserHandle2
import eu.darken.sdmse.common.user.UserManager2
import kotlinx.coroutines.CoroutineScope
Expand All @@ -39,6 +41,7 @@ class PkgRepo @Inject constructor(
private val pkgSources: Set<@JvmSuppressWildcards PkgDataSource>,
private val gatewaySwitch: GatewaySwitch,
private val pkgOps: PkgOps,
private val shellOps: ShellOps,
private val userManager: UserManager2,
) {

Expand Down Expand Up @@ -97,57 +100,58 @@ class PkgRepo @Inject constructor(
private suspend fun gatherPkgData(): Map<CacheKey, CachedInfo> {
log(TAG, INFO) { "generatePkgcache()..." }
val start = System.currentTimeMillis()
return gatewaySwitch.useRes {
pkgOps.useRes {
val sourceMap: Map<KClass<out PkgDataSource>, Collection<Installed>> = coroutineScope {
pkgSources.map { source ->
async {
log(TAG) { "generatePkgcache(): $source start..." }
val sourceStart = System.currentTimeMillis()
val fromSource = source.getPkgs()
val sourceStop = System.currentTimeMillis()
log(TAG) {
"generatePkgcache(): ${fromSource.size} pkgs from $source took ${sourceStop - sourceStart}ms"
}
source::class to fromSource
}
val leases = setOf(pkgOps, gatewaySwitch, shellOps).map { it.sharedResource.get() }

val sourceMap: Map<KClass<out PkgDataSource>, Collection<Installed>> = coroutineScope {
pkgSources.map { source ->
async {
log(TAG) { "generatePkgcache(): $source start..." }
val sourceStart = System.currentTimeMillis()
val fromSource = source.getPkgs()
val sourceStop = System.currentTimeMillis()
log(TAG) {
"generatePkgcache(): ${fromSource.size} pkgs from $source took ${sourceStop - sourceStart}ms"
}
}.awaitAll().toMap()
source::class to fromSource
}
}
}.awaitAll().toMap()

val mergedData = mutableMapOf<CacheKey, CachedInfo>()
val mergedData = mutableMapOf<CacheKey, CachedInfo>()

// This is our primary source of data, we don't overwrite this data with data from other sources
sourceMap[NormalPkgsSource::class]!!.forEach { pkg ->
val key = CacheKey(pkg)
mergedData[key] = CachedInfo(key, pkg)
}
// This is our primary source of data, we don't overwrite this data with data from other sources
sourceMap[NormalPkgsSource::class]!!.forEach { pkg ->
val key = CacheKey(pkg)
mergedData[key] = CachedInfo(key, pkg)
}

sourceMap
.filter { it.key != NormalPkgsSource::class }
.onEach { (type, pkgs) ->
val extraPkgs = pkgs
.map { pkg ->
val key = CacheKey(pkg)
key to CachedInfo(key, pkg)
}
.filter { (key, _) -> !mergedData.containsKey(key) }
.associate { (key, info) -> key to info }

if (Bugs.isTrace) {
log(TAG) { "${extraPkgs.size} extra pkgs from $type" }
extraPkgs.forEach { log(TAG, VERBOSE) { "Extra pkg from $type: ${it.value}" } }
}

mergedData.putAll(extraPkgs)
sourceMap
.filter { it.key != NormalPkgsSource::class }
.onEach { (type, pkgs) ->
val extraPkgs = pkgs
.map { pkg ->
val key = CacheKey(pkg)
key to CachedInfo(key, pkg)
}
.filter { (key, _) -> !mergedData.containsKey(key) }
.associate { (key, info) -> key to info }

if (Bugs.isTrace) {
log(TAG) { "${extraPkgs.size} extra pkgs from $type" }
extraPkgs.forEach { log(TAG, VERBOSE) { "Extra pkg from $type: ${it.value}" } }
}

log(TAG, INFO) { "Pkgs total: ${mergedData.size}" }
mergedData.values.forEach { log(TAG, DEBUG) { "Installed package: $it" } }
val stop = System.currentTimeMillis()
log(TAG, INFO) { "generatePkgcache(): PkgRepo load took ${stop - start}ms" }
mergedData
mergedData.putAll(extraPkgs)
}
}

log(TAG, INFO) { "Pkgs total: ${mergedData.size}" }
mergedData.values.forEach { log(TAG, DEBUG) { "Installed package: $it" } }
val stop = System.currentTimeMillis()
log(TAG, INFO) { "generatePkgcache(): PkgRepo load took ${stop - start}ms" }

leases.closeAll()

return mergedData
}

suspend fun refresh(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import eu.darken.sdmse.common.progress.withProgress
import eu.darken.sdmse.common.root.RootManager
import eu.darken.sdmse.common.sharedresource.SharedResource
import eu.darken.sdmse.common.sharedresource.keepResourceHoldersAlive
import eu.darken.sdmse.common.shell.ShellOps
import eu.darken.sdmse.exclusion.core.ExclusionManager
import eu.darken.sdmse.exclusion.core.types.Exclusion
import eu.darken.sdmse.exclusion.core.types.PathExclusion
Expand Down Expand Up @@ -71,11 +72,12 @@ class AppCleaner @Inject constructor(
usageStatsSetupModule: UsageStatsSetupModule,
rootManager: RootManager,
adbManager: AdbManager,
shellOps: ShellOps,
private val filterFactories: Set<@JvmSuppressWildcards ExpendablesFilter.Factory>,
private val appInventorySetupModule: InventorySetupModule,
) : SDMTool, Progress.Client {

private val usedResources = setOf(fileForensics, gatewaySwitch, pkgOps)
private val usedResources = setOf(fileForensics, gatewaySwitch, pkgOps, shellOps)

override val sharedResource = SharedResource.createKeepAlive(TAG, appScope)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ import eu.darken.sdmse.common.debug.logging.logTag
import eu.darken.sdmse.common.files.GatewaySwitch
import eu.darken.sdmse.common.pkgs.pkgops.PkgOps
import eu.darken.sdmse.common.sharedresource.closeAll
import eu.darken.sdmse.common.shell.ShellOps
import javax.inject.Inject

@Reusable
class DataAreaFactory @Inject constructor(
private val pkgOps: PkgOps,
private val gatewaySwitch: GatewaySwitch,
private val shellOps: ShellOps,
private val areaModules: Set<@JvmSuppressWildcards DataAreaModule>,
) {

suspend fun build(): Collection<DataArea> {
log(TAG) { "build()" }
val leases = setOf(pkgOps, gatewaySwitch).map { it.sharedResource.get() }
val leases = setOf(pkgOps, gatewaySwitch, shellOps).map { it.sharedResource.get() }

val firstPass = areaModules.map { it.firstPass() }.flatten()
log(TAG, VERBOSE) { "build(): First pass: ${firstPass.joinToString("\n")}" }
Expand Down

0 comments on commit 715acc3

Please sign in to comment.