Skip to content

Commit 205a4de

Browse files
committed
Improve session management
1 parent 5223696 commit 205a4de

3 files changed

Lines changed: 92 additions & 17 deletions

File tree

app/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ dependencies {
102102
implementation 'androidx.navigation:navigation-compose:2.8.0'
103103
implementation 'androidx.activity:activity-compose:1.9.2'
104104
implementation 'androidx.biometric:biometric:1.1.0'
105-
implementation "androidx.autofill:autofill:1.1.0"
105+
implementation 'androidx.autofill:autofill:1.1.0'
106+
implementation 'androidx.work:work-runtime-ktx:2.9.1'
106107

107108
//Room dependencies
108109
implementation "androidx.room:room-ktx:$room_version"

app/src/main/java/com/hegocre/nextcloudpasswords/api/ApiController.kt

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.hegocre.nextcloudpasswords.api
33
import android.content.Context
44
import android.util.Log
55
import androidx.lifecycle.MutableLiveData
6+
import androidx.work.WorkManager
67
import com.hegocre.nextcloudpasswords.api.encryption.CSEv1Keychain
78
import com.hegocre.nextcloudpasswords.api.exceptions.PWDv1ChallengeMasterKeyInvalidException
89
import com.hegocre.nextcloudpasswords.api.exceptions.PWDv1ChallengeMasterKeyNeededException
@@ -15,6 +16,7 @@ import com.hegocre.nextcloudpasswords.data.password.NewPassword
1516
import com.hegocre.nextcloudpasswords.data.password.Password
1617
import com.hegocre.nextcloudpasswords.data.password.UpdatedPassword
1718
import com.hegocre.nextcloudpasswords.data.user.UserController
19+
import com.hegocre.nextcloudpasswords.services.keepalive.KeepAliveWorker
1820
import com.hegocre.nextcloudpasswords.utils.Error
1921
import com.hegocre.nextcloudpasswords.utils.OkHttpRequest
2022
import com.hegocre.nextcloudpasswords.utils.PreferencesManager
@@ -57,6 +59,8 @@ class ApiController private constructor(context: Context) {
5759
val sessionOpen: StateFlow<Boolean>
5860
get() = _sessionOpen.asStateFlow()
5961

62+
private val workManager = WorkManager.getInstance(context)
63+
6064
init {
6165
decryptCSEv1Keychain(
6266
preferencesManager.getCSEv1Keychain(),
@@ -77,22 +81,6 @@ class ApiController private constructor(context: Context) {
7781
serverSettings.postValue(settings)
7882
preferencesManager.setServerSettings(settings)
7983
preferencesManager.setInstanceColor(settings.themeColorPrimary)
80-
81-
var keepAliveDelay = (settings.sessionLifetime * 3 / 4 * 1000).toLong()
82-
while (true) {
83-
sessionCode?.let {
84-
delay(keepAliveDelay)
85-
keepAliveDelay = if (sessionApi.keepAlive(it)) {
86-
Log.i("KeepAlive", "Successfully sent keep alive request")
87-
(settings.sessionLifetime * 3 / 4 * 1000).toLong()
88-
} else {
89-
Log.e("KeepAlive", "Error sending keep alive request")
90-
_sessionOpen.emit(false)
91-
sessionCode = null
92-
5000L
93-
}
94-
} ?: delay(5000L)
95-
}
9684
}
9785
OkHttpRequest.getInstance().allowInsecureRequests =
9886
preferencesManager.getSkipCertificateValidation()
@@ -215,6 +203,11 @@ class ApiController private constructor(context: Context) {
215203
}
216204
}
217205
sessionCode = newSessionCode
206+
serverSettings.value?.let { settings ->
207+
val keepAliveDelay = (settings.sessionLifetime * 3 / 4 * 1000).toLong()
208+
workManager.cancelAllWorkByTag(KeepAliveWorker.TAG)
209+
workManager.enqueue(KeepAliveWorker.getRequest(keepAliveDelay, newSessionCode))
210+
}
218211

219212
_sessionOpen.emit(true)
220213
return@withContext true
@@ -236,6 +229,11 @@ class ApiController private constructor(context: Context) {
236229
}
237230
}
238231

232+
suspend fun clearSession() {
233+
_sessionOpen.emit(false)
234+
sessionCode = null
235+
}
236+
239237
/**
240238
* Gets a list of the user passwords via the [PasswordsApi] class. This can only be called when a
241239
* session is open, otherwise an error is thrown.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.hegocre.nextcloudpasswords.services.keepalive
2+
3+
import android.content.Context
4+
import androidx.work.BackoffPolicy
5+
import androidx.work.Constraints
6+
import androidx.work.CoroutineWorker
7+
import androidx.work.Data
8+
import androidx.work.NetworkType
9+
import androidx.work.OneTimeWorkRequestBuilder
10+
import androidx.work.WorkManager
11+
import androidx.work.WorkRequest
12+
import androidx.work.WorkerParameters
13+
import com.hegocre.nextcloudpasswords.api.ApiController
14+
import com.hegocre.nextcloudpasswords.api.SessionApi
15+
import com.hegocre.nextcloudpasswords.data.user.UserController
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.withContext
18+
import java.util.concurrent.TimeUnit
19+
20+
class KeepAliveWorker(context: Context, private val params: WorkerParameters) :
21+
CoroutineWorker(context, params) {
22+
override suspend fun doWork(): Result {
23+
val apiController = ApiController.getInstance(applicationContext)
24+
if (!apiController.sessionOpen.value) {
25+
return Result.success()
26+
}
27+
28+
val server = UserController.getInstance(applicationContext).getServer()
29+
val sessionApi = SessionApi.getInstance(server)
30+
31+
val sessionCode = params.inputData.getString(SESSION_CODE_KEY) ?: return Result.failure()
32+
val keepAliveDelay = params.inputData.getLong(KEEPALIVE_DELAY_KEY, -1L)
33+
if (keepAliveDelay == -1L) return Result.failure()
34+
35+
val resultSuccess = withContext(Dispatchers.IO) {
36+
sessionApi.keepAlive(sessionCode)
37+
}
38+
39+
if (resultSuccess) {
40+
WorkManager.getInstance(applicationContext)
41+
.enqueue(getRequest(keepAliveDelay, sessionCode))
42+
return Result.success()
43+
} else {
44+
if (params.runAttemptCount > 2) {
45+
ApiController.getInstance(applicationContext).clearSession()
46+
return Result.failure()
47+
} else {
48+
return Result.retry()
49+
}
50+
}
51+
}
52+
53+
companion object {
54+
private const val SESSION_CODE_KEY = "com.hegocre.nextcloudpasswords.sessionCode"
55+
private const val KEEPALIVE_DELAY_KEY = "com.hegocre.nextcloudpasswords.keepAliveDelay"
56+
const val TAG = "com.hegocre.nextcloudpasswords.keepaliveworker"
57+
58+
fun getRequest(delay: Long, sessionCode: String): WorkRequest {
59+
val data = Data.Builder()
60+
.putString(SESSION_CODE_KEY, sessionCode)
61+
.putLong(KEEPALIVE_DELAY_KEY, delay)
62+
.build()
63+
return OneTimeWorkRequestBuilder<KeepAliveWorker>()
64+
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
65+
.setInputData(data)
66+
.setBackoffCriteria(BackoffPolicy.LINEAR, 10L, TimeUnit.SECONDS)
67+
.addTag(TAG)
68+
.setConstraints(
69+
Constraints.Builder()
70+
.setRequiredNetworkType(NetworkType.CONNECTED)
71+
.build()
72+
)
73+
.build()
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)