diff --git a/app/build.gradle b/app/build.gradle index fb731f89dac185547a3f55f56213d0ef1c8f6cfa..6fc16b30f80ca44c795f1bd1a0feab7494ec9c69 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -51,7 +51,6 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = [ - persistent: "false", mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER" ] @@ -89,7 +88,6 @@ android { dimension 'os' applicationIdSuffix '.standalone' manifestPlaceholders = [ - persistent: "false", mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER" ] @@ -112,7 +110,6 @@ android { } release { manifestPlaceholders = [ - persistent: "true", mainActivityIntentFilterCategory: "android.intent.category.INFO" ] diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 90a5bbf14efb52633b7fcf5b82ae6b56dd067faa..508c2f5901c96fe1c4ef6fb79dcdc89ad8061f8e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,12 +39,13 @@ + + (CoroutineScope::class.java).launch { + initBackgroundSingletons() } + } + private fun synchronousInitComponents() { // Fix 3386 : Initialize OrbotSupervisor, to make sure all Orbot is waked up before // OrbotService may be started by the system. val orbotSupervisor = get(OrbotSupervisor::class.java) - get(CoroutineScope::class.java).launch { - initBackgroundSingletons() - } + get(IpScramblingStateUseCase::class.java) + get(TrackersStateUseCase::class.java) + get(FakeLocationStateUseCase::class.java) + get(VpnSupervisorUseCase::class.java).listenSettings() } private suspend fun initBackgroundSingletons() = withContext(Dispatchers.IO) { @@ -82,11 +97,6 @@ class AdvancedPrivacyApplication : Application() { ) get(NotificationsPresenter::class.java).startListening() - - get(IpScramblingStateUseCase::class.java) - get(TrackersStateUseCase::class.java) - get(FakeLocationStateUseCase::class.java) - get(VpnSupervisorUseCase::class.java).listenSettings() get(WeeklyReportUseCase::class.java).listen() } } diff --git a/privapp-permissions-foundation.e.advancedprivacy.xml b/privapp-permissions-foundation.e.advancedprivacy.xml index 7edaacee198ba9064a4dc36705251db3218c737d..29b2c3eec87ac0390150ca84c453658b30204ad5 100644 --- a/privapp-permissions-foundation.e.advancedprivacy.xml +++ b/privapp-permissions-foundation.e.advancedprivacy.xml @@ -6,5 +6,6 @@ + - \ No newline at end of file + diff --git a/trackersserviceeos/build.gradle b/trackersserviceeos/build.gradle index 6d67b5822b606f9d359a5a6164b45fcac36d5028..30d35648806db6d11302971e76277ac9839f12d0 100644 --- a/trackersserviceeos/build.gradle +++ b/trackersserviceeos/build.gradle @@ -34,6 +34,7 @@ dependencies { implementation( libs.androidx.core.ktx, + libs.androidx.work.ktx, libs.bundles.koin, libs.kotlinx.coroutines, libs.pcap4j, diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/StayAwakeWorker.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/StayAwakeWorker.kt new file mode 100644 index 0000000000000000000000000000000000000000..239febf12179a8ff5b754a756432d68c8f40cee5 --- /dev/null +++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/StayAwakeWorker.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.advancedprivacy.trackers.service + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor +import org.koin.java.KoinJavaComponent +import timber.log.Timber + +class StayAwakeWorker( + context: Context, + workerParams: WorkerParameters +) : CoroutineWorker(context, workerParams) { + override suspend fun doWork(): Result { + return try { + val trackerSupervisor: TrackersSupervisorEos by KoinJavaComponent.inject( + TrackersSupervisor::class.java + ) + + trackerSupervisor.wakeUpService() + Result.success() + } catch (e: Exception) { + Timber.w(e, "Can't Wakeup tracker service") + Result.failure() + } + } +} diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt index 5f573b0fa9d5d4fa8e3e283870e4e3cd24ead7e6..f38699dd5af357d970a59d57ec14f74f7abadde9 100644 --- a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt +++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt @@ -22,24 +22,40 @@ import android.content.Intent import android.os.IBinder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import org.koin.java.KoinJavaComponent.get class TrackersService : Service() { companion object { const val ACTION_START = "foundation.e.privacymodules.trackers.intent.action.START" + const val ACTION_RESTART = "foundation.e.privacymodules.trackers.intent.action.RESTART" var coroutineScope = CoroutineScope(Dispatchers.IO) } + private var writeLogJob: Job? = null + private var listenJob: Job? = null + override fun onBind(intent: Intent): IBinder? { throw UnsupportedOperationException("Not yet implemented") } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (ACTION_START == intent?.action) { - stop() - start() + when (intent?.action) { + ACTION_START -> { + if (writeLogJob?.isActive != true || + listenJob?.isActive != true + ) { + stop() + start() + } + } + + ACTION_RESTART -> { + stop() + start() + } } return START_REDELIVER_INTENT } @@ -47,8 +63,8 @@ class TrackersService : Service() { private fun start() { coroutineScope = CoroutineScope(Dispatchers.IO) get(DNSBlocker::class.java).apply { - filterHostnameUseCase.writeLogJob(coroutineScope) - listenJob(coroutineScope) + writeLogJob = filterHostnameUseCase.writeLogJob(coroutineScope) + listenJob = listenJob(coroutineScope) } } diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorEos.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorEos.kt index 4580389acc77e9725ced5d956144bf100011e25b..f6bd3c5284a17a915399ae727cd656c8e6627ce4 100644 --- a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorEos.kt +++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorEos.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2025 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify @@ -18,10 +19,14 @@ package foundation.e.advancedprivacy.trackers.service import android.content.Context import android.content.Intent +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor import foundation.e.advancedprivacy.trackers.service.TrackersService.Companion.ACTION_START +import java.time.Duration import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.isActive @@ -32,10 +37,23 @@ import org.koin.core.qualifier.named import org.koin.dsl.module class TrackersSupervisorEos(private val context: Context) : TrackersSupervisor { + companion object { + private const val STAYAWAKE_WORKER_TAG = "STAYAWAKE_WORKER" + private const val STAYAWAKE_PERIOD_IN_MINUTES = 15L + } override val state: StateFlow = MutableStateFlow(FeatureState.ON) override fun start(): Boolean { + startStayAwayWorker() + return startService() + } + + fun wakeUpService(): Boolean { + return startService() + } + + private fun startService(): Boolean { val intent = Intent(context, TrackersService::class.java) intent.action = ACTION_START return context.startService(intent) != null @@ -50,6 +68,13 @@ class TrackersSupervisorEos(private val context: Context) : TrackersSupervisor { } override val dnsFilterForIpScrambling = null + + private fun startStayAwayWorker() { + val workRequest = PeriodicWorkRequestBuilder(Duration.ofMinutes(STAYAWAKE_PERIOD_IN_MINUTES)) + .addTag(STAYAWAKE_WORKER_TAG).build() + + WorkManager.getInstance(context).enqueueUniquePeriodicWork(STAYAWAKE_WORKER_TAG, ExistingPeriodicWorkPolicy.REPLACE, workRequest) + } } val trackerServiceModule = module { diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt index 510c0499447240ce2c3a9d2750c19592ebb346bd..8b2a9117a0fd928c7059dfa416d115c2f0594d57 100644 --- a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt +++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt @@ -30,6 +30,7 @@ import foundation.e.advancedprivacy.ipscrambler.OrbotSupervisor import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch @@ -44,16 +45,21 @@ class VpnSupervisorUseCaseEos( private val scope: CoroutineScope ) : VpnSupervisorUseCase { + private var listenIpscramblingJob: Job? = null + private var listenDisclaimerJob: Job? = null + override fun listenSettings() { trackersSupervisor.start() - scope.launch(Dispatchers.IO) { + listenIpscramblingJob?.cancel() + listenIpscramblingJob = scope.launch(Dispatchers.IO) { localStateRepository.ipScramblingEnabled.collect { applySettings(it) } } - scope.launch(Dispatchers.IO) { + listenDisclaimerJob?.cancel() + listenDisclaimerJob = scope.launch(Dispatchers.IO) { localStateRepository.blockTrackers.drop(1).filter { it }.collect { localStateRepository.emitStartVpnDisclaimer(TrackersControl()) }