Skip to content
Draft
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
7 changes: 5 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
android:foregroundServiceType="specialUse"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:process=":engine"
tools:ignore="VpnServicePolicy">
<intent-filter>
<action android:name="android.net.VpnService" />
Expand All @@ -79,7 +80,8 @@
android:exported="true"
android:icon="@drawable/ic_play_arrow"
android:label="@string/app_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:process=":engine">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
Expand All @@ -88,7 +90,8 @@
<service
android:name=".service.engine.control.tester.EngineTesterService"
android:enabled="true"
android:exported="false" />
android:exported="false"
android:process=":engine" />

</application>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ abstract class AppDatabase : RoomDatabase() {
context.applicationContext,
AppDatabase::class.java,
"app_database"
).build().also { INSTANCE = it }
)
.enableMultiInstanceInvalidation()
.build().also { INSTANCE = it }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package com.clearpath.xray_compose.data.repo

import android.content.Context
import androidx.datastore.dataStore
import androidx.datastore.core.DataStore
import androidx.datastore.core.MultiProcessDataStoreFactory
import androidx.datastore.dataStoreFile
import com.clearpath.xray_compose.data.ConfigItem

val Context.configDataStore by dataStore(
fileName = "config.json",
serializer = ConfigItemSerializer
)
private var INSTANCE: DataStore<ConfigItem>? = null

val Context.configDataStore: DataStore<ConfigItem>
get() = INSTANCE ?: synchronized(MultiProcessDataStoreFactory::class.java) {
INSTANCE ?: MultiProcessDataStoreFactory.create(
serializer = ConfigItemSerializer,
produceFile = { this.applicationContext.dataStoreFile("config.json") }
).also { INSTANCE = it }
}
Comment on lines +9 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Concurrency & Thread Safety Issues

  1. Missing @Volatile: The INSTANCE variable is used in a double-checked locking pattern but is not marked as @Volatile. Without this, writes to INSTANCE might not be immediately visible to other threads, or the compiler/CPU might reorder instructions, leading to another thread reading a partially initialized INSTANCE.
  2. Locking on a Framework Class: Locking on MultiProcessDataStoreFactory::class.java is not recommended. It is a framework class, and locking on it can lead to unexpected contention or deadlocks if other parts of the app or libraries also lock on it. Use a private lock object instead.
Suggested change
private var INSTANCE: DataStore<ConfigItem>? = null
val Context.configDataStore: DataStore<ConfigItem>
get() = INSTANCE ?: synchronized(MultiProcessDataStoreFactory::class.java) {
INSTANCE ?: MultiProcessDataStoreFactory.create(
serializer = ConfigItemSerializer,
produceFile = { this.applicationContext.dataStoreFile("config.json") }
).also { INSTANCE = it }
}
@Volatile
private var INSTANCE: DataStore<ConfigItem>? = null
private val lock = Any()
val Context.configDataStore: DataStore<ConfigItem>
get() = INSTANCE ?: synchronized(lock) {
INSTANCE ?: MultiProcessDataStoreFactory.create(
serializer = ConfigItemSerializer,
produceFile = { this.applicationContext.dataStoreFile("config.json") }
).also { INSTANCE = it }
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
package com.clearpath.xray_compose.data.repo

import android.content.Context
import androidx.datastore.preferences.preferencesDataStore
import androidx.datastore.core.DataStore
import androidx.datastore.core.MultiProcessDataStoreFactory
import androidx.datastore.dataStoreFile
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.PreferencesFileSerializer

val Context.preferencesDataStore by preferencesDataStore(name = "config.preferences_pb")
private var INSTANCE: DataStore<Preferences>? = null

val Context.preferencesDataStore: DataStore<Preferences>
get() = INSTANCE ?: synchronized(MultiProcessDataStoreFactory::class.java) {
INSTANCE ?: MultiProcessDataStoreFactory.create(
serializer = PreferencesFileSerializer,
produceFile = {
this.applicationContext.dataStoreFile("config.preferences_pb")
}
).also { INSTANCE = it }
}
Comment on lines +10 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Compilation & Concurrency Issues

  1. Compilation Error (PreferencesFileSerializer is internal): PreferencesFileSerializer is an internal object in the androidx.datastore.preferences.core package. Referencing it directly here will cause a compilation error. To resolve this, you can copy the implementation of PreferencesFileSerializer into your project or implement a custom serializer for Preferences.
  2. Missing @Volatile: The INSTANCE variable is used in a double-checked locking pattern but is not marked as @Volatile.
  3. Locking on a Framework Class: Locking on MultiProcessDataStoreFactory::class.java is not recommended. Use a private lock object instead.
Suggested change
private var INSTANCE: DataStore<Preferences>? = null
val Context.preferencesDataStore: DataStore<Preferences>
get() = INSTANCE ?: synchronized(MultiProcessDataStoreFactory::class.java) {
INSTANCE ?: MultiProcessDataStoreFactory.create(
serializer = PreferencesFileSerializer,
produceFile = {
this.applicationContext.dataStoreFile("config.preferences_pb")
}
).also { INSTANCE = it }
}
@Volatile
private var INSTANCE: DataStore<Preferences>? = null
private val lock = Any()
val Context.preferencesDataStore: DataStore<Preferences>
get() = INSTANCE ?: synchronized(lock) {
INSTANCE ?: MultiProcessDataStoreFactory.create(
serializer = PreferencesFileSerializer,
produceFile = {
this.applicationContext.dataStoreFile("config.preferences_pb")
}
).also { INSTANCE = it }
}