Skip to content
Open
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
Expand Up @@ -11,6 +11,7 @@ import com.itsaky.androidide.events.InstallationEvent
import com.itsaky.androidide.models.StorageInfo
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.FeatureFlags
import com.itsaky.androidide.utils.bytesToGigabytes
import com.itsaky.androidide.utils.gigabytesToBytes
import com.itsaky.androidide.utils.withStopWatch
Expand All @@ -34,9 +35,17 @@ import org.slf4j.LoggerFactory

class InstallationViewModel : ViewModel() {
companion object {
const val LEAST_STORAGE_NEEDED_FOR_INSTALLATION = 2L
private const val MINIMUM_STORAGE_STABLE_GB = 4L
private const val MINIMUM_STORAGE_EXPERIMENTAL_GB = 6L
}

private val minimumStorageNeeded: Long
get() = if (FeatureFlags.isExperimentsEnabled) {
MINIMUM_STORAGE_EXPERIMENTAL_GB
} else {
MINIMUM_STORAGE_STABLE_GB
}
Comment on lines +42 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the FeatureFlags file
fd -t f 'FeatureFlags' --extension=kt

Repository: appdevforall/CodeOnTheGo

Length of output: 132


🏁 Script executed:

#!/bin/bash
# Search for isExperimentsEnabled definition and usage
rg -n 'isExperimentsEnabled' --type=kotlin -B 2 -A 5

Repository: appdevforall/CodeOnTheGo

Length of output: 9661


🏁 Script executed:

#!/bin/bash
# Check for the getStorageInfo method to understand threading context
rg -n 'getStorageInfo' --type=kotlin -B 2 -A 10

Repository: appdevforall/CodeOnTheGo

Length of output: 3223


🏁 Script executed:

#!/bin/bash
# Look for FeatureFlags object definition
rg -n 'object FeatureFlags' --type=kotlin -A 30

Repository: appdevforall/CodeOnTheGo

Length of output: 3096


🏁 Script executed:

#!/bin/bash
# Check how flags is accessed and if mutex is used
rg -n 'flags\.' common/src/main/java/com/itsaky/androidide/utils/FeatureFlags.kt -B 1 -A 1

Repository: appdevforall/CodeOnTheGo

Length of output: 769


🏁 Script executed:

#!/bin/bash
# Check for mutex usage in FeatureFlags
rg -n 'mutex' common/src/main/java/com/itsaky/androidide/utils/FeatureFlags.kt -B 2 -A 2

Repository: appdevforall/CodeOnTheGo

Length of output: 367


🏁 Script executed:

#!/bin/bash
# Check the full FeatureFlags file to understand the synchronization strategy
wc -l common/src/main/java/com/itsaky/androidide/utils/FeatureFlags.kt

Repository: appdevforall/CodeOnTheGo

Length of output: 136


🏁 Script executed:

#!/bin/bash
# Get the full FeatureFlags file
cat -n common/src/main/java/com/itsaky/androidide/utils/FeatureFlags.kt

Repository: appdevforall/CodeOnTheGo

Length of output: 3529


The getter should be protected by the mutex to ensure explicit thread-safety.

The computed property is correct and idiomatic. However, the thread-safety concern is valid: FeatureFlags.isExperimentsEnabled reads flags.experimentsEnabled without acquiring the Mutex, while the initialize() function safely updates flags using mutex.withLock(). This creates an unprotected read path that could see stale or intermediate values during flag updates.

While Boolean field access is atomic at the JVM level (mitigating practical risk), the code is not explicitly thread-safe by design. Either acquire the mutex in the getter or annotate the flags field with @Volatile for clarity and correctness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/viewmodel/InstallationViewModel.kt`
around lines 42 - 47, The getter minimumStorageNeeded reads
FeatureFlags.isExperimentsEnabled which indirectly reads the shared flags field
without synchronization; to make it explicitly thread-safe, protect that read by
acquiring the same mutex used in initialize() (use mutex.withLock {
FeatureFlags.isExperimentsEnabled } or wrap the whole conditional in
mutex.withLock) so the computed property cannot observe intermediate flag state,
or alternatively mark the flags field `@Volatile` if you prefer lock-free
visibility—apply the chosen change to minimumStorageNeeded and keep initialize()
unchanged.


private val log = LoggerFactory.getLogger(InstallationViewModel::class.java)

private val _state = MutableStateFlow<InstallationState>(InstallationPending)
Expand Down Expand Up @@ -139,7 +148,7 @@ class InstallationViewModel : ViewModel() {
val stat = StatFs(internalStoragePath)

val availableStorageInBytes = stat.availableBlocksLong * stat.blockSizeLong
val requiredStorageInBytes = LEAST_STORAGE_NEEDED_FOR_INSTALLATION.gigabytesToBytes()
val requiredStorageInBytes = minimumStorageNeeded.gigabytesToBytes()

val isLowStorage = availableStorageInBytes < requiredStorageInBytes

Expand Down