From 9d55978063947d5865bb3fa4e0c2ebef78f78812 Mon Sep 17 00:00:00 2001
From: Guillaume Jacquart <guillaume.jacquart@hoodbrains.com>
Date: Mon, 6 Nov 2023 08:14:27 +0000
Subject: epic18: Manage VPN services for Tor or Tracker control

---
 .gitlab-ci.yml                                     |   2 +-
 app/build.gradle                                   |  13 +-
 .../advancedprivacy/AdvancedPrivacyApplication.kt  |   5 +-
 .../foundation/e/advancedprivacy/KoinModule.kt     |  13 +-
 .../foundation/e/advancedprivacy/Notifications.kt  |  28 +-
 .../common/BootCompletedReceiver.kt                |   8 +-
 .../e/advancedprivacy/common/WarningDialog.kt      |  43 +--
 .../data/repositories/LocalStateRepository.kt      |  54 ++--
 .../domain/entities/LocationMode.kt                |  22 --
 .../domain/entities/MainFeatures.kt                |  22 --
 .../domain/entities/ShowFeaturesWarning.kt         |  31 --
 .../domain/usecases/FakeLocationStateUseCase.kt    |   9 +-
 .../domain/usecases/GetQuickPrivacyStateUseCase.kt |  13 +-
 .../domain/usecases/IpScramblingStateUseCase.kt    |  95 +------
 .../domain/usecases/ShowFeaturesWarningUseCase.kt  |  25 +-
 .../domain/usecases/TrackersStateUseCase.kt        |   9 +-
 .../features/dashboard/DashboardFragment.kt        |  10 +-
 .../features/dashboard/DashboardState.kt           |   4 +-
 .../internetprivacy/InternetPrivacyFragment.kt     |   8 +-
 .../internetprivacy/InternetPrivacyState.kt        |   4 +-
 .../internetprivacy/InternetPrivacyViewModel.kt    |   4 +-
 .../e/advancedprivacy/widget/WidgetUI.kt           |   6 +-
 core/build.gradle                                  |   9 +-
 .../domain/entities/FeatureServiceState.kt         |  25 --
 .../domain/entities/FeatureState.kt                |  25 ++
 .../domain/entities/LocationMode.kt                |  22 ++
 .../domain/entities/MainFeatures.kt                |  31 ++
 .../domain/repositories/LocalStateRepository.kt    |  59 ++++
 .../domain/usecases/VpnSupervisorUseCase.kt        |  27 ++
 .../servicesupervisors/FeatureSupervisor.kt        |  27 ++
 gradle/libs.versions.toml                          |   6 +-
 ipscrambling/README.md                             |   4 +-
 ipscrambling/build.gradle                          |   2 +-
 ipscrambling/orbotservice                          |   1 +
 .../e/advancedprivacy/ipscrambler/KoinModule.kt    |   2 +-
 .../ipscrambler/OrbotServiceSupervisor.kt          | 308 --------------------
 .../advancedprivacy/ipscrambler/OrbotSupervisor.kt | 312 +++++++++++++++++++++
 permissionse/.gitignore                            |   1 -
 permissionse/build.gradle                          |  32 ---
 permissionse/consumer-rules.pro                    |   0
 permissionse/libs/hidden-apis-stub/.gitignore      |   1 -
 permissionse/libs/hidden-apis-stub/build.gradle    |  35 ---
 .../hidden-apis-stub/src/main/AndroidManifest.xml  |  24 --
 .../src/main/java/android/app/AppOpsManager.java   |  46 ---
 .../main/java/android/app/NotificationChannel.java |  34 ---
 .../java/android/content/pm/PackageManager.java    | 104 -------
 .../src/main/java/android/content/pm/UserInfo.java |  36 ---
 .../java/android/net/IConnectivityManager.java     |  65 -----
 .../src/main/java/android/net/VpnManager.java      |  64 -----
 .../src/main/java/android/os/ServiceManager.java   |  24 --
 .../src/main/java/android/os/UserHandle.java       |  24 --
 .../src/main/java/android/os/UserManager.java      |  48 ----
 permissionse/proguard-rules.pro                    |  21 --
 permissionse/src/main/AndroidManifest.xml          |  59 ----
 .../permissions/PermissionsPrivacyModuleImpl.kt    | 258 -----------------
 permissionseos/.gitignore                          |   1 +
 permissionseos/build.gradle                        |  32 +++
 permissionseos/consumer-rules.pro                  |   0
 permissionseos/libs/hidden-apis-stub/.gitignore    |   1 +
 permissionseos/libs/hidden-apis-stub/build.gradle  |  35 +++
 .../hidden-apis-stub/src/main/AndroidManifest.xml  |  24 ++
 .../src/main/java/android/app/AppOpsManager.java   |  46 +++
 .../main/java/android/app/NotificationChannel.java |  34 +++
 .../java/android/content/pm/PackageManager.java    | 104 +++++++
 .../src/main/java/android/content/pm/UserInfo.java |  36 +++
 .../java/android/net/IConnectivityManager.java     |  65 +++++
 .../src/main/java/android/net/VpnManager.java      |  64 +++++
 .../src/main/java/android/os/ServiceManager.java   |  24 ++
 .../src/main/java/android/os/UserHandle.java       |  24 ++
 .../src/main/java/android/os/UserManager.java      |  48 ++++
 permissionseos/proguard-rules.pro                  |  21 ++
 permissionseos/src/main/AndroidManifest.xml        |  59 ++++
 .../permissions/PermissionsPrivacyModuleImpl.kt    | 258 +++++++++++++++++
 settings.gradle                                    |   6 +-
 .../TrackersServiceSupervisor.kt                   |  28 --
 .../externalinterfaces/TrackersSupervisor.kt       |  27 ++
 trackersservicee/.gitignore                        |   1 -
 trackersservicee/build.gradle                      |  40 ---
 trackersservicee/consumer-rules.pro                |   0
 trackersservicee/proguard-rules.pro                |  21 --
 trackersservicee/src/main/AndroidManifest.xml      |  35 ---
 .../advancedprivacy/trackers/service/DNSBlocker.kt | 104 -------
 .../trackers/service/TrackersService.kt            |  58 ----
 .../service/TrackersServiceSupervisorImpl.kt       |  53 ----
 trackersserviceeos/.gitignore                      |   1 +
 trackersserviceeos/build.gradle                    |  41 +++
 trackersserviceeos/consumer-rules.pro              |   0
 trackersserviceeos/proguard-rules.pro              |  21 ++
 trackersserviceeos/src/main/AndroidManifest.xml    |  35 +++
 .../advancedprivacy/trackers/service/DNSBlocker.kt | 104 +++++++
 .../trackers/service/TrackersService.kt            |  58 ++++
 .../trackers/service/TrackersSupervisorEos.kt      |  70 +++++
 .../trackers/service/VpnSupervisorUseCaseEos.kt    | 108 +++++++
 trackersservicestandalone/build.gradle             |   1 +
 .../trackers/service/TrackersService.kt            |  21 +-
 .../service/TrackersServiceSupervisorImpl.kt       |  73 -----
 .../service/TrackersSupervisorStandalone.kt        |  79 ++++++
 .../advancedprivacy/trackers/service/TunLooper.kt  |   1 +
 .../usecases/VpnSupervisorUseCaseStandalone.kt     | 154 ++++++++++
 99 files changed, 2249 insertions(+), 1931 deletions(-)
 delete mode 100644 app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
 delete mode 100644 app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
 delete mode 100644 app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt
 delete mode 100644 core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
 create mode 100644 core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureState.kt
 create mode 100644 core/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
 create mode 100644 core/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
 create mode 100644 core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt
 create mode 100644 core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt
 create mode 100644 core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/servicesupervisors/FeatureSupervisor.kt
 create mode 160000 ipscrambling/orbotservice
 delete mode 100644 ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt
 create mode 100644 ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotSupervisor.kt
 delete mode 100644 permissionse/.gitignore
 delete mode 100644 permissionse/build.gradle
 delete mode 100644 permissionse/consumer-rules.pro
 delete mode 100644 permissionse/libs/hidden-apis-stub/.gitignore
 delete mode 100644 permissionse/libs/hidden-apis-stub/build.gradle
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/AndroidManifest.xml
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java
 delete mode 100644 permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
 delete mode 100644 permissionse/proguard-rules.pro
 delete mode 100644 permissionse/src/main/AndroidManifest.xml
 delete mode 100644 permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
 create mode 100644 permissionseos/.gitignore
 create mode 100644 permissionseos/build.gradle
 create mode 100644 permissionseos/consumer-rules.pro
 create mode 100644 permissionseos/libs/hidden-apis-stub/.gitignore
 create mode 100644 permissionseos/libs/hidden-apis-stub/build.gradle
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/AndroidManifest.xml
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java
 create mode 100644 permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
 create mode 100644 permissionseos/proguard-rules.pro
 create mode 100644 permissionseos/src/main/AndroidManifest.xml
 create mode 100644 permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
 delete mode 100644 trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
 create mode 100644 trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersSupervisor.kt
 delete mode 100644 trackersservicee/.gitignore
 delete mode 100644 trackersservicee/build.gradle
 delete mode 100644 trackersservicee/consumer-rules.pro
 delete mode 100644 trackersservicee/proguard-rules.pro
 delete mode 100644 trackersservicee/src/main/AndroidManifest.xml
 delete mode 100644 trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
 delete mode 100644 trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
 delete mode 100644 trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
 create mode 100644 trackersserviceeos/.gitignore
 create mode 100644 trackersserviceeos/build.gradle
 create mode 100644 trackersserviceeos/consumer-rules.pro
 create mode 100644 trackersserviceeos/proguard-rules.pro
 create mode 100644 trackersserviceeos/src/main/AndroidManifest.xml
 create mode 100644 trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
 create mode 100644 trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
 create mode 100644 trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorEos.kt
 create mode 100644 trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt
 delete mode 100644 trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
 create mode 100644 trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt
 create mode 100644 trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 89a6f78..9f3d1f0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -93,7 +93,7 @@ test-debug:
 build-e-release:
   stage: build
   script:
-    - ./gradlew :app:assembleERelease
+    - ./gradlew :app:assembleEosRelease
   rules:
     - if: '$CI_COMMIT_REF_PROTECTED == "true" && $CI_COMMIT_TAG =~ /^eOS-/'
       variables:
diff --git a/app/build.gradle b/app/build.gradle
index 216b81a..95bbee6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,7 +20,6 @@ plugins {
     id 'com.android.application'
     id 'kotlin-android'
     id 'kotlin-kapt'
-    id 'kotlin-parcelize'
     id 'androidx.navigation.safeargs.kotlin'
 }
 
@@ -82,7 +81,7 @@ android {
     // expected by the android gradle plugin.
     flavorDimensions 'os'
     productFlavors {
-        e {
+        eos {
             dimension 'os'
             minSdkVersion 29
             targetSdkVersion 32
@@ -144,20 +143,20 @@ android {
 dependencies {
     implementation project(':core')
     standaloneImplementation project(':permissionsstandalone')
-    eImplementation project(':permissionse')
+    eosImplementation project(':permissionseos')
 
     implementation project(':fakelocation')
 
-    eImplementation files('libs/lineage-sdk.jar')
+    eosImplementation files('libs/lineage-sdk.jar')
 
     implementation project(':trackers')
 
     implementation project(':ipscrambling')
-    eImplementation project(':trackersservicee')
+    eosImplementation project(':trackersserviceeos')
     standaloneImplementation project(':trackersservicestandalone')
 
     implementation (
-        libs.e.elib,
+        libs.eos.elib,
 
         libs.androidx.core.ktx,
         libs.androidx.appcompat,
@@ -174,7 +173,7 @@ dependencies {
         libs.maplibre,
         libs.mpandroidcharts,
 
-        libs.e.telemetry,
+        libs.eos.telemetry,
         libs.timber
     )
 
diff --git a/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt b/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt
index 71fef00..0fc1d67 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2021 E FOUNDATION, 2022 - 2023 MURENA SAS
+ * Copyright (C) 2022 - 2023 MURENA SAS
+ * Copyright (C) 2021 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
@@ -25,6 +26,7 @@ import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase
 import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase
 import foundation.e.advancedprivacy.domain.usecases.TrackersStateUseCase
 import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
+import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase
 import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
 import foundation.e.advancedprivacy.trackers.services.UpdateTrackersWorker
 import foundation.e.lib.telemetry.Telemetry
@@ -70,5 +72,6 @@ class AdvancedPrivacyApplication : Application() {
         get<IpScramblingStateUseCase>(IpScramblingStateUseCase::class.java)
         get<TrackersStateUseCase>(TrackersStateUseCase::class.java)
         get<FakeLocationStateUseCase>(FakeLocationStateUseCase::class.java)
+        get<VpnSupervisorUseCase>(VpnSupervisorUseCase::class.java).listenSettings()
     }
 }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
index fbf1252..efcd096 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
@@ -20,11 +20,12 @@ package foundation.e.advancedprivacy
 import android.content.res.Resources
 import android.os.Process
 import foundation.e.advancedprivacy.core.coreModule
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.data.repositories.LocalStateRepositoryImpl
 import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
 import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG
 import foundation.e.advancedprivacy.domain.entities.NotificationContent
 import foundation.e.advancedprivacy.domain.entities.ProfileType
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
 import foundation.e.advancedprivacy.domain.usecases.AppListUseCase
 import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase
 import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
@@ -55,8 +56,8 @@ val appModule = module {
     includes(coreModule, trackersModule, fakelocationModule, ipScramblerModule, trackerServiceModule)
 
     factory<Resources> { androidContext().resources }
-    single {
-        LocalStateRepository(context = androidContext())
+    single<LocalStateRepository> {
+        LocalStateRepositoryImpl(context = androidContext())
     }
 
     single<ApplicationDescription>(named("AdvancedPrivacy")) {
@@ -120,15 +121,13 @@ val appModule = module {
     singleOf(::GetQuickPrivacyStateUseCase)
     single {
         IpScramblingStateUseCase(
-            orbotServiceSupervisor = get(),
-            permissionsPrivacyModule = get(),
-            appDesc = get(named("AdvancedPrivacy")),
+            orbotSupervisor = get(),
             localStateRepository = get(),
             appListsRepository = get(),
-            trackersServiceSupervisor = get(),
             coroutineScope = get()
         )
     }
+
     singleOf(::ShowFeaturesWarningUseCase)
     singleOf(::TrackersStateUseCase)
     singleOf(::TrackersStatisticsUseCase)
diff --git a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
index 639ede4..430e9d5 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
@@ -29,8 +29,7 @@ import foundation.e.advancedprivacy.domain.entities.CHANNEL_FAKE_LOCATION_FLAG
 import foundation.e.advancedprivacy.domain.entities.CHANNEL_FIRST_BOOT
 import foundation.e.advancedprivacy.domain.entities.CHANNEL_IPSCRAMBLING_FLAG
 import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
-import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FAKE_LOCATION_FLAG
 import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FIRST_BOOT
 import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_IPSCRAMBLING_FLAG
@@ -98,19 +97,19 @@ object Notifications {
 
         getQuickPrivacyStateUseCase.isLocationHidden.onEach {
             if (it) {
-                showFlagNotification(appContext, MainFeatures.FAKE_LOCATION)
+                showFlagNotification(appContext, NOTIFICATION_FAKE_LOCATION_FLAG)
             } else {
-                hideFlagNotification(appContext, MainFeatures.FAKE_LOCATION)
+                hideFlagNotification(appContext, NOTIFICATION_FAKE_LOCATION_FLAG)
             }
         }.launchIn(appScope)
 
         getQuickPrivacyStateUseCase.ipScramblingMode.map {
-            it != FeatureServiceState.OFF
+            it != FeatureState.OFF
         }.distinctUntilChanged().onEach {
             if (it) {
-                showFlagNotification(appContext, MainFeatures.IP_SCRAMBLING)
+                showFlagNotification(appContext, NOTIFICATION_IPSCRAMBLING_FLAG)
             } else {
-                hideFlagNotification(appContext, MainFeatures.IP_SCRAMBLING)
+                hideFlagNotification(appContext, NOTIFICATION_IPSCRAMBLING_FLAG)
             }
         }.launchIn(appScope)
     }
@@ -139,9 +138,9 @@ object Notifications {
         NotificationManagerCompat.from(context).createNotificationChannel(channel)
     }
 
-    private fun showFlagNotification(context: Context, feature: MainFeatures) {
-        when (feature) {
-            MainFeatures.FAKE_LOCATION -> showFlagNotification(
+    private fun showFlagNotification(context: Context, id: Int) {
+        when (id) {
+            NOTIFICATION_FAKE_LOCATION_FLAG -> showFlagNotification(
                 context = context,
                 id = NOTIFICATION_FAKE_LOCATION_FLAG,
                 content = NotificationContent(
@@ -154,7 +153,7 @@ object Notifications {
                         .createPendingIntent()
                 )
             )
-            MainFeatures.IP_SCRAMBLING -> showFlagNotification(
+            NOTIFICATION_IPSCRAMBLING_FLAG -> showFlagNotification(
                 context = context,
                 id = NOTIFICATION_IPSCRAMBLING_FLAG,
                 content = NotificationContent(
@@ -183,12 +182,7 @@ object Notifications {
         NotificationManagerCompat.from(context).notify(id, builder.build())
     }
 
-    private fun hideFlagNotification(context: Context, feature: MainFeatures) {
-        val id = when (feature) {
-            MainFeatures.FAKE_LOCATION -> NOTIFICATION_FAKE_LOCATION_FLAG
-            MainFeatures.IP_SCRAMBLING -> NOTIFICATION_IPSCRAMBLING_FLAG
-            else -> return
-        }
+    private fun hideFlagNotification(context: Context, id: Int) {
         NotificationManagerCompat.from(context).cancel(id)
     }
 }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt b/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt
index d73f770..562144d 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2023 MURENA SAS
  * Copyright (C) 2022 E FOUNDATION
  *
  * This program is free software: you can redistribute it and/or modify
@@ -21,12 +22,15 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import foundation.e.advancedprivacy.Notifications
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
+import org.koin.java.KoinJavaComponent.inject
 
 class BootCompletedReceiver : BroadcastReceiver() {
+
+    private val localStateRepository by inject<LocalStateRepository>(LocalStateRepository::class.java)
+
     override fun onReceive(context: Context, intent: Intent?) {
         if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
-            val localStateRepository = LocalStateRepository(context)
             if (localStateRepository.firstBoot) {
                 Notifications.showFirstBootNotification(context)
                 localStateRepository.firstBoot = false
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
index 589aa74..9dbfea9 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
@@ -29,9 +29,12 @@ import androidx.activity.result.contract.ActivityResultContracts
 import androidx.appcompat.app.AlertDialog
 import androidx.appcompat.app.AppCompatActivity
 import foundation.e.advancedprivacy.R
-import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning
-import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.FakeLocation
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.IpScrambling
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.TrackersControl
 import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase
+import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase
 import foundation.e.advancedprivacy.main.MainActivity
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.launchIn
@@ -57,7 +60,7 @@ class WarningDialog : AppCompatActivity() {
 
         private fun createIntent(
             context: Context,
-            feature: ShowFeaturesWarning,
+            feature: MainFeatures,
         ): Intent {
             val intent = Intent(context, WarningDialog::class.java)
             intent.putExtra(PARAM_FEATURE, feature)
@@ -67,13 +70,14 @@ class WarningDialog : AppCompatActivity() {
     }
 
     private var isWaitingForResult = false
+    private lateinit var feature: MainFeatures
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         window.setBackgroundDrawable(ColorDrawable(0))
 
-        val feature = try {
-            intent.getParcelableExtra<ShowFeaturesWarning>(PARAM_FEATURE)!!
+        feature = try {
+            intent.getParcelableExtra<MainFeatures>(PARAM_FEATURE)!!
         } catch (e: Exception) {
             Timber.e(e, "Missing mandatory activity parameter")
             finish()
@@ -82,7 +86,7 @@ class WarningDialog : AppCompatActivity() {
         showWarningDialog(feature)
     }
 
-    private fun showWarningDialog(feature: ShowFeaturesWarning) {
+    private fun showWarningDialog(feature: MainFeatures) {
         val builder = AlertDialog.Builder(this)
         builder.setOnDismissListener { if (!isWaitingForResult) finish() }
 
@@ -92,23 +96,23 @@ class WarningDialog : AppCompatActivity() {
 
         builder.setMessage(
             when (feature) {
-                ShowFeaturesWarning.TrackersControl -> R.string.warningdialog_trackers_message
-                ShowFeaturesWarning.FakeLocation -> R.string.warningdialog_location_message
-                is ShowFeaturesWarning.IpScrambling -> R.string.warningdialog_ipscrambling_message
+                is TrackersControl -> R.string.warningdialog_trackers_message
+                is FakeLocation -> R.string.warningdialog_location_message
+                is IpScrambling -> R.string.warningdialog_ipscrambling_message
             }
         )
 
         builder.setTitle(
             when (feature) {
-                ShowFeaturesWarning.TrackersControl -> R.string.warningdialog_trackers_title
-                ShowFeaturesWarning.FakeLocation -> R.string.warningdialog_location_title
-                is ShowFeaturesWarning.IpScrambling -> R.string.warningdialog_ipscrambling_title
+                is TrackersControl -> R.string.warningdialog_trackers_title
+                is FakeLocation -> R.string.warningdialog_location_title
+                is IpScrambling -> R.string.warningdialog_ipscrambling_title
             }
         )
 
         builder.setPositiveButton(
             when (feature) {
-                is ShowFeaturesWarning.IpScrambling -> R.string.warningdialog_ipscrambling_cta
+                is IpScrambling -> R.string.warningdialog_ipscrambling_cta
                 else -> R.string.ok
             }
         ) { _, _ ->
@@ -117,7 +121,7 @@ class WarningDialog : AppCompatActivity() {
                     .doNotShowAgain(feature)
             }
 
-            val vpnDisclaimerIntent = (feature as? ShowFeaturesWarning.IpScrambling)
+            val vpnDisclaimerIntent = (feature as? MainFeatures.IpScrambling)
                 ?.startVpnDisclaimer
 
             if (vpnDisclaimerIntent != null) {
@@ -126,7 +130,7 @@ class WarningDialog : AppCompatActivity() {
             } else finish()
         }
 
-        if (feature == ShowFeaturesWarning.TrackersControl) {
+        if (feature is MainFeatures.TrackersControl) {
             builder.setNeutralButton(R.string.warningdialog_trackers_secondary_cta) { _, _ ->
                 MainActivity.deepLinkBuilder(this)
                     .setDestination(R.id.trackersFragment)
@@ -135,16 +139,17 @@ class WarningDialog : AppCompatActivity() {
                 finish()
             }
         }
-
         builder.show()
     }
 
     private val launchAndroidVpnDisclaimer = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
-        val ipScramblingStateUseCase = get<IpScramblingStateUseCase>(IpScramblingStateUseCase::class.java)
+        val vpnSupervisorUseCase = get<VpnSupervisorUseCase>(
+            VpnSupervisorUseCase::class.java
+        )
         if (result.resultCode == Activity.RESULT_OK) {
-            ipScramblingStateUseCase.startIpScrambling()
+            vpnSupervisorUseCase.startVpnService(feature)
         } else {
-            ipScramblingStateUseCase.toggle(false)
+            vpnSupervisorUseCase.cancelStartVpnService(feature)
         }
         finish()
     }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
index c7d4a27..2afd6ee 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
@@ -19,18 +19,18 @@
 package foundation.e.advancedprivacy.data.repositories
 
 import android.content.Context
-import android.content.Intent
 import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.LocationMode
-import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.update
 
-class LocalStateRepository(context: Context) {
+class LocalStateRepositoryImpl(context: Context) : LocalStateRepository {
     companion object {
         private const val SHARED_PREFS_FILE = "localState"
         private const val KEY_BLOCK_TRACKERS = "blockTrackers"
@@ -47,25 +47,26 @@ class LocalStateRepository(context: Context) {
     private val sharedPref = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
 
     private val _blockTrackers = MutableStateFlow(sharedPref.getBoolean(KEY_BLOCK_TRACKERS, true))
-    val blockTrackers = _blockTrackers.asStateFlow()
 
-    fun setBlockTrackers(enabled: Boolean) {
+    override val blockTrackers = _blockTrackers.asStateFlow()
+
+    override fun setBlockTrackers(enabled: Boolean) {
         set(KEY_BLOCK_TRACKERS, enabled)
         _blockTrackers.update { enabled }
     }
 
-    val areAllTrackersBlocked: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    override val areAllTrackersBlocked: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
     private val _fakeLocationEnabled = MutableStateFlow(sharedPref.getBoolean(KEY_FAKE_LOCATION, false))
 
-    val fakeLocationEnabled = _fakeLocationEnabled.asStateFlow()
+    override val fakeLocationEnabled = _fakeLocationEnabled.asStateFlow()
 
-    fun setFakeLocationEnabled(enabled: Boolean) {
+    override fun setFakeLocationEnabled(enabled: Boolean) {
         set(KEY_FAKE_LOCATION, enabled)
         _fakeLocationEnabled.update { enabled }
     }
 
-    var fakeLocation: Pair<Float, Float>
+    override var fakeLocation: Pair<Float, Float>
         get() = Pair(
             // Initial default value is Quezon City
             sharedPref.getFloat(KEY_FAKE_LATITUDE, 14.6760f),
@@ -79,43 +80,48 @@ class LocalStateRepository(context: Context) {
                 .apply()
         }
 
-    val locationMode: MutableStateFlow<LocationMode> = MutableStateFlow(LocationMode.REAL_LOCATION)
+    override val locationMode: MutableStateFlow<LocationMode> = MutableStateFlow(LocationMode.REAL_LOCATION)
 
     private val _ipScramblingSetting = MutableStateFlow(sharedPref.getBoolean(KEY_IP_SCRAMBLING, false))
-    val ipScramblingSetting = _ipScramblingSetting.asStateFlow()
 
-    fun setIpScramblingSetting(enabled: Boolean) {
+    override val ipScramblingSetting = _ipScramblingSetting.asStateFlow()
+
+    override fun setIpScramblingSetting(enabled: Boolean) {
         set(KEY_IP_SCRAMBLING, enabled)
         _ipScramblingSetting.update { enabled }
     }
 
-    val internetPrivacyMode: MutableStateFlow<FeatureServiceState> = MutableStateFlow(FeatureServiceState.OFF)
+    override val internetPrivacyMode: MutableStateFlow<FeatureState> = MutableStateFlow(FeatureState.OFF)
+
+    private val _startVpnDisclaimer = MutableSharedFlow<MainFeatures>()
 
-    private val _startVpnDisclaimer = MutableSharedFlow<ShowFeaturesWarning.IpScrambling>()
-    suspend fun emitStartVpnDisclaimer(intent: Intent?) {
-        _startVpnDisclaimer.emit(ShowFeaturesWarning.IpScrambling(startVpnDisclaimer = intent))
+    override suspend fun emitStartVpnDisclaimer(feature: MainFeatures) {
+        _startVpnDisclaimer.emit(feature)
     }
-    val startVpnDisclaimer: SharedFlow<ShowFeaturesWarning.IpScrambling> = _startVpnDisclaimer
+
+    override val startVpnDisclaimer: SharedFlow<MainFeatures> = _startVpnDisclaimer
 
     private val _otherVpnRunning = MutableSharedFlow<ApplicationDescription>()
-    suspend fun emitOtherVpnRunning(appDesc: ApplicationDescription) {
+
+    override suspend fun emitOtherVpnRunning(appDesc: ApplicationDescription) {
         _otherVpnRunning.emit(appDesc)
     }
-    val otherVpnRunning: SharedFlow<ApplicationDescription> = _otherVpnRunning
 
-    var firstBoot: Boolean
+    override val otherVpnRunning: SharedFlow<ApplicationDescription> = _otherVpnRunning
+
+    override var firstBoot: Boolean
         get() = sharedPref.getBoolean(KEY_FIRST_BOOT, true)
         set(value) = set(KEY_FIRST_BOOT, value)
 
-    var hideWarningTrackers: Boolean
+    override var hideWarningTrackers: Boolean
         get() = sharedPref.getBoolean(KEY_HIDE_WARNING_TRACKERS, false)
         set(value) = set(KEY_HIDE_WARNING_TRACKERS, value)
 
-    var hideWarningLocation: Boolean
+    override var hideWarningLocation: Boolean
         get() = sharedPref.getBoolean(KEY_HIDE_WARNING_LOCATION, false)
         set(value) = set(KEY_HIDE_WARNING_LOCATION, value)
 
-    var hideWarningIpScrambling: Boolean
+    override var hideWarningIpScrambling: Boolean
         get() = sharedPref.getBoolean(KEY_HIDE_WARNING_IPSCRAMBLING, false)
         set(value) = set(KEY_HIDE_WARNING_IPSCRAMBLING, value)
 
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
deleted file mode 100644
index 62581eb..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.advancedprivacy.domain.entities
-
-enum class LocationMode {
-    REAL_LOCATION, RANDOM_LOCATION, SPECIFIC_LOCATION
-}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
deleted file mode 100644
index c63d3ab..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.advancedprivacy.domain.entities
-
-enum class MainFeatures {
-    TRACKERS_CONTROL, FAKE_LOCATION, IP_SCRAMBLING
-}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt
deleted file mode 100644
index 0d8e0e8..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-package foundation.e.advancedprivacy.domain.entities
-
-import android.content.Intent
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-sealed class ShowFeaturesWarning : Parcelable {
-    @Parcelize
-    object TrackersControl : ShowFeaturesWarning()
-    @Parcelize
-    object FakeLocation : ShowFeaturesWarning()
-    @Parcelize
-    data class IpScrambling(val startVpnDisclaimer: Intent? = null) : ShowFeaturesWarning()
-}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
index 983ba71..282116e 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
@@ -25,10 +25,10 @@ import android.location.Location
 import android.location.LocationListener
 import android.location.LocationManager
 import android.os.Bundle
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
 import foundation.e.advancedprivacy.domain.entities.AppOpModes
 import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
 import foundation.e.advancedprivacy.domain.entities.LocationMode
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
 import foundation.e.advancedprivacy.dummy.CityDataSource
 import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
 import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule
@@ -49,11 +49,10 @@ class FakeLocationStateUseCase(
     private val appContext: Context,
     coroutineScope: CoroutineScope
 ) {
-    companion object {
-        private const val TAG = "FakeLocationStateUseCase"
-    }
+    private val _configuredLocationMode = MutableStateFlow<Triple<LocationMode, Float?, Float?>>(
+        Triple(LocationMode.REAL_LOCATION, null, null)
+    )
 
-    private val _configuredLocationMode = MutableStateFlow<Triple<LocationMode, Float?, Float?>>(Triple(LocationMode.REAL_LOCATION, null, null))
     val configuredLocationMode: StateFlow<Triple<LocationMode, Float?, Float?>> = _configuredLocationMode
 
     init {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
index 1b8f62c..480d3b3 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2023 MURENA SAS
  * Copyright (C) 2021 E FOUNDATION
  *
  * This program is free software: you can redistribute it and/or modify
@@ -17,12 +18,12 @@
 
 package foundation.e.advancedprivacy.domain.usecases
 
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
 import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.LocationMode
 import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
 import foundation.e.advancedprivacy.domain.entities.TrackerMode
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -41,13 +42,13 @@ class GetQuickPrivacyStateUseCase(
         when {
             !isBlockTrackers &&
                 locationMode == LocationMode.REAL_LOCATION &&
-                internetPrivacyMode == FeatureServiceState.OFF -> QuickPrivacyState.DISABLED
+                internetPrivacyMode == FeatureState.OFF -> QuickPrivacyState.DISABLED
 
             isAllTrackersBlocked &&
                 locationMode != LocationMode.REAL_LOCATION &&
                 internetPrivacyMode in listOf(
-                FeatureServiceState.ON,
-                FeatureServiceState.STARTING
+                FeatureState.ON,
+                FeatureState.STARTING
             ) -> QuickPrivacyState.FULL_ENABLED
 
             else -> QuickPrivacyState.ENABLED
@@ -71,7 +72,7 @@ class GetQuickPrivacyStateUseCase(
 
     val locationMode: StateFlow<LocationMode> = localStateRepository.locationMode
 
-    val ipScramblingMode: Flow<FeatureServiceState> =
+    val ipScramblingMode: Flow<FeatureState> =
         localStateRepository.internetPrivacyMode
 
     fun toggleTrackers(enabled: Boolean?) {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
index 79c79f7..00613dd 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
@@ -18,43 +18,27 @@
 
 package foundation.e.advancedprivacy.domain.usecases
 
-import android.content.Intent
-import foundation.e.advancedprivacy.common.isStandaloneBuild
 import foundation.e.advancedprivacy.data.repositories.AppListsRepository
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
-import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
-import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
-import foundation.e.advancedprivacy.ipscrambler.OrbotServiceSupervisor
-import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
+import foundation.e.advancedprivacy.domain.entities.FeatureState
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.ipscrambler.OrbotSupervisor
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.launch
 
 class IpScramblingStateUseCase(
-    private val orbotServiceSupervisor: OrbotServiceSupervisor,
-    private val permissionsPrivacyModule: IPermissionsPrivacyModule,
-    private val appDesc: ApplicationDescription,
+    private val orbotSupervisor: OrbotSupervisor,
     private val localStateRepository: LocalStateRepository,
     private val appListsRepository: AppListsRepository,
-    private val trackersServiceSupervisor: TrackersServiceSupervisor,
     private val coroutineScope: CoroutineScope
 ) {
-    val internetPrivacyMode: StateFlow<FeatureServiceState> = orbotServiceSupervisor.state
+    val internetPrivacyMode: StateFlow<FeatureState> = orbotSupervisor.state
 
     init {
-        orbotServiceSupervisor.requestStatus()
+        orbotSupervisor.requestStatus()
 
-        coroutineScope.launch(Dispatchers.Default) {
-            localStateRepository.ipScramblingSetting.collect {
-                applySettings(it)
-            }
-        }
-
-        orbotServiceSupervisor.state.map {
+        orbotSupervisor.state.map {
             localStateRepository.internetPrivacyMode.value = it
         }.launchIn(coroutineScope)
     }
@@ -68,7 +52,7 @@ class IpScramblingStateUseCase(
     }
 
     val bypassTorApps: Set<String> get() {
-        var whitelist = orbotServiceSupervisor.appList
+        var whitelist = orbotSupervisor.appList
         if (getHiddenPackageNames().any { it in whitelist }) {
             val mutable = whitelist.toMutableSet()
             mutable.removeAll(getHiddenPackageNames())
@@ -86,7 +70,7 @@ class IpScramblingStateUseCase(
 
     fun toggleBypassTor(packageName: String) {
         val visibleList = bypassTorApps.toMutableSet()
-        val rawList = orbotServiceSupervisor.appList.toMutableSet()
+        val rawList = orbotSupervisor.appList.toMutableSet()
 
         if (visibleList.contains(packageName)) {
             if (packageName == appListsRepository.dummySystemApp.packageName) {
@@ -105,69 +89,16 @@ class IpScramblingStateUseCase(
                 rawList.add(packageName)
             }
         }
-        orbotServiceSupervisor.appList = rawList
+        orbotSupervisor.appList = rawList
     }
 
-    val availablesLocations: List<String> = orbotServiceSupervisor.getAvailablesLocations().sorted()
+    val availablesLocations: List<String> = orbotSupervisor.getAvailablesLocations().sorted()
 
-    val exitCountry: String get() = orbotServiceSupervisor.getExitCountryCode()
+    val exitCountry: String get() = orbotSupervisor.getExitCountryCode()
 
     suspend fun setExitCountry(locationId: String) {
         if (locationId != exitCountry) {
-            orbotServiceSupervisor.setExitCountryCode(locationId)
+            orbotSupervisor.setExitCountryCode(locationId)
         }
     }
-
-    private suspend fun applySettings(isIpScramblingEnabled: Boolean) {
-        val currentMode = localStateRepository.internetPrivacyMode.value
-        when {
-            isIpScramblingEnabled && currentMode in setOf(FeatureServiceState.OFF, FeatureServiceState.STOPPING) ->
-                applyStartIpScrambling()
-
-            !isIpScramblingEnabled && currentMode in setOf(FeatureServiceState.ON, FeatureServiceState.STARTING) ->
-                orbotServiceSupervisor.stop()
-
-            else -> {}
-        }
-    }
-
-    private suspend fun applyStartIpScrambling() {
-        val authorizeVpnIntent = orbotServiceSupervisor.prepareAndroidVpn()
-        if (authorizeVpnIntent == null) {
-            localStateRepository.emitStartVpnDisclaimer(null)
-
-            startIpScrambling()
-            return
-        }
-
-        acquireVpnAuthorization(authorizeVpnIntent)
-    }
-
-    private suspend fun acquireVpnAuthorization(authorizeVpnIntent: Intent) {
-        val authorized = permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName)
-        val alwaysOnVpnPackage = permissionsPrivacyModule.getAlwaysOnVpnPackage()
-
-        when {
-            authorized && alwaysOnVpnPackage == null -> {
-                localStateRepository.emitStartVpnDisclaimer(null)
-                startIpScrambling()
-            }
-            authorized && alwaysOnVpnPackage != null -> {
-                localStateRepository.emitOtherVpnRunning(
-                    permissionsPrivacyModule.getApplicationDescription(
-                        packageName = alwaysOnVpnPackage,
-                        withIcon = false
-                    )
-                )
-                localStateRepository.setIpScramblingSetting(enabled = false)
-            }
-            else -> localStateRepository.emitStartVpnDisclaimer(authorizeVpnIntent)
-        }
-    }
-
-    fun startIpScrambling() {
-        localStateRepository.internetPrivacyMode.value = FeatureServiceState.STARTING
-        orbotServiceSupervisor.setDNSFilter((trackersServiceSupervisor.dnsFilterForIpScrambling))
-        orbotServiceSupervisor.start(enableNotification = isStandaloneBuild)
-    }
 }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt
index c99d5f1..f8a0986 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2023 MURENA SAS
  * Copyright (C) 2022 E FOUNDATION
  *
  * This program is free software: you can redistribute it and/or modify
@@ -17,11 +18,11 @@
 
 package foundation.e.advancedprivacy.domain.usecases
 
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
-import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning
-import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning.FakeLocation
-import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning.IpScrambling
-import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning.TrackersControl
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.FakeLocation
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.IpScrambling
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.TrackersControl
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.drop
 import kotlinx.coroutines.flow.dropWhile
@@ -33,24 +34,22 @@ class ShowFeaturesWarningUseCase(
     private val localStateRepository: LocalStateRepository
 ) {
 
-    fun showWarning(): Flow<ShowFeaturesWarning> {
+    fun showWarning(): Flow<MainFeatures> {
         return merge(
-            localStateRepository.blockTrackers.drop(1).dropWhile { !it }
-                .filter { it && !localStateRepository.hideWarningTrackers }
-                .map { TrackersControl },
             localStateRepository.fakeLocationEnabled.drop(1).dropWhile { !it }
                 .filter { it && !localStateRepository.hideWarningLocation }
                 .map { FakeLocation },
             localStateRepository.startVpnDisclaimer.filter {
-                it.startVpnDisclaimer != null || !localStateRepository.hideWarningIpScrambling
+                (it is IpScrambling && !localStateRepository.hideWarningIpScrambling) ||
+                    (it is TrackersControl && !localStateRepository.hideWarningTrackers)
             }
         )
     }
 
-    fun doNotShowAgain(feature: ShowFeaturesWarning) {
+    fun doNotShowAgain(feature: MainFeatures) {
         when (feature) {
-            TrackersControl -> localStateRepository.hideWarningTrackers = true
-            FakeLocation -> localStateRepository.hideWarningLocation = true
+            is TrackersControl -> localStateRepository.hideWarningTrackers = true
+            is FakeLocation -> localStateRepository.hideWarningLocation = true
             is IpScrambling -> localStateRepository.hideWarningIpScrambling = true
         }
     }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
index 9b79dcc..2c47d70 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2021 E FOUNDATION, 2022 - 2023 MURENA SAS
+ * Copyright (C) 2022 - 2023 MURENA SAS
+ * Copyright (C) 2021 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
@@ -18,11 +19,10 @@
 package foundation.e.advancedprivacy.domain.usecases
 
 import foundation.e.advancedprivacy.data.repositories.AppListsRepository
-import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
 import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
 import foundation.e.advancedprivacy.trackers.data.WhitelistRepository
 import foundation.e.advancedprivacy.trackers.domain.entities.Tracker
-import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
@@ -30,7 +30,6 @@ class TrackersStateUseCase(
     private val whitelistRepository: WhitelistRepository,
     private val localStateRepository: LocalStateRepository,
     private val appListsRepository: AppListsRepository,
-    private val trackersServiceSupervisor: TrackersServiceSupervisor,
     coroutineScope: CoroutineScope,
 ) {
     init {
@@ -40,8 +39,6 @@ class TrackersStateUseCase(
                 updateAllTrackersBlockedState()
             }
         }
-
-        trackersServiceSupervisor.start()
     }
 
     private fun updateAllTrackersBlockedState() {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
index 0a53c6c..56cf81f 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
@@ -34,7 +34,7 @@ import foundation.e.advancedprivacy.R
 import foundation.e.advancedprivacy.common.GraphHolder
 import foundation.e.advancedprivacy.common.NavToolbarFragment
 import foundation.e.advancedprivacy.databinding.FragmentDashboardBinding
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.LocationMode
 import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
 import foundation.e.advancedprivacy.domain.entities.TrackerMode
@@ -186,11 +186,11 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
         binding.toggleIpscrambling.isChecked = state.ipScramblingMode.isChecked
         val isLoading = state.ipScramblingMode.isLoading
         binding.toggleIpscrambling.isEnabled = (
-            state.ipScramblingMode != FeatureServiceState.STOPPING
+            state.ipScramblingMode != FeatureState.STOPPING
             )
 
         binding.stateIpAddress.text = getString(
-            if (state.ipScramblingMode == FeatureServiceState.ON) R.string.dashboard_state_ipaddress_on
+            if (state.ipScramblingMode == FeatureState.ON) R.string.dashboard_state_ipaddress_on
             else R.string.dashboard_state_ipaddress_off
         )
 
@@ -200,7 +200,7 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
         binding.stateIpAddress.setTextColor(
             getColor(
                 requireContext(),
-                if (state.ipScramblingMode == FeatureServiceState.ON) R.color.green_valid
+                if (state.ipScramblingMode == FeatureState.ON) R.color.green_valid
                 else R.color.red_off
             )
         )
@@ -250,7 +250,7 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
         )
 
         binding.internetActivityPrivacy.subTitle = getString(
-            if (state.ipScramblingMode == FeatureServiceState.ON) R.string.dashboard_internet_activity_privacy_subtitle_on
+            if (state.ipScramblingMode == FeatureState.ON) R.string.dashboard_internet_activity_privacy_subtitle_on
             else R.string.dashboard_internet_activity_privacy_subtitle_off
         )
 
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt
index 069ff04..d26c53d 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt
@@ -17,7 +17,7 @@
 
 package foundation.e.advancedprivacy.features.dashboard
 
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.LocationMode
 import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
 import foundation.e.advancedprivacy.domain.entities.TrackerMode
@@ -26,7 +26,7 @@ data class DashboardState(
     val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED,
     val trackerMode: TrackerMode = TrackerMode.VULNERABLE,
     val isLocationHidden: Boolean = false,
-    val ipScramblingMode: FeatureServiceState = FeatureServiceState.STOPPING,
+    val ipScramblingMode: FeatureState = FeatureState.STOPPING,
     val locationMode: LocationMode = LocationMode.REAL_LOCATION,
     val leakedTrackersCount: Int? = null,
     val trackersCount: Int? = null,
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
index b4fc8a1..482a773 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
@@ -32,7 +32,7 @@ import foundation.e.advancedprivacy.common.NavToolbarFragment
 import foundation.e.advancedprivacy.common.ToggleAppsAdapter
 import foundation.e.advancedprivacy.common.setToolTipForAsterisk
 import foundation.e.advancedprivacy.databinding.FragmentInternetActivityPolicyBinding
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import kotlinx.coroutines.launch
 import org.koin.androidx.viewmodel.ext.android.viewModel
 import java.util.Locale
@@ -137,11 +137,11 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac
     private fun render(state: InternetPrivacyState) {
         binding.radioUseHiddenIp.radiobutton.apply {
             isChecked = state.mode.isChecked
-            isEnabled = state.mode != FeatureServiceState.STARTING
+            isEnabled = state.mode != FeatureState.STARTING
         }
         binding.radioUseRealIp.radiobutton.apply {
             isChecked = !state.mode.isChecked
-            isEnabled = state.mode != FeatureServiceState.STOPPING
+            isEnabled = state.mode != FeatureState.STOPPING
         }
 
         binding.ipscramblingSelectLocation.setSelection(state.selectedLocationPosition)
@@ -150,7 +150,7 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac
         binding.apps.post {
             (binding.apps.adapter as ToggleAppsAdapter?)?.setData(
                 list = state.getApps(),
-                isEnabled = state.mode == FeatureServiceState.ON
+                isEnabled = state.mode == FeatureState.ON
             )
         }
 
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt
index e607d6c..9ba716f 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt
@@ -18,10 +18,10 @@
 package foundation.e.advancedprivacy.features.internetprivacy
 
 import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 
 data class InternetPrivacyState(
-    val mode: FeatureServiceState = FeatureServiceState.OFF,
+    val mode: FeatureState = FeatureState.OFF,
     val availableApps: List<ApplicationDescription> = emptyList(),
     val bypassTorApps: Collection<String> = emptyList(),
     val selectedLocation: String = "",
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt
index 10530e1..b2f93c2 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt
@@ -22,7 +22,7 @@ import androidx.annotation.StringRes
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import foundation.e.advancedprivacy.R
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.usecases.AppListUseCase
 import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
 import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase
@@ -88,7 +88,7 @@ class InternetPrivacyViewModel(
 
         launch {
             ipScramblingStateUseCase.internetPrivacyMode
-                .map { it == FeatureServiceState.STARTING }
+                .map { it == FeatureState.STARTING }
                 .debounce(WARNING_LOADING_LONG_DELAY)
                 .collect {
                     if (it) _singleEvents.emit(
diff --git a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
index 1bd8693..e3454b9 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
@@ -31,7 +31,7 @@ import foundation.e.advancedprivacy.R
 import foundation.e.advancedprivacy.Widget
 import foundation.e.advancedprivacy.Widget.Companion.isDarkText
 import foundation.e.advancedprivacy.common.extensions.dpToPxF
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
 import foundation.e.advancedprivacy.domain.entities.TrackerMode
 import foundation.e.advancedprivacy.features.dashboard.DashboardFragmentArgs
@@ -45,7 +45,7 @@ data class State(
     val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED,
     val trackerMode: TrackerMode = TrackerMode.VULNERABLE,
     val isLocationHidden: Boolean = false,
-    val ipScramblingMode: FeatureServiceState = FeatureServiceState.STOPPING,
+    val ipScramblingMode: FeatureState = FeatureState.STOPPING,
     val dayStatistics: List<Pair<Int, Int>> = emptyList(),
     val activeTrackersCount: Int = 0,
 )
@@ -157,7 +157,7 @@ fun render(
         setTextViewText(
             R.id.state_ip_address,
             context.getString(
-                if (state.ipScramblingMode == FeatureServiceState.ON) R.string.widget_state_ipaddress_on
+                if (state.ipScramblingMode == FeatureState.ON) R.string.widget_state_ipaddress_on
                 else R.string.widget_state_ipaddress_off
             )
         )
diff --git a/core/build.gradle b/core/build.gradle
index 0e53f22..3b5234d 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2023 MURENA SAS
  * Copyright (C) 2022 E FOUNDATION
  *
  * This program is free software: you can redistribute it and/or modify
@@ -15,8 +16,12 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+    id 'kotlin-kapt'
+    id 'kotlin-parcelize'
+}
 
 group 'foundation.e'
 
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
deleted file mode 100644
index f079c56..0000000
--- a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-package foundation.e.advancedprivacy.domain.entities
-
-enum class FeatureServiceState {
-    OFF, ON, STARTING, STOPPING;
-
-    val isChecked get() = this == ON || this == STARTING
-
-    val isLoading get() = this == STARTING || this == STOPPING
-}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureState.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureState.kt
new file mode 100644
index 0000000..c756f4a
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureState.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.domain.entities
+
+enum class FeatureState {
+    OFF, ON, STARTING, STOPPING;
+
+    val isChecked get() = this == ON || this == STARTING
+
+    val isLoading get() = this == STARTING || this == STOPPING
+}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
new file mode 100644
index 0000000..62581eb
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.advancedprivacy.domain.entities
+
+enum class LocationMode {
+    REAL_LOCATION, RANDOM_LOCATION, SPECIFIC_LOCATION
+}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
new file mode 100644
index 0000000..1af2ae0
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.domain.entities
+
+import android.content.Intent
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+sealed class MainFeatures : Parcelable {
+    @Parcelize
+    data class TrackersControl(val startVpnDisclaimer: Intent? = null) : MainFeatures()
+    @Parcelize
+    object FakeLocation : MainFeatures()
+    @Parcelize
+    data class IpScrambling(val startVpnDisclaimer: Intent? = null) : MainFeatures()
+}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt
new file mode 100644
index 0000000..0266f85
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.domain.repositories
+
+import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.entities.FeatureState
+import foundation.e.advancedprivacy.domain.entities.LocationMode
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+
+interface LocalStateRepository {
+    val blockTrackers: StateFlow<Boolean>
+    fun setBlockTrackers(enabled: Boolean)
+
+    val areAllTrackersBlocked: MutableStateFlow<Boolean>
+
+    val fakeLocationEnabled: StateFlow<Boolean>
+    fun setFakeLocationEnabled(enabled: Boolean)
+
+    var fakeLocation: Pair<Float, Float>
+
+    val locationMode: MutableStateFlow<LocationMode>
+
+    fun setIpScramblingSetting(enabled: Boolean)
+    val ipScramblingSetting: StateFlow<Boolean>
+
+    val internetPrivacyMode: MutableStateFlow<FeatureState>
+
+    suspend fun emitStartVpnDisclaimer(feature: MainFeatures)
+
+    val startVpnDisclaimer: SharedFlow<MainFeatures>
+
+    suspend fun emitOtherVpnRunning(appDesc: ApplicationDescription)
+    val otherVpnRunning: SharedFlow<ApplicationDescription>
+
+    var firstBoot: Boolean
+
+    var hideWarningTrackers: Boolean
+
+    var hideWarningLocation: Boolean
+
+    var hideWarningIpScrambling: Boolean
+}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt
new file mode 100644
index 0000000..fce9fd0
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.domain.usecases
+
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+
+interface VpnSupervisorUseCase {
+    fun listenSettings()
+
+    fun startVpnService(feature: MainFeatures)
+
+    fun cancelStartVpnService(feature: MainFeatures)
+}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/servicesupervisors/FeatureSupervisor.kt b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/servicesupervisors/FeatureSupervisor.kt
new file mode 100644
index 0000000..632172d
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/servicesupervisors/FeatureSupervisor.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.externalinterfaces.servicesupervisors
+
+import foundation.e.advancedprivacy.domain.entities.FeatureState
+import kotlinx.coroutines.flow.StateFlow
+
+interface FeatureSupervisor {
+    fun start(): Boolean
+    fun stop(): Boolean
+
+    val state: StateFlow<FeatureState>
+}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ec4122e..5c945fa 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -29,9 +29,9 @@ androidx-room-compiler = { group = "androidx.room", name = "room-compiler", vers
 androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androidx-room" }
 androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidx-room" }
 androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", version = "2.7.1" }
-e-elib = { group = "foundation.e", name = "elib", version = "0.0.1-alpha11" }
-e-orbotservice = { group = "foundation.e", name = "orbotservice", version.ref = "orbotservice" }
-e-telemetry = { group = "foundation.e.lib", name = "telemetry", version = "0.0.8-alpha" }
+eos-elib = { group = "foundation.e", name = "elib", version = "0.0.1-alpha11" }
+eos-orbotservice = { group = "foundation.e", name = "orbotservice", version.ref = "orbotservice" }
+eos-telemetry = { group = "foundation.e.lib", name = "telemetry", version = "0.0.8-alpha" }
 google-material = { group = "com.google.android.material", name = "material", version = "1.6.1" }
 google-gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" }
 junit = { group = "junit", name = "junit", version = "4.13.1" }
diff --git a/ipscrambling/README.md b/ipscrambling/README.md
index be51828..104060a 100644
--- a/ipscrambling/README.md
+++ b/ipscrambling/README.md
@@ -8,8 +8,8 @@ Ipscrambling includes a git repo submodules of OrbotService, the module used by
 
 Be sure that you have all of the git submodules up-to-date:
 
-	git submodule update --init --recursive
-
+`	git submodule update --init --recursive
+`
 You can build the AAR modules :
 
     ./gradlew :ipscrambling:orbotservice:assembleRelease
diff --git a/ipscrambling/build.gradle b/ipscrambling/build.gradle
index 29fed4f..9ff3f8c 100644
--- a/ipscrambling/build.gradle
+++ b/ipscrambling/build.gradle
@@ -53,6 +53,6 @@ dependencies {
         libs.pcap4j,
         libs.timber
     )
-    implementation libs.e.orbotservice
+    implementation libs.eos.orbotservice
     implementation project(':core')
 }
diff --git a/ipscrambling/orbotservice b/ipscrambling/orbotservice
new file mode 160000
index 0000000..16c61e2
--- /dev/null
+++ b/ipscrambling/orbotservice
@@ -0,0 +1 @@
+Subproject commit 16c61e2f6fcb78c664aa23b9fed24048b52d943a
diff --git a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt
index 79aeb05..d9ef0be 100644
--- a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt
+++ b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt
@@ -21,5 +21,5 @@ import org.koin.core.module.dsl.singleOf
 import org.koin.dsl.module
 
 val ipScramblerModule = module {
-    singleOf(::OrbotServiceSupervisor)
+    singleOf(::OrbotSupervisor)
 }
diff --git a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt
deleted file mode 100644
index 8813948..0000000
--- a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.advancedprivacy.ipscrambler
-
-import android.annotation.SuppressLint
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.net.VpnService
-import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.update
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.pcap4j.packet.DnsPacket
-import org.torproject.android.service.OrbotConstants
-import org.torproject.android.service.OrbotConstants.ACTION_STOP_FOREGROUND_TASK
-import org.torproject.android.service.OrbotService
-import org.torproject.android.service.util.Prefs
-import timber.log.Timber
-import java.security.InvalidParameterException
-import java.util.function.Function
-
-@SuppressLint("CommitPrefEdits")
-class OrbotServiceSupervisor(
-    private val context: Context,
-    private val coroutineScope: CoroutineScope,
-) {
-    private val _state = MutableStateFlow(FeatureServiceState.OFF)
-    val state: StateFlow<FeatureServiceState> = _state
-
-    enum class Status {
-        OFF, ON, STARTING, STOPPING, START_DISABLED
-    }
-    companion object {
-        private val EXIT_COUNTRY_CODES = setOf("DE", "AT", "SE", "CH", "IS", "CA", "US", "ES", "FR", "BG", "PL", "AU", "BR", "CZ", "DK", "FI", "GB", "HU", "NL", "JP", "RO", "RU", "SG", "SK")
-
-        // Key where exit country is stored by orbot service.
-        private const val PREFS_KEY_EXIT_NODES = "pref_exit_nodes"
-        // Copy of the package private OrbotService.NOTIFY_ID value.
-        // const val ORBOT_SERVICE_NOTIFY_ID_COPY = 1
-    }
-
-    private var currentStatus: Status? = null
-
-    private val localBroadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            val action = intent.action ?: return
-            if (action == OrbotConstants.ACTION_RUNNING_SYNC) {
-                try {
-                    intent.getStringExtra(OrbotConstants.EXTRA_STATUS)?.let {
-                        val newStatus = Status.valueOf(it)
-                        currentStatus = newStatus
-                    }
-                } catch (e: Exception) {
-                    Timber.e("Can't parse Orbot service status.")
-                }
-                return
-            }
-
-            val msg = messageHandler.obtainMessage()
-            msg.obj = action
-            msg.data = intent.extras
-            messageHandler.sendMessage(msg)
-        }
-    }
-
-    private val messageHandler: Handler = object : Handler(Looper.getMainLooper()) {
-        override fun handleMessage(msg: Message) {
-            val action = msg.obj as? String ?: return
-            val data = msg.data
-            when (action) {
-                OrbotConstants.LOCAL_ACTION_PORTS -> {
-                    httpProxyPort = data.getInt(OrbotService.EXTRA_HTTP_PROXY_PORT, -1)
-                    socksProxyPort = data.getInt(OrbotService.EXTRA_SOCKS_PROXY_PORT, -1)
-                }
-
-                OrbotConstants.LOCAL_ACTION_STATUS ->
-                    data.getString(OrbotConstants.EXTRA_STATUS)?.let {
-                        try {
-                            val newStatus = Status.valueOf(it)
-                            updateStatus(newStatus, force = true)
-                        } catch (e: Exception) {
-                            Timber.e("Can't parse Orbot service status.")
-                        }
-                    }
-                OrbotConstants.LOCAL_ACTION_LOG,
-                OrbotConstants.LOCAL_ACTION_BANDWIDTH -> {} // Unused in Advanced Privacy
-            }
-            super.handleMessage(msg)
-        }
-    }
-
-    init {
-        Prefs.setContext(context)
-
-        val lbm = LocalBroadcastManager.getInstance(context)
-        lbm.registerReceiver(
-            localBroadcastReceiver,
-            IntentFilter(OrbotConstants.LOCAL_ACTION_STATUS)
-        )
-        lbm.registerReceiver(
-            localBroadcastReceiver,
-            IntentFilter(OrbotConstants.LOCAL_ACTION_BANDWIDTH)
-        )
-        lbm.registerReceiver(
-            localBroadcastReceiver,
-            IntentFilter(OrbotConstants.LOCAL_ACTION_LOG)
-        )
-        lbm.registerReceiver(
-            localBroadcastReceiver,
-            IntentFilter(OrbotConstants.LOCAL_ACTION_PORTS)
-        )
-        lbm.registerReceiver(
-            localBroadcastReceiver,
-            IntentFilter(OrbotConstants.ACTION_RUNNING_SYNC)
-        )
-
-        Prefs.getSharedPrefs(context).edit()
-            .putInt(OrbotConstants.PREFS_DNS_PORT, OrbotConstants.TOR_DNS_PORT_DEFAULT)
-            .apply()
-    }
-
-    private fun updateStatus(status: Status, force: Boolean = false) {
-        if (force || status != currentStatus) {
-            val newState = when (status) {
-                Status.OFF -> FeatureServiceState.OFF
-                Status.ON -> FeatureServiceState.ON
-                Status.STARTING -> FeatureServiceState.STARTING
-                Status.STOPPING,
-                Status.START_DISABLED -> FeatureServiceState.STOPPING
-            }
-
-            coroutineScope.launch(Dispatchers.IO) {
-                _state.update { currentState ->
-                    if (newState == FeatureServiceState.OFF &&
-                        currentState == FeatureServiceState.STOPPING
-                    ) {
-                        // Wait for orbot to relax before allowing user to reactivate it.
-                        delay(1000)
-                    }
-                    newState
-                }
-            }
-        }
-    }
-
-    private fun isServiceRunning(): Boolean {
-        // Reset status, and then ask to refresh it synchronously.
-        currentStatus = Status.OFF
-        LocalBroadcastManager.getInstance(context)
-            .sendBroadcastSync(Intent(OrbotConstants.ACTION_CHECK_RUNNING_SYNC))
-        return currentStatus != Status.OFF
-    }
-
-    private fun sendIntentToService(action: String, extra: Bundle? = null) {
-        val intent = Intent(context, OrbotService::class.java)
-        intent.action = action
-        extra?.let { intent.putExtras(it) }
-        context.startService(intent)
-    }
-
-    @SuppressLint("ApplySharedPref")
-    private fun saveTorifiedApps(packageNames: Collection<String>) {
-        packageNames.joinToString("|")
-        Prefs.getSharedPrefs(context).edit().putString(
-            OrbotConstants.PREFS_KEY_TORIFIED, packageNames.joinToString("|")
-        ).commit()
-
-        if (isServiceRunning()) {
-            sendIntentToService(OrbotConstants.ACTION_RESTART_VPN)
-        }
-    }
-
-    private fun getTorifiedApps(): Set<String> {
-        val list = Prefs.getSharedPrefs(context).getString(OrbotConstants.PREFS_KEY_TORIFIED, "")
-            ?.split("|")
-        return if (list == null || list == listOf("")) {
-            emptySet()
-        } else {
-            list.toSet()
-        }
-    }
-
-    @SuppressLint("ApplySharedPref")
-    suspend fun setExitCountryCode(countryCode: String) {
-        withContext(Dispatchers.IO) {
-            val countryParam = when {
-                countryCode.isEmpty() -> ""
-                countryCode in EXIT_COUNTRY_CODES -> "{$countryCode}"
-                else -> throw InvalidParameterException(
-                    "Only these countries are available: ${EXIT_COUNTRY_CODES.joinToString { ", " }}"
-                )
-            }
-
-            if (isServiceRunning()) {
-                val extra = Bundle()
-                extra.putString("exit", countryParam)
-                sendIntentToService(OrbotConstants.CMD_SET_EXIT, extra)
-            } else {
-                Prefs.getSharedPrefs(context)
-                    .edit().putString(PREFS_KEY_EXIT_NODES, countryParam)
-                    .commit()
-            }
-        }
-    }
-
-    fun getExitCountryCode(): String {
-        val raw = Prefs.getExitNodes()
-        return if (raw.isEmpty()) raw else raw.slice(1..2)
-    }
-
-    fun prepareAndroidVpn(): Intent? {
-        return VpnService.prepare(context)
-    }
-
-    fun setDNSFilter(shouldBlock: Function<DnsPacket?, DnsPacket?>?) {
-        OrbotService.shouldBlock = shouldBlock
-    }
-
-    fun start(enableNotification: Boolean) {
-        Prefs.enableNotification(enableNotification)
-        Prefs.putUseVpn(true)
-        Prefs.putStartOnBoot(true)
-
-        sendIntentToService(OrbotConstants.ACTION_START)
-        sendIntentToService(OrbotConstants.ACTION_START_VPN)
-    }
-
-    fun stop() {
-        if (!isServiceRunning()) return
-
-        updateStatus(Status.STOPPING)
-
-        Prefs.putUseVpn(false)
-        Prefs.putStartOnBoot(false)
-
-        sendIntentToService(OrbotConstants.ACTION_STOP_VPN)
-        sendIntentToService(
-            action = OrbotConstants.ACTION_STOP,
-            extra = Bundle().apply { putBoolean(ACTION_STOP_FOREGROUND_TASK, true) }
-        )
-        stoppingWatchdog(5)
-    }
-
-    private fun stoppingWatchdog(countDown: Int) {
-        Handler(Looper.getMainLooper()).postDelayed(
-            {
-                if (isServiceRunning() && countDown > 0) {
-                    stoppingWatchdog(countDown - 1)
-                } else {
-                    updateStatus(Status.OFF, force = true)
-                }
-            },
-            500
-        )
-    }
-
-    fun requestStatus() {
-        if (isServiceRunning()) {
-            sendIntentToService(OrbotConstants.ACTION_STATUS)
-        } else {
-            updateStatus(Status.OFF, force = true)
-        }
-    }
-
-    var appList: Set<String>
-        get() = getTorifiedApps()
-        set(value) = saveTorifiedApps(value)
-
-    fun getAvailablesLocations(): Set<String> = EXIT_COUNTRY_CODES
-
-    var httpProxyPort: Int = -1
-        private set
-
-    var socksProxyPort: Int = -1
-        private set
-
-    fun onCleared() {
-        LocalBroadcastManager.getInstance(context).unregisterReceiver(localBroadcastReceiver)
-    }
-}
diff --git a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotSupervisor.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotSupervisor.kt
new file mode 100644
index 0000000..6e0e205
--- /dev/null
+++ b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotSupervisor.kt
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.advancedprivacy.ipscrambler
+
+import android.annotation.SuppressLint
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.VpnService
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import foundation.e.advancedprivacy.domain.entities.FeatureState
+import foundation.e.advancedprivacy.externalinterfaces.servicesupervisors.FeatureSupervisor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.pcap4j.packet.DnsPacket
+import org.torproject.android.service.OrbotConstants
+import org.torproject.android.service.OrbotConstants.ACTION_STOP_FOREGROUND_TASK
+import org.torproject.android.service.OrbotService
+import org.torproject.android.service.util.Prefs
+import timber.log.Timber
+import java.security.InvalidParameterException
+import java.util.function.Function
+
+@SuppressLint("CommitPrefEdits")
+class OrbotSupervisor(
+    private val context: Context,
+    private val coroutineScope: CoroutineScope,
+) : FeatureSupervisor {
+    private val _state = MutableStateFlow(FeatureState.OFF)
+    override val state: StateFlow<FeatureState> = _state
+
+    enum class Status {
+        OFF, ON, STARTING, STOPPING, START_DISABLED
+    }
+    companion object {
+        private val EXIT_COUNTRY_CODES = setOf("DE", "AT", "SE", "CH", "IS", "CA", "US", "ES", "FR", "BG", "PL", "AU", "BR", "CZ", "DK", "FI", "GB", "HU", "NL", "JP", "RO", "RU", "SG", "SK")
+
+        // Key where exit country is stored by orbot service.
+        private const val PREFS_KEY_EXIT_NODES = "pref_exit_nodes"
+        // Copy of the package private OrbotService.NOTIFY_ID value.
+        // const val ORBOT_SERVICE_NOTIFY_ID_COPY = 1
+    }
+
+    private var currentStatus: Status? = null
+
+    private val localBroadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            val action = intent.action ?: return
+            if (action == OrbotConstants.ACTION_RUNNING_SYNC) {
+                try {
+                    intent.getStringExtra(OrbotConstants.EXTRA_STATUS)?.let {
+                        val newStatus = Status.valueOf(it)
+                        currentStatus = newStatus
+                    }
+                } catch (e: Exception) {
+                    Timber.e("Can't parse Orbot service status.")
+                }
+                return
+            }
+
+            val msg = messageHandler.obtainMessage()
+            msg.obj = action
+            msg.data = intent.extras
+            messageHandler.sendMessage(msg)
+        }
+    }
+
+    private val messageHandler: Handler = object : Handler(Looper.getMainLooper()) {
+        override fun handleMessage(msg: Message) {
+            val action = msg.obj as? String ?: return
+            val data = msg.data
+            when (action) {
+                OrbotConstants.LOCAL_ACTION_PORTS -> {
+                    httpProxyPort = data.getInt(OrbotService.EXTRA_HTTP_PROXY_PORT, -1)
+                    socksProxyPort = data.getInt(OrbotService.EXTRA_SOCKS_PROXY_PORT, -1)
+                }
+
+                OrbotConstants.LOCAL_ACTION_STATUS ->
+                    data.getString(OrbotConstants.EXTRA_STATUS)?.let {
+                        try {
+                            val newStatus = Status.valueOf(it)
+                            updateStatus(newStatus, force = true)
+                        } catch (e: Exception) {
+                            Timber.e("Can't parse Orbot service status.")
+                        }
+                    }
+                OrbotConstants.LOCAL_ACTION_LOG,
+                OrbotConstants.LOCAL_ACTION_BANDWIDTH -> {} // Unused in Advanced Privacy
+            }
+            super.handleMessage(msg)
+        }
+    }
+
+    init {
+        Prefs.setContext(context)
+
+        val lbm = LocalBroadcastManager.getInstance(context)
+        lbm.registerReceiver(
+            localBroadcastReceiver,
+            IntentFilter(OrbotConstants.LOCAL_ACTION_STATUS)
+        )
+        lbm.registerReceiver(
+            localBroadcastReceiver,
+            IntentFilter(OrbotConstants.LOCAL_ACTION_BANDWIDTH)
+        )
+        lbm.registerReceiver(
+            localBroadcastReceiver,
+            IntentFilter(OrbotConstants.LOCAL_ACTION_LOG)
+        )
+        lbm.registerReceiver(
+            localBroadcastReceiver,
+            IntentFilter(OrbotConstants.LOCAL_ACTION_PORTS)
+        )
+        lbm.registerReceiver(
+            localBroadcastReceiver,
+            IntentFilter(OrbotConstants.ACTION_RUNNING_SYNC)
+        )
+
+        Prefs.getSharedPrefs(context).edit()
+            .putInt(OrbotConstants.PREFS_DNS_PORT, OrbotConstants.TOR_DNS_PORT_DEFAULT)
+            .apply()
+    }
+
+    private fun updateStatus(status: Status, force: Boolean = false) {
+        if (force || status != currentStatus) {
+            val newState = when (status) {
+                Status.OFF -> FeatureState.OFF
+                Status.ON -> FeatureState.ON
+                Status.STARTING -> FeatureState.STARTING
+                Status.STOPPING,
+                Status.START_DISABLED -> FeatureState.STOPPING
+            }
+
+            coroutineScope.launch(Dispatchers.IO) {
+                _state.update { currentState ->
+                    if (newState == FeatureState.OFF &&
+                        currentState == FeatureState.STOPPING
+                    ) {
+                        // Wait for orbot to relax before allowing user to reactivate it.
+                        delay(1000)
+                    }
+                    newState
+                }
+            }
+        }
+    }
+
+    private fun isServiceRunning(): Boolean {
+        // Reset status, and then ask to refresh it synchronously.
+        currentStatus = Status.OFF
+        LocalBroadcastManager.getInstance(context)
+            .sendBroadcastSync(Intent(OrbotConstants.ACTION_CHECK_RUNNING_SYNC))
+        return currentStatus != Status.OFF
+    }
+
+    private fun sendIntentToService(action: String, extra: Bundle? = null) {
+        val intent = Intent(context, OrbotService::class.java)
+        intent.action = action
+        extra?.let { intent.putExtras(it) }
+        context.startService(intent)
+    }
+
+    @SuppressLint("ApplySharedPref")
+    private fun saveTorifiedApps(packageNames: Collection<String>) {
+        packageNames.joinToString("|")
+        Prefs.getSharedPrefs(context).edit().putString(
+            OrbotConstants.PREFS_KEY_TORIFIED, packageNames.joinToString("|")
+        ).commit()
+
+        if (isServiceRunning()) {
+            sendIntentToService(OrbotConstants.ACTION_RESTART_VPN)
+        }
+    }
+
+    private fun getTorifiedApps(): Set<String> {
+        val list = Prefs.getSharedPrefs(context).getString(OrbotConstants.PREFS_KEY_TORIFIED, "")
+            ?.split("|")
+        return if (list == null || list == listOf("")) {
+            emptySet()
+        } else {
+            list.toSet()
+        }
+    }
+
+    @SuppressLint("ApplySharedPref")
+    suspend fun setExitCountryCode(countryCode: String) {
+        withContext(Dispatchers.IO) {
+            val countryParam = when {
+                countryCode.isEmpty() -> ""
+                countryCode in EXIT_COUNTRY_CODES -> "{$countryCode}"
+                else -> throw InvalidParameterException(
+                    "Only these countries are available: ${EXIT_COUNTRY_CODES.joinToString { ", " }}"
+                )
+            }
+
+            if (isServiceRunning()) {
+                val extra = Bundle()
+                extra.putString("exit", countryParam)
+                sendIntentToService(OrbotConstants.CMD_SET_EXIT, extra)
+            } else {
+                Prefs.getSharedPrefs(context)
+                    .edit().putString(PREFS_KEY_EXIT_NODES, countryParam)
+                    .commit()
+            }
+        }
+    }
+
+    fun getExitCountryCode(): String {
+        val raw = Prefs.getExitNodes()
+        return if (raw.isEmpty()) raw else raw.slice(1..2)
+    }
+
+    fun prepareAndroidVpn(): Intent? {
+        return VpnService.prepare(context)
+    }
+
+    fun setDNSFilter(shouldBlock: Function<DnsPacket?, DnsPacket?>?) {
+        OrbotService.shouldBlock = shouldBlock
+    }
+
+    override fun start(): Boolean {
+        val enableNotification = OrbotService.shouldBlock != null
+        Prefs.enableNotification(enableNotification)
+        Prefs.putUseVpn(true)
+        Prefs.putStartOnBoot(true)
+
+        sendIntentToService(OrbotConstants.ACTION_START)
+        sendIntentToService(OrbotConstants.ACTION_START_VPN)
+        return true
+    }
+
+    override fun stop(): Boolean {
+        if (!isServiceRunning()) return false
+
+        updateStatus(Status.STOPPING)
+
+        Prefs.putUseVpn(false)
+        Prefs.putStartOnBoot(false)
+
+        sendIntentToService(OrbotConstants.ACTION_STOP_VPN)
+        sendIntentToService(
+            action = OrbotConstants.ACTION_STOP,
+            extra = Bundle().apply { putBoolean(ACTION_STOP_FOREGROUND_TASK, true) }
+        )
+        stoppingWatchdog(5)
+        return true
+    }
+
+    private fun stoppingWatchdog(countDown: Int) {
+        Handler(Looper.getMainLooper()).postDelayed(
+            {
+                if (isServiceRunning() && countDown > 0) {
+                    stoppingWatchdog(countDown - 1)
+                } else {
+                    updateStatus(Status.OFF, force = true)
+                }
+            },
+            500
+        )
+    }
+
+    fun requestStatus() {
+        if (isServiceRunning()) {
+            sendIntentToService(OrbotConstants.ACTION_STATUS)
+        } else {
+            updateStatus(Status.OFF, force = true)
+        }
+    }
+
+    var appList: Set<String>
+        get() = getTorifiedApps()
+        set(value) = saveTorifiedApps(value)
+
+    fun getAvailablesLocations(): Set<String> = EXIT_COUNTRY_CODES
+
+    var httpProxyPort: Int = -1
+        private set
+
+    var socksProxyPort: Int = -1
+        private set
+
+    fun onCleared() {
+        LocalBroadcastManager.getInstance(context).unregisterReceiver(localBroadcastReceiver)
+    }
+}
diff --git a/permissionse/.gitignore b/permissionse/.gitignore
deleted file mode 100644
index 42afabf..0000000
--- a/permissionse/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/permissionse/build.gradle b/permissionse/build.gradle
deleted file mode 100644
index 7b6ff48..0000000
--- a/permissionse/build.gradle
+++ /dev/null
@@ -1,32 +0,0 @@
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
-
-android {
-    compileSdkVersion buildConfig.compileSdk
-
-    defaultConfig {
-        minSdkVersion buildConfig.minSdk
-        targetSdkVersion buildConfig.targetSdk
-
-        consumerProguardFiles "consumer-rules.pro"
-    }
-
-    buildTypes {
-        release {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-        }
-    }
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_8
-        targetCompatibility JavaVersion.VERSION_1_8
-    }
-}
-
-dependencies {
-    compileOnly project(':permissionse:libs:hidden-apis-stub')
-
-    implementation(libs.bundles.kotlin.android.coroutines)
-    implementation project(':core')
-
-}
diff --git a/permissionse/consumer-rules.pro b/permissionse/consumer-rules.pro
deleted file mode 100644
index e69de29..0000000
diff --git a/permissionse/libs/hidden-apis-stub/.gitignore b/permissionse/libs/hidden-apis-stub/.gitignore
deleted file mode 100644
index 42afabf..0000000
--- a/permissionse/libs/hidden-apis-stub/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/permissionse/libs/hidden-apis-stub/build.gradle b/permissionse/libs/hidden-apis-stub/build.gradle
deleted file mode 100644
index 2043edc..0000000
--- a/permissionse/libs/hidden-apis-stub/build.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-plugins {
-    id 'com.android.library'
-}
-
-android {
-    compileSdkVersion buildConfig.compileSdk
-}
-
-
-java {
-    sourceCompatibility = JavaVersion.VERSION_1_7
-    targetCompatibility = JavaVersion.VERSION_1_7
-}
-
-dependencies {
-    implementation 'org.jetbrains:annotations:15.0'
-    implementation 'androidx.annotation:annotation:1.5.0'
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/AndroidManifest.xml b/permissionse/libs/hidden-apis-stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 61f315a..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="foundation.e.advancedprivacy.hidden.apis"
-    tools:ignore="ProtectedPermissions">
-
-</manifest>
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java
deleted file mode 100644
index 753b456..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.app;
-
-import android.annotation.TargetApi;
-
-import androidx.annotation.DeprecatedSinceApi;
-import androidx.annotation.NonNull;
-
-// Stub based on:
-// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/app/AppOpsManager.java
-public class AppOpsManager {
-
-    public static final int OP_NONE = -1;
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    public static int strOpToOp(@NonNull String op) {
-        return 0;
-    }
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    public void setMode(int code, int uid, String packageName, int mode) {}
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java
deleted file mode 100644
index 9e8d65a..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2022 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.app;
-
-import android.annotation.TargetApi;
-
-import androidx.annotation.DeprecatedSinceApi;
-
-public class NotificationChannel {
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(api = 30, message = "Use setBlockable() instead.")
-    public void setBlockableSystem(boolean blockableSystem) {}
-
-    // Public in API 33.
-    @TargetApi(30)
-    public void setBlockable(boolean blockableSystem) {}
-
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java
deleted file mode 100644
index c6232ce..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2022 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.content.pm;
-
-import android.annotation.TargetApi;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-import androidx.annotation.DeprecatedSinceApi;
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresPermission;
-
-import java.util.List;
-import android.util.AndroidException;
-
-// Stub based on:
-// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/content/pm/PackageManager.java
-public abstract class PackageManager {
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check availability in SDK34"
-    )
-    public static class NameNotFoundException extends AndroidException {
-        public NameNotFoundException() {
-        }
-
-        public NameNotFoundException(String name) {
-            super(name);
-        }
-    }
-
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS")
-    public abstract void grantRuntimePermission(
-        @NonNull String packageName,
-        @NonNull String permissionName,
-        @NonNull UserHandle user
-    );
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS")
-    public abstract void revokeRuntimePermission(
-        @NonNull String packageName,
-        @NonNull String permissionName,
-        @NonNull UserHandle user
-    );
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-        api = 33,
-        message = "@deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} instead."
-    )
-    public abstract ApplicationInfo getApplicationInfoAsUser(
-        @NonNull String packageName,
-        int flags,
-        int userId
-    ) throws NameNotFoundException;
-
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
-    public abstract List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId);
-
-    // Public
-    public abstract List<PackageInfo> getInstalledPackages(int flags);
-
-    @NonNull
-    public abstract Drawable getUserBadgedIcon(
-        @NonNull Drawable drawable,
-        @NonNull UserHandle user
-    );
-
-    public static final int GET_PERMISSIONS = 0x00001000;
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
deleted file mode 100644
index 28a3732..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2022 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.content.pm;
-
-import android.annotation.TargetApi;
-import android.os.UserHandle;
-
-import androidx.annotation.DeprecatedSinceApi;
-
-public class UserInfo {
-    public int id;
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check availability in SDK34"
-    )
-    public UserHandle getUserHandle() {
-        return null;
-    }
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java
deleted file mode 100644
index 53440e0..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.net;
-
-import android.annotation.TargetApi;
-
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import androidx.annotation.DeprecatedSinceApi;
-
-// Stub based on:
-// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/net/IConnectivityManager.java
-public interface IConnectivityManager {
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 31,
-            message = "Moved to android.net.VpnManager"
-    )
-    boolean prepareVpn(String oldPackage, String newPackage, int userId) throws RemoteException;
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-        api = 30,
-        message = "Use instead setVpnPackageAuthorization(String packageName, int userId, int vpnType)"
-    )
-    void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) throws RemoteException;
-
-    @TargetApi(30)
-    @DeprecatedSinceApi(
-            api = 31,
-            message = "Moved to android.net.VpnManager"
-    )
-    void setVpnPackageAuthorization(String packageName, int userId, int vpnType) throws RemoteException;
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 31,
-            message = "Moved to android.net.VpnManager"
-    )
-    public String getAlwaysOnVpnPackage(int userId) throws RemoteException;
-
-    public abstract static class Stub extends Binder implements IConnectivityManager {
-        public static IConnectivityManager asInterface(IBinder obj) {
-            return null;
-        }
-    }
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java
deleted file mode 100644
index dab2173..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.net;
-
-import android.annotation.TargetApi;
-
-import androidx.annotation.DeprecatedSinceApi;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresPermission;
-
-// Stub based on:
-// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/net/VpnManager.java
-public class VpnManager {
-    public static final int TYPE_VPN_SERVICE = 1;
-
-    @TargetApi(31)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    public boolean prepareVpn(
-        @Nullable String oldPackage,
-        @Nullable String newPackage,
-        int userId
-    ) {
-        return true;
-    }
-
-    @TargetApi(31)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    public void setVpnPackageAuthorization(
-            String packageName,
-            int userId,
-            int vpnType
-    ) {}
-
-    @TargetApi(31)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check disponibility in SDK34"
-    )
-    @RequiresPermission("android.permission.CONTROL_ALWAYS_ON_VPN")
-    public String getAlwaysOnVpnPackageForUser(int userId) {
-        return null;
-    }
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java
deleted file mode 100644
index 4696b79..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.os;
-
-public class ServiceManager {
-    public static IBinder getService(String name) {
-        return null;
-    }
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java
deleted file mode 100644
index df56daf..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.os;
-
-public class UserHandle {
-    public static /*@UserIdInt*/ int myUserId() {
-        return 0;
-    }
-}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
deleted file mode 100644
index be6797e..0000000
--- a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2022 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-
-package android.os;
-
-import android.annotation.TargetApi;
-import android.content.pm.UserInfo;
-
-import androidx.annotation.DeprecatedSinceApi;
-import androidx.annotation.RequiresPermission;
-import java.util.List;
-
-public class UserManager {
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check availability in SDK34"
-    )
-    @RequiresPermission("android.permission.MANAGE_USERS")
-    public List<UserInfo> getProfiles(int userHandle) {
-        return null;
-    }
-
-    @TargetApi(29)
-    @DeprecatedSinceApi(
-            api = 34,
-            message = "Check availability in SDK34"
-    )
-    @RequiresPermission("android.permission.MANAGE_USERS")
-    public boolean isManagedProfile(int userId) {
-        return false;
-    }
-}
diff --git a/permissionse/proguard-rules.pro b/permissionse/proguard-rules.pro
deleted file mode 100644
index 481bb43..0000000
--- a/permissionse/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/permissionse/src/main/AndroidManifest.xml b/permissionse/src/main/AndroidManifest.xml
deleted file mode 100644
index 4766007..0000000
--- a/permissionse/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<!--
-  ~ Copyright  (C) 2023 MURENA SAS
-  ~ Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="foundation.e.advancedprivacy.permissions.e">
-
-    <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"
-        tools:ignore="ProtectedPermissions" />
-    <uses-permission
-        android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
-        tools:ignore="ProtectedPermissions"
-        />
-    <uses-permission
-        android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
-        tools:ignore="ProtectedPermissions"
-        />
-
-    <!-- The following permission have privileged protection level.
-        These permissions are granted only if the app is privileged by the system,
-         like installed in /system/priv-app and a privapp-permissions in /system/etc/permissions/
-        (see eprivavymoduledemo for an example) -->
-    <uses-permission
-        android:name="android.permission.UPDATE_APP_OPS_STATS"
-        tools:ignore="ProtectedPermissions"
-        />
-    <uses-permission android:name="android.permission.WATCH_APPOPS"
-        tools:ignore="ProtectedPermissions"
-        />
-    <uses-permission
-        android:name="android.permission.GET_APP_OPS_STATS"
-        tools:ignore="ProtectedPermissions"
-        />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
-        tools:ignore="ProtectedPermissions"
-        />
-    <uses-permission android:name="android.permission.CONTROL_VPN"
-        tools:ignore="ProtectedPermissions" />
-    <uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN"
-        tools:ignore="ProtectedPermissions" />
-    <uses-permission android:name="android.permission.MANAGE_USERS"
-        tools:ignore="ProtectedPermissions" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
-        tools:ignore="ProtectedPermissions" />
-</manifest>
diff --git a/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
deleted file mode 100644
index 0d32bce..0000000
--- a/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.advancedprivacy.permissions.externalinterfaces
-
-import android.annotation.TargetApi
-import android.app.AppOpsManager
-import android.app.AppOpsManager.OP_NONE
-import android.app.AppOpsManager.strOpToOp
-import android.app.NotificationChannel
-import android.content.Context
-import android.content.pm.PackageInfo
-import android.content.pm.PackageManager
-import android.content.pm.UserInfo
-import android.graphics.drawable.Drawable
-import android.net.IConnectivityManager
-import android.net.VpnManager
-import android.net.VpnManager.TYPE_VPN_SERVICE
-import android.os.Build
-import android.os.ServiceManager
-import android.os.UserHandle
-import android.os.UserManager
-import android.util.Log
-import foundation.e.advancedprivacy.domain.entities.AppOpModes
-import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.ProfileType.MAIN
-import foundation.e.advancedprivacy.domain.entities.ProfileType.WORK
-import foundation.e.advancedprivacy.externalinterfaces.permissions.PermissionsPrivacyModuleBase
-
-/**
- * Implements [IPermissionsPrivacyModule] with all privileges of a system app.
- */
-class PermissionsPrivacyModuleImpl(context: Context) : PermissionsPrivacyModuleBase(context) {
-
-    private val appOpsManager: AppOpsManager
-        get() = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
-
-    /**
-     * @see IPermissionsPrivacyModule.toggleDangerousPermission
-     * Always return true, permission is set using privileged capacities.
-     */
-    override fun toggleDangerousPermission(
-        appDesc: ApplicationDescription,
-        permissionName: String,
-        grant: Boolean
-    ): Boolean {
-        try {
-            if (grant) {
-                context.packageManager.grantRuntimePermission(
-                    appDesc.packageName,
-                    permissionName,
-                    android.os.Process.myUserHandle()
-                )
-            } else {
-                context.packageManager.revokeRuntimePermission(
-                    appDesc.packageName,
-                    permissionName,
-                    android.os.Process.myUserHandle()
-                )
-            }
-        } catch (e: Exception) {
-            Log.e("Permissions-e", "Exception while setting permission", e)
-            return false
-        }
-
-        return true
-    }
-
-    override fun setAppOpMode(
-        appDesc: ApplicationDescription,
-        appOpPermissionName: String,
-        status: AppOpModes
-    ): Boolean {
-        val op = strOpToOp(appOpPermissionName)
-        if (op != OP_NONE) {
-            appOpsManager.setMode(op, appDesc.uid, appDesc.packageName, status.modeValue)
-        }
-        return true
-    }
-
-    override fun getApplications(
-        filter: ((PackageInfo) -> Boolean)?
-    ): List<ApplicationDescription> {
-        val pm = context.packageManager
-        val mainUserId = UserHandle.myUserId()
-        val workProfileId = getWorkProfile()?.id
-
-        val userIds = listOf(mainUserId, workProfileId).filterNotNull()
-        return userIds.map { profileId ->
-            pm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, profileId)
-                .filter { filter?.invoke(it) ?: true }
-                .map {
-                    buildApplicationDescription(
-                        appInfo = it.applicationInfo,
-                        profileId = profileId,
-                        profileType = if (profileId == mainUserId) MAIN else WORK
-                    )
-                }
-        }.flatten()
-    }
-
-    override fun getApplicationIcon(app: ApplicationDescription): Drawable? {
-        return if (app.profileType == WORK) {
-            getWorkProfile()?.let { workProfile ->
-                val pm = context.packageManager
-                getApplicationIcon(
-                    pm.getApplicationInfoAsUser(app.packageName, 0, workProfile.id)
-                )?.let {
-                    pm.getUserBadgedIcon(it, workProfile.getUserHandle())
-                }
-            }
-        } else getApplicationIcon(app.packageName)
-    }
-
-    override fun setBlockable(notificationChannel: NotificationChannel) {
-        when (Build.VERSION.SDK_INT) {
-            29 -> notificationChannel.setBlockableSystem(true)
-            30, 31, 32, 33 -> notificationChannel.setBlockable(true)
-            else -> {
-                Log.e("Permissions-e", "Bad android sdk version")
-            }
-        }
-    }
-
-    override fun setVpnPackageAuthorization(packageName: String): Boolean {
-        return when (Build.VERSION.SDK_INT) {
-            29 -> setVpnPackageAuthorizationSDK29(packageName)
-            30 -> setVpnPackageAuthorizationSDK30(packageName)
-            31, 32, 33 -> setVpnPackageAuthorizationSDK32(packageName)
-            else -> {
-                Log.e("Permissions-e", "Bad android sdk version")
-                false
-            }
-        }
-    }
-
-    @TargetApi(29)
-    private fun setVpnPackageAuthorizationSDK29(packageName: String): Boolean {
-        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
-            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
-        )
-
-        try {
-            if (service.prepareVpn(null, packageName, UserHandle.myUserId())) {
-                // Authorize this app to initiate VPN connections in the future without user
-                // intervention.
-                service.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), true)
-                return true
-            }
-        } catch (e: java.lang.Exception) {
-            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
-        } catch (e: NoSuchMethodError) {
-            Log.e("Permissions-e", "Bad android sdk version", e)
-        }
-        return false
-    }
-
-    @TargetApi(30)
-    private fun setVpnPackageAuthorizationSDK30(packageName: String): Boolean {
-        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
-            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
-        )
-
-        try {
-            if (service.prepareVpn(null, packageName, UserHandle.myUserId())) {
-                // Authorize this app to initiate VPN connections in the future without user
-                // intervention.
-                service.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), TYPE_VPN_SERVICE)
-                return true
-            }
-        } catch (e: java.lang.Exception) {
-            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
-        } catch (e: NoSuchMethodError) {
-            Log.e("Permissions-e", "Bad android sdk version", e)
-        }
-        return false
-    }
-
-    @TargetApi(31)
-    private fun setVpnPackageAuthorizationSDK32(packageName: String): Boolean {
-        val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager
-
-        try {
-            if (vpnManager.prepareVpn(null, packageName, UserHandle.myUserId())) {
-                // Authorize this app to initiate VPN connections in the future without user
-                // intervention.
-                vpnManager.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), TYPE_VPN_SERVICE)
-                return true
-            }
-        } catch (e: java.lang.Exception) {
-            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
-        } catch (e: NoSuchMethodError) {
-            Log.e("Permissions-e", "Bad android sdk version", e)
-        }
-        return false
-    }
-
-    override fun getAlwaysOnVpnPackage(): String? {
-        return when (Build.VERSION.SDK_INT) {
-            29, 30 -> getAlwaysOnVpnPackageSDK29()
-            31, 32, 33 -> getAlwaysOnVpnPackageSDK32()
-            else -> {
-                Log.e("Permissions-e", "Bad android sdk version")
-                null
-            }
-        }
-    }
-
-    @TargetApi(29)
-    private fun getAlwaysOnVpnPackageSDK29(): String? {
-        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
-            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
-        )
-
-        return try {
-            service.getAlwaysOnVpnPackage(UserHandle.myUserId())
-        } catch (e: java.lang.Exception) {
-            Log.e("Permissions-e", "Bad android sdk version ", e)
-            return null
-        }
-    }
-
-    @TargetApi(31)
-    private fun getAlwaysOnVpnPackageSDK32(): String? {
-        val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager
-        return try {
-            vpnManager.getAlwaysOnVpnPackageForUser(UserHandle.myUserId())
-        } catch (e: java.lang.Exception) {
-            Log.e("Permissions-e", "Bad android sdk version ", e)
-            return null
-        }
-    }
-
-    private fun getWorkProfile(): UserInfo? {
-        val userManager: UserManager = context.getSystemService(UserManager::class.java)
-        val userId = UserHandle.myUserId()
-        for (user in userManager.getProfiles(UserHandle.myUserId())) {
-            if (user.id != userId && userManager.isManagedProfile(user.id)) {
-                return user
-            }
-        }
-        return null
-    }
-}
diff --git a/permissionseos/.gitignore b/permissionseos/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/permissionseos/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/permissionseos/build.gradle b/permissionseos/build.gradle
new file mode 100644
index 0000000..d57ea9c
--- /dev/null
+++ b/permissionseos/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+    compileSdkVersion buildConfig.compileSdk
+
+    defaultConfig {
+        minSdkVersion buildConfig.minSdk
+        targetSdkVersion buildConfig.targetSdk
+
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+}
+
+dependencies {
+    compileOnly project(':permissionseos:libs:hidden-apis-stub')
+
+    implementation(libs.bundles.kotlin.android.coroutines)
+    implementation project(':core')
+
+}
diff --git a/permissionseos/consumer-rules.pro b/permissionseos/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/permissionseos/libs/hidden-apis-stub/.gitignore b/permissionseos/libs/hidden-apis-stub/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/permissionseos/libs/hidden-apis-stub/build.gradle b/permissionseos/libs/hidden-apis-stub/build.gradle
new file mode 100644
index 0000000..2043edc
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+plugins {
+    id 'com.android.library'
+}
+
+android {
+    compileSdkVersion buildConfig.compileSdk
+}
+
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_7
+    targetCompatibility = JavaVersion.VERSION_1_7
+}
+
+dependencies {
+    implementation 'org.jetbrains:annotations:15.0'
+    implementation 'androidx.annotation:annotation:1.5.0'
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/AndroidManifest.xml b/permissionseos/libs/hidden-apis-stub/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..61f315a
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="foundation.e.advancedprivacy.hidden.apis"
+    tools:ignore="ProtectedPermissions">
+
+</manifest>
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java
new file mode 100644
index 0000000..753b456
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/app/AppOpsManager.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.app;
+
+import android.annotation.TargetApi;
+
+import androidx.annotation.DeprecatedSinceApi;
+import androidx.annotation.NonNull;
+
+// Stub based on:
+// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/app/AppOpsManager.java
+public class AppOpsManager {
+
+    public static final int OP_NONE = -1;
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    public static int strOpToOp(@NonNull String op) {
+        return 0;
+    }
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    public void setMode(int code, int uid, String packageName, int mode) {}
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java
new file mode 100644
index 0000000..9e8d65a
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/app/NotificationChannel.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.app;
+
+import android.annotation.TargetApi;
+
+import androidx.annotation.DeprecatedSinceApi;
+
+public class NotificationChannel {
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(api = 30, message = "Use setBlockable() instead.")
+    public void setBlockableSystem(boolean blockableSystem) {}
+
+    // Public in API 33.
+    @TargetApi(30)
+    public void setBlockable(boolean blockableSystem) {}
+
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java
new file mode 100644
index 0000000..c6232ce
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.content.pm;
+
+import android.annotation.TargetApi;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+
+import androidx.annotation.DeprecatedSinceApi;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresPermission;
+
+import java.util.List;
+import android.util.AndroidException;
+
+// Stub based on:
+// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/content/pm/PackageManager.java
+public abstract class PackageManager {
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check availability in SDK34"
+    )
+    public static class NameNotFoundException extends AndroidException {
+        public NameNotFoundException() {
+        }
+
+        public NameNotFoundException(String name) {
+            super(name);
+        }
+    }
+
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS")
+    public abstract void grantRuntimePermission(
+        @NonNull String packageName,
+        @NonNull String permissionName,
+        @NonNull UserHandle user
+    );
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS")
+    public abstract void revokeRuntimePermission(
+        @NonNull String packageName,
+        @NonNull String permissionName,
+        @NonNull UserHandle user
+    );
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+        api = 33,
+        message = "@deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} instead."
+    )
+    public abstract ApplicationInfo getApplicationInfoAsUser(
+        @NonNull String packageName,
+        int flags,
+        int userId
+    ) throws NameNotFoundException;
+
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
+    public abstract List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId);
+
+    // Public
+    public abstract List<PackageInfo> getInstalledPackages(int flags);
+
+    @NonNull
+    public abstract Drawable getUserBadgedIcon(
+        @NonNull Drawable drawable,
+        @NonNull UserHandle user
+    );
+
+    public static final int GET_PERMISSIONS = 0x00001000;
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
new file mode 100644
index 0000000..28a3732
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.content.pm;
+
+import android.annotation.TargetApi;
+import android.os.UserHandle;
+
+import androidx.annotation.DeprecatedSinceApi;
+
+public class UserInfo {
+    public int id;
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check availability in SDK34"
+    )
+    public UserHandle getUserHandle() {
+        return null;
+    }
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java
new file mode 100644
index 0000000..53440e0
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.net;
+
+import android.annotation.TargetApi;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.annotation.DeprecatedSinceApi;
+
+// Stub based on:
+// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/net/IConnectivityManager.java
+public interface IConnectivityManager {
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 31,
+            message = "Moved to android.net.VpnManager"
+    )
+    boolean prepareVpn(String oldPackage, String newPackage, int userId) throws RemoteException;
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+        api = 30,
+        message = "Use instead setVpnPackageAuthorization(String packageName, int userId, int vpnType)"
+    )
+    void setVpnPackageAuthorization(String packageName, int userId, boolean authorized) throws RemoteException;
+
+    @TargetApi(30)
+    @DeprecatedSinceApi(
+            api = 31,
+            message = "Moved to android.net.VpnManager"
+    )
+    void setVpnPackageAuthorization(String packageName, int userId, int vpnType) throws RemoteException;
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 31,
+            message = "Moved to android.net.VpnManager"
+    )
+    public String getAlwaysOnVpnPackage(int userId) throws RemoteException;
+
+    public abstract static class Stub extends Binder implements IConnectivityManager {
+        public static IConnectivityManager asInterface(IBinder obj) {
+            return null;
+        }
+    }
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java
new file mode 100644
index 0000000..dab2173
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.net;
+
+import android.annotation.TargetApi;
+
+import androidx.annotation.DeprecatedSinceApi;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresPermission;
+
+// Stub based on:
+// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/net/VpnManager.java
+public class VpnManager {
+    public static final int TYPE_VPN_SERVICE = 1;
+
+    @TargetApi(31)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    public boolean prepareVpn(
+        @Nullable String oldPackage,
+        @Nullable String newPackage,
+        int userId
+    ) {
+        return true;
+    }
+
+    @TargetApi(31)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    public void setVpnPackageAuthorization(
+            String packageName,
+            int userId,
+            int vpnType
+    ) {}
+
+    @TargetApi(31)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check disponibility in SDK34"
+    )
+    @RequiresPermission("android.permission.CONTROL_ALWAYS_ON_VPN")
+    public String getAlwaysOnVpnPackageForUser(int userId) {
+        return null;
+    }
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java
new file mode 100644
index 0000000..4696b79
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/ServiceManager.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.os;
+
+public class ServiceManager {
+    public static IBinder getService(String name) {
+        return null;
+    }
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java
new file mode 100644
index 0000000..df56daf
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserHandle.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.os;
+
+public class UserHandle {
+    public static /*@UserIdInt*/ int myUserId() {
+        return 0;
+    }
+}
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
new file mode 100644
index 0000000..be6797e
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package android.os;
+
+import android.annotation.TargetApi;
+import android.content.pm.UserInfo;
+
+import androidx.annotation.DeprecatedSinceApi;
+import androidx.annotation.RequiresPermission;
+import java.util.List;
+
+public class UserManager {
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check availability in SDK34"
+    )
+    @RequiresPermission("android.permission.MANAGE_USERS")
+    public List<UserInfo> getProfiles(int userHandle) {
+        return null;
+    }
+
+    @TargetApi(29)
+    @DeprecatedSinceApi(
+            api = 34,
+            message = "Check availability in SDK34"
+    )
+    @RequiresPermission("android.permission.MANAGE_USERS")
+    public boolean isManagedProfile(int userId) {
+        return false;
+    }
+}
diff --git a/permissionseos/proguard-rules.pro b/permissionseos/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/permissionseos/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/permissionseos/src/main/AndroidManifest.xml b/permissionseos/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ed25c1c
--- /dev/null
+++ b/permissionseos/src/main/AndroidManifest.xml
@@ -0,0 +1,59 @@
+<!--
+  ~ Copyright  (C) 2023 MURENA SAS
+  ~ Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="foundation.e.advancedprivacy.permissions.eos">
+
+    <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission
+        android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
+        tools:ignore="ProtectedPermissions"
+        />
+    <uses-permission
+        android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
+        tools:ignore="ProtectedPermissions"
+        />
+
+    <!-- The following permission have privileged protection level.
+        These permissions are granted only if the app is privileged by the system,
+         like installed in /system/priv-app and a privapp-permissions in /system/etc/permissions/
+        (see eprivavymoduledemo for an example) -->
+    <uses-permission
+        android:name="android.permission.UPDATE_APP_OPS_STATS"
+        tools:ignore="ProtectedPermissions"
+        />
+    <uses-permission android:name="android.permission.WATCH_APPOPS"
+        tools:ignore="ProtectedPermissions"
+        />
+    <uses-permission
+        android:name="android.permission.GET_APP_OPS_STATS"
+        tools:ignore="ProtectedPermissions"
+        />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+        tools:ignore="ProtectedPermissions"
+        />
+    <uses-permission android:name="android.permission.CONTROL_VPN"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission android:name="android.permission.MANAGE_USERS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
+        tools:ignore="ProtectedPermissions" />
+</manifest>
diff --git a/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt b/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
new file mode 100644
index 0000000..0d32bce
--- /dev/null
+++ b/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.advancedprivacy.permissions.externalinterfaces
+
+import android.annotation.TargetApi
+import android.app.AppOpsManager
+import android.app.AppOpsManager.OP_NONE
+import android.app.AppOpsManager.strOpToOp
+import android.app.NotificationChannel
+import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.UserInfo
+import android.graphics.drawable.Drawable
+import android.net.IConnectivityManager
+import android.net.VpnManager
+import android.net.VpnManager.TYPE_VPN_SERVICE
+import android.os.Build
+import android.os.ServiceManager
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import foundation.e.advancedprivacy.domain.entities.AppOpModes
+import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.entities.ProfileType.MAIN
+import foundation.e.advancedprivacy.domain.entities.ProfileType.WORK
+import foundation.e.advancedprivacy.externalinterfaces.permissions.PermissionsPrivacyModuleBase
+
+/**
+ * Implements [IPermissionsPrivacyModule] with all privileges of a system app.
+ */
+class PermissionsPrivacyModuleImpl(context: Context) : PermissionsPrivacyModuleBase(context) {
+
+    private val appOpsManager: AppOpsManager
+        get() = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
+
+    /**
+     * @see IPermissionsPrivacyModule.toggleDangerousPermission
+     * Always return true, permission is set using privileged capacities.
+     */
+    override fun toggleDangerousPermission(
+        appDesc: ApplicationDescription,
+        permissionName: String,
+        grant: Boolean
+    ): Boolean {
+        try {
+            if (grant) {
+                context.packageManager.grantRuntimePermission(
+                    appDesc.packageName,
+                    permissionName,
+                    android.os.Process.myUserHandle()
+                )
+            } else {
+                context.packageManager.revokeRuntimePermission(
+                    appDesc.packageName,
+                    permissionName,
+                    android.os.Process.myUserHandle()
+                )
+            }
+        } catch (e: Exception) {
+            Log.e("Permissions-e", "Exception while setting permission", e)
+            return false
+        }
+
+        return true
+    }
+
+    override fun setAppOpMode(
+        appDesc: ApplicationDescription,
+        appOpPermissionName: String,
+        status: AppOpModes
+    ): Boolean {
+        val op = strOpToOp(appOpPermissionName)
+        if (op != OP_NONE) {
+            appOpsManager.setMode(op, appDesc.uid, appDesc.packageName, status.modeValue)
+        }
+        return true
+    }
+
+    override fun getApplications(
+        filter: ((PackageInfo) -> Boolean)?
+    ): List<ApplicationDescription> {
+        val pm = context.packageManager
+        val mainUserId = UserHandle.myUserId()
+        val workProfileId = getWorkProfile()?.id
+
+        val userIds = listOf(mainUserId, workProfileId).filterNotNull()
+        return userIds.map { profileId ->
+            pm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, profileId)
+                .filter { filter?.invoke(it) ?: true }
+                .map {
+                    buildApplicationDescription(
+                        appInfo = it.applicationInfo,
+                        profileId = profileId,
+                        profileType = if (profileId == mainUserId) MAIN else WORK
+                    )
+                }
+        }.flatten()
+    }
+
+    override fun getApplicationIcon(app: ApplicationDescription): Drawable? {
+        return if (app.profileType == WORK) {
+            getWorkProfile()?.let { workProfile ->
+                val pm = context.packageManager
+                getApplicationIcon(
+                    pm.getApplicationInfoAsUser(app.packageName, 0, workProfile.id)
+                )?.let {
+                    pm.getUserBadgedIcon(it, workProfile.getUserHandle())
+                }
+            }
+        } else getApplicationIcon(app.packageName)
+    }
+
+    override fun setBlockable(notificationChannel: NotificationChannel) {
+        when (Build.VERSION.SDK_INT) {
+            29 -> notificationChannel.setBlockableSystem(true)
+            30, 31, 32, 33 -> notificationChannel.setBlockable(true)
+            else -> {
+                Log.e("Permissions-e", "Bad android sdk version")
+            }
+        }
+    }
+
+    override fun setVpnPackageAuthorization(packageName: String): Boolean {
+        return when (Build.VERSION.SDK_INT) {
+            29 -> setVpnPackageAuthorizationSDK29(packageName)
+            30 -> setVpnPackageAuthorizationSDK30(packageName)
+            31, 32, 33 -> setVpnPackageAuthorizationSDK32(packageName)
+            else -> {
+                Log.e("Permissions-e", "Bad android sdk version")
+                false
+            }
+        }
+    }
+
+    @TargetApi(29)
+    private fun setVpnPackageAuthorizationSDK29(packageName: String): Boolean {
+        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
+        )
+
+        try {
+            if (service.prepareVpn(null, packageName, UserHandle.myUserId())) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                service.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), true)
+                return true
+            }
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
+        } catch (e: NoSuchMethodError) {
+            Log.e("Permissions-e", "Bad android sdk version", e)
+        }
+        return false
+    }
+
+    @TargetApi(30)
+    private fun setVpnPackageAuthorizationSDK30(packageName: String): Boolean {
+        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
+        )
+
+        try {
+            if (service.prepareVpn(null, packageName, UserHandle.myUserId())) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                service.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), TYPE_VPN_SERVICE)
+                return true
+            }
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
+        } catch (e: NoSuchMethodError) {
+            Log.e("Permissions-e", "Bad android sdk version", e)
+        }
+        return false
+    }
+
+    @TargetApi(31)
+    private fun setVpnPackageAuthorizationSDK32(packageName: String): Boolean {
+        val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager
+
+        try {
+            if (vpnManager.prepareVpn(null, packageName, UserHandle.myUserId())) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                vpnManager.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), TYPE_VPN_SERVICE)
+                return true
+            }
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
+        } catch (e: NoSuchMethodError) {
+            Log.e("Permissions-e", "Bad android sdk version", e)
+        }
+        return false
+    }
+
+    override fun getAlwaysOnVpnPackage(): String? {
+        return when (Build.VERSION.SDK_INT) {
+            29, 30 -> getAlwaysOnVpnPackageSDK29()
+            31, 32, 33 -> getAlwaysOnVpnPackageSDK32()
+            else -> {
+                Log.e("Permissions-e", "Bad android sdk version")
+                null
+            }
+        }
+    }
+
+    @TargetApi(29)
+    private fun getAlwaysOnVpnPackageSDK29(): String? {
+        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
+        )
+
+        return try {
+            service.getAlwaysOnVpnPackage(UserHandle.myUserId())
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Bad android sdk version ", e)
+            return null
+        }
+    }
+
+    @TargetApi(31)
+    private fun getAlwaysOnVpnPackageSDK32(): String? {
+        val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager
+        return try {
+            vpnManager.getAlwaysOnVpnPackageForUser(UserHandle.myUserId())
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Bad android sdk version ", e)
+            return null
+        }
+    }
+
+    private fun getWorkProfile(): UserInfo? {
+        val userManager: UserManager = context.getSystemService(UserManager::class.java)
+        val userId = UserHandle.myUserId()
+        for (user in userManager.getProfiles(UserHandle.myUserId())) {
+            if (user.id != userId && userManager.isManagedProfile(user.id)) {
+                return user
+            }
+        }
+        return null
+    }
+}
diff --git a/settings.gradle b/settings.gradle
index a25c6a8..7eadbc9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,12 +15,12 @@ include ':fakelocation:fakelocationdemo'
 include ':core'
 include ':permissionsstandalone'
 include ':trackers'
-include ':permissionse'
-include ':permissionse:libs:hidden-apis-stub'
+include ':permissionseos'
+include ':permissionseos:libs:hidden-apis-stub'
 include ':ipscrambling'
 include ':ipscrambling:orbotservice'
 include ':trackersservicestandalone'
-include ':trackersservicee'
+include ':trackersserviceeos'
 
 dependencyResolutionManagement {
     repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
deleted file mode 100644
index 79f721b..0000000
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-package foundation.e.advancedprivacy.trackers.domain.externalinterfaces
-
-import org.pcap4j.packet.DnsPacket
-import java.util.function.Function
-
-interface TrackersServiceSupervisor {
-    fun start(): Boolean
-    fun stop(): Boolean
-    fun isRunning(): Boolean
-
-    val dnsFilterForIpScrambling: Function<DnsPacket?, DnsPacket?>?
-}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersSupervisor.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersSupervisor.kt
new file mode 100644
index 0000000..a0c7935
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersSupervisor.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.trackers.domain.externalinterfaces
+
+import foundation.e.advancedprivacy.externalinterfaces.servicesupervisors.FeatureSupervisor
+import org.pcap4j.packet.DnsPacket
+import java.util.function.Function
+
+interface TrackersSupervisor : FeatureSupervisor {
+    fun isRunning(): Boolean
+
+    val dnsFilterForIpScrambling: Function<DnsPacket?, DnsPacket?>?
+}
diff --git a/trackersservicee/.gitignore b/trackersservicee/.gitignore
deleted file mode 100644
index 42afabf..0000000
--- a/trackersservicee/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/trackersservicee/build.gradle b/trackersservicee/build.gradle
deleted file mode 100644
index e93d5d6..0000000
--- a/trackersservicee/build.gradle
+++ /dev/null
@@ -1,40 +0,0 @@
-plugins {
-    id 'com.android.library'
-    id 'org.jetbrains.kotlin.android'
-}
-
-android {
-    namespace 'foundation.e.advancedprivacy.trackers.service'
-    compileSdkVersion buildConfig.compileSdk
-
-    defaultConfig {
-        minSdkVersion buildConfig.minSdk
-        targetSdkVersion buildConfig.targetSdk
-    }
-
-    buildTypes {
-        release {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-        }
-    }
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_8
-        targetCompatibility JavaVersion.VERSION_1_8
-    }
-    kotlinOptions {
-        jvmTarget = '1.8'
-    }
-}
-
-dependencies {
-    implementation project(":core")
-    implementation project(":trackers")
-
-    implementation(
-        libs.androidx.core.ktx,
-        libs.bundles.koin,
-        libs.kotlinx.coroutines,
-        libs.timber,
-    )
-}
diff --git a/trackersservicee/consumer-rules.pro b/trackersservicee/consumer-rules.pro
deleted file mode 100644
index e69de29..0000000
diff --git a/trackersservicee/proguard-rules.pro b/trackersservicee/proguard-rules.pro
deleted file mode 100644
index 481bb43..0000000
--- a/trackersservicee/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/trackersservicee/src/main/AndroidManifest.xml b/trackersservicee/src/main/AndroidManifest.xml
deleted file mode 100644
index 2290432..0000000
--- a/trackersservicee/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright  (C) 2023 MURENA SAS
-    Copyright  (C) 2022 ECORP
-
-    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 <https://www.gnu.org/licenses/>.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="foundation.e.advancedprivacy.trackers.service">
-
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-
-
-    <application>
-        <service
-            android:name=".TrackersService"
-            android:enabled="true"
-            android:exported="true" />
-    </application>
-</manifest>
\ No newline at end of file
diff --git a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
deleted file mode 100644
index 6a2b218..0000000
--- a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.advancedprivacy.trackers.service
-
-import android.net.LocalServerSocket
-import android.system.ErrnoException
-import android.system.Os
-import android.system.OsConstants
-import foundation.e.advancedprivacy.core.utils.runSuspendCatching
-import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
-import timber.log.Timber
-import java.io.BufferedReader
-import java.io.InputStreamReader
-import java.io.PrintWriter
-
-class DNSBlocker(
-    val filterHostnameUseCase: FilterHostnameUseCase
-) {
-    private var resolverReceiver: LocalServerSocket? = null
-
-    companion object {
-        private const val SOCKET_NAME = "foundation.e.advancedprivacy"
-    }
-
-    private fun closeSocket() {
-        // Known bug and workaround that LocalServerSocket::close is not working well
-        // https://issuetracker.google.com/issues/36945762
-        if (resolverReceiver != null) {
-            try {
-                Os.shutdown(resolverReceiver!!.fileDescriptor, OsConstants.SHUT_RDWR)
-                resolverReceiver!!.close()
-                resolverReceiver = null
-            } catch (e: ErrnoException) {
-                if (e.errno != OsConstants.EBADF) {
-                    Timber.w("Socket already closed")
-                } else {
-                    Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !")
-                }
-            } catch (e: Exception) {
-                Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !")
-            }
-        }
-    }
-
-    fun listenJob(scope: CoroutineScope): Job = scope.launch(Dispatchers.IO) {
-        val resolverReceiver = runSuspendCatching {
-            LocalServerSocket(SOCKET_NAME)
-        }.getOrElse {
-            Timber.e(it, "Exception: cannot open DNS port on $SOCKET_NAME")
-            return@launch
-        }
-
-        this@DNSBlocker.resolverReceiver = resolverReceiver
-        Timber.d("DNSFilterProxy running on port $SOCKET_NAME")
-
-        while (isActive) {
-            runSuspendCatching {
-                val socket = resolverReceiver.accept()
-                val reader = BufferedReader(InputStreamReader(socket.inputStream))
-                val line = reader.readLine()
-                val params = line.split(",").toTypedArray()
-                val output = socket.outputStream
-                val writer = PrintWriter(output, true)
-                val domainName = params[0]
-                val appUid = params[1].toInt()
-                if (filterHostnameUseCase.shouldBlock(domainName, appUid)) {
-                    writer.println("block")
-                } else {
-                    writer.println("pass")
-                }
-                socket.close()
-            }.onFailure {
-                if (it is CancellationException) {
-                    closeSocket()
-                    throw it
-                } else {
-                    Timber.w(it, "Exception while listening DNS resolver")
-                }
-            }
-        }
-    }
-}
diff --git a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
deleted file mode 100644
index 5f573b0..0000000
--- a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
- */
-package foundation.e.advancedprivacy.trackers.service
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-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"
-
-        var coroutineScope = CoroutineScope(Dispatchers.IO)
-    }
-
-    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()
-        }
-        return START_REDELIVER_INTENT
-    }
-
-    private fun start() {
-        coroutineScope = CoroutineScope(Dispatchers.IO)
-        get<DNSBlocker>(DNSBlocker::class.java).apply {
-            filterHostnameUseCase.writeLogJob(coroutineScope)
-            listenJob(coroutineScope)
-        }
-    }
-
-    private fun stop() {
-        kotlin.runCatching { coroutineScope.cancel() }
-    }
-}
diff --git a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
deleted file mode 100644
index dcdf0d4..0000000
--- a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-package foundation.e.advancedprivacy.trackers.service
-
-import android.content.Context
-import android.content.Intent
-import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
-import foundation.e.advancedprivacy.trackers.service.TrackersService.Companion.ACTION_START
-import kotlinx.coroutines.isActive
-import org.koin.core.module.dsl.bind
-import org.koin.core.module.dsl.factoryOf
-import org.koin.core.module.dsl.singleOf
-import org.koin.dsl.module
-
-class TrackersServiceSupervisorImpl(private val context: Context) : TrackersServiceSupervisor {
-
-    override fun start(): Boolean {
-        val intent = Intent(context, TrackersService::class.java)
-        intent.action = ACTION_START
-        return context.startService(intent) != null
-    }
-
-    override fun stop(): Boolean {
-        return context.stopService(Intent(context, TrackersService::class.java))
-    }
-
-    override fun isRunning(): Boolean {
-        return TrackersService.coroutineScope.isActive
-    }
-
-    override val dnsFilterForIpScrambling = null
-}
-
-val trackerServiceModule = module {
-    factoryOf(::DNSBlocker)
-    singleOf(::TrackersServiceSupervisorImpl) {
-        bind<TrackersServiceSupervisor>()
-    }
-}
diff --git a/trackersserviceeos/.gitignore b/trackersserviceeos/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/trackersserviceeos/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/trackersserviceeos/build.gradle b/trackersserviceeos/build.gradle
new file mode 100644
index 0000000..d9f80af
--- /dev/null
+++ b/trackersserviceeos/build.gradle
@@ -0,0 +1,41 @@
+plugins {
+    id 'com.android.library'
+    id 'org.jetbrains.kotlin.android'
+}
+
+android {
+    namespace 'foundation.e.advancedprivacy.trackers.service'
+    compileSdkVersion buildConfig.compileSdk
+
+    defaultConfig {
+        minSdkVersion buildConfig.minSdk
+        targetSdkVersion buildConfig.targetSdk
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+}
+
+dependencies {
+    implementation project(":core")
+    implementation project(":trackers")
+    implementation project(":ipscrambling")
+
+    implementation(
+        libs.androidx.core.ktx,
+        libs.bundles.koin,
+        libs.kotlinx.coroutines,
+        libs.timber,
+    )
+}
diff --git a/trackersserviceeos/consumer-rules.pro b/trackersserviceeos/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/trackersserviceeos/proguard-rules.pro b/trackersserviceeos/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/trackersserviceeos/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/trackersserviceeos/src/main/AndroidManifest.xml b/trackersserviceeos/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2290432
--- /dev/null
+++ b/trackersserviceeos/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright  (C) 2023 MURENA SAS
+    Copyright  (C) 2022 ECORP
+
+    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 <https://www.gnu.org/licenses/>.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="foundation.e.advancedprivacy.trackers.service">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
+
+    <application>
+        <service
+            android:name=".TrackersService"
+            android:enabled="true"
+            android:exported="true" />
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
new file mode 100644
index 0000000..6a2b218
--- /dev/null
+++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.advancedprivacy.trackers.service
+
+import android.net.LocalServerSocket
+import android.system.ErrnoException
+import android.system.Os
+import android.system.OsConstants
+import foundation.e.advancedprivacy.core.utils.runSuspendCatching
+import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import java.io.PrintWriter
+
+class DNSBlocker(
+    val filterHostnameUseCase: FilterHostnameUseCase
+) {
+    private var resolverReceiver: LocalServerSocket? = null
+
+    companion object {
+        private const val SOCKET_NAME = "foundation.e.advancedprivacy"
+    }
+
+    private fun closeSocket() {
+        // Known bug and workaround that LocalServerSocket::close is not working well
+        // https://issuetracker.google.com/issues/36945762
+        if (resolverReceiver != null) {
+            try {
+                Os.shutdown(resolverReceiver!!.fileDescriptor, OsConstants.SHUT_RDWR)
+                resolverReceiver!!.close()
+                resolverReceiver = null
+            } catch (e: ErrnoException) {
+                if (e.errno != OsConstants.EBADF) {
+                    Timber.w("Socket already closed")
+                } else {
+                    Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !")
+                }
+            } catch (e: Exception) {
+                Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !")
+            }
+        }
+    }
+
+    fun listenJob(scope: CoroutineScope): Job = scope.launch(Dispatchers.IO) {
+        val resolverReceiver = runSuspendCatching {
+            LocalServerSocket(SOCKET_NAME)
+        }.getOrElse {
+            Timber.e(it, "Exception: cannot open DNS port on $SOCKET_NAME")
+            return@launch
+        }
+
+        this@DNSBlocker.resolverReceiver = resolverReceiver
+        Timber.d("DNSFilterProxy running on port $SOCKET_NAME")
+
+        while (isActive) {
+            runSuspendCatching {
+                val socket = resolverReceiver.accept()
+                val reader = BufferedReader(InputStreamReader(socket.inputStream))
+                val line = reader.readLine()
+                val params = line.split(",").toTypedArray()
+                val output = socket.outputStream
+                val writer = PrintWriter(output, true)
+                val domainName = params[0]
+                val appUid = params[1].toInt()
+                if (filterHostnameUseCase.shouldBlock(domainName, appUid)) {
+                    writer.println("block")
+                } else {
+                    writer.println("pass")
+                }
+                socket.close()
+            }.onFailure {
+                if (it is CancellationException) {
+                    closeSocket()
+                    throw it
+                } else {
+                    Timber.w(it, "Exception while listening DNS resolver")
+                }
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..5f573b0
--- /dev/null
+++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.trackers.service
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+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"
+
+        var coroutineScope = CoroutineScope(Dispatchers.IO)
+    }
+
+    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()
+        }
+        return START_REDELIVER_INTENT
+    }
+
+    private fun start() {
+        coroutineScope = CoroutineScope(Dispatchers.IO)
+        get<DNSBlocker>(DNSBlocker::class.java).apply {
+            filterHostnameUseCase.writeLogJob(coroutineScope)
+            listenJob(coroutineScope)
+        }
+    }
+
+    private fun stop() {
+        kotlin.runCatching { coroutineScope.cancel() }
+    }
+}
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
new file mode 100644
index 0000000..71a4fc4
--- /dev/null
+++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorEos.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.trackers.service
+
+import android.content.Context
+import android.content.Intent
+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 kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.isActive
+import org.koin.core.module.dsl.bind
+import org.koin.core.module.dsl.factoryOf
+import org.koin.core.module.dsl.singleOf
+import org.koin.core.qualifier.named
+import org.koin.dsl.module
+
+class TrackersSupervisorEos(private val context: Context) : TrackersSupervisor {
+
+    override val state: StateFlow<FeatureState> = MutableStateFlow(FeatureState.ON)
+
+    override fun start(): Boolean {
+        val intent = Intent(context, TrackersService::class.java)
+        intent.action = ACTION_START
+        return context.startService(intent) != null
+    }
+
+    override fun stop(): Boolean {
+        return context.stopService(Intent(context, TrackersService::class.java))
+    }
+
+    override fun isRunning(): Boolean {
+        return TrackersService.coroutineScope.isActive
+    }
+
+    override val dnsFilterForIpScrambling = null
+}
+
+val trackerServiceModule = module {
+    factoryOf(::DNSBlocker)
+    singleOf(::TrackersSupervisorEos) {
+        bind<TrackersSupervisor>()
+    }
+    single<VpnSupervisorUseCase> {
+        VpnSupervisorUseCaseEos(
+            localStateRepository = get(),
+            orbotSupervisor = get(),
+            trackersSupervisor = get(),
+            appDesc = get(named("AdvancedPrivacy")),
+            permissionsPrivacyModule = get(),
+            scope = get(),
+        )
+    }
+}
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
new file mode 100644
index 0000000..976a2b7
--- /dev/null
+++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.trackers.service
+
+import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.entities.FeatureState
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.IpScrambling
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.TrackersControl
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase
+import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
+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.flow.drop
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.launch
+
+class VpnSupervisorUseCaseEos(
+    private val localStateRepository: LocalStateRepository,
+    private val orbotSupervisor: OrbotSupervisor,
+    private val trackersSupervisor: TrackersSupervisor,
+    private val appDesc: ApplicationDescription,
+    private val permissionsPrivacyModule: IPermissionsPrivacyModule,
+    private val scope: CoroutineScope,
+) : VpnSupervisorUseCase {
+
+    override fun listenSettings() {
+        trackersSupervisor.start()
+
+        scope.launch(Dispatchers.IO) {
+            localStateRepository.ipScramblingSetting.collect {
+                applySettings(it)
+            }
+        }
+
+        scope.launch(Dispatchers.IO) {
+            localStateRepository.blockTrackers.drop(1).dropWhile { !it }.collect {
+                localStateRepository.emitStartVpnDisclaimer(TrackersControl())
+            }
+        }
+    }
+
+    private suspend fun applySettings(isIpScramblingEnabled: Boolean) {
+        val currentMode = localStateRepository.internetPrivacyMode.value
+        when {
+            (
+                isIpScramblingEnabled &&
+                    currentMode in setOf(FeatureState.OFF, FeatureState.STOPPING)
+                ) -> {
+                applyStartIpScrambling()
+            }
+            (
+                !isIpScramblingEnabled &&
+                    currentMode in setOf(FeatureState.ON, FeatureState.STARTING)
+                ) -> {
+                orbotSupervisor.stop()
+            }
+            else -> {}
+        }
+    }
+
+    private suspend fun applyStartIpScrambling() {
+        if (orbotSupervisor.prepareAndroidVpn() != null) {
+            permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName)
+            val alwaysOnVpnPackage = permissionsPrivacyModule.getAlwaysOnVpnPackage()
+            if (alwaysOnVpnPackage != null) {
+                localStateRepository.emitOtherVpnRunning(
+                    permissionsPrivacyModule.getApplicationDescription(
+                        packageName = alwaysOnVpnPackage,
+                        withIcon = false
+                    )
+                )
+                localStateRepository.setIpScramblingSetting(enabled = false)
+                return
+            }
+        }
+
+        localStateRepository.emitStartVpnDisclaimer(IpScrambling())
+        startVpnService(IpScrambling())
+    }
+
+    override fun startVpnService(feature: MainFeatures) {
+        localStateRepository.internetPrivacyMode.value = FeatureState.STARTING
+        orbotSupervisor.setDNSFilter(null)
+        orbotSupervisor.start()
+    }
+
+    override fun cancelStartVpnService(feature: MainFeatures) {
+        localStateRepository.setIpScramblingSetting(enabled = false)
+    }
+}
diff --git a/trackersservicestandalone/build.gradle b/trackersservicestandalone/build.gradle
index ead9dbd..5b574cd 100644
--- a/trackersservicestandalone/build.gradle
+++ b/trackersservicestandalone/build.gradle
@@ -29,6 +29,7 @@ android {
 dependencies {
     implementation project(":core")
     implementation project(":trackers")
+    implementation project(":ipscrambling")
 
     implementation(
         libs.androidx.core.ktx,
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
index 918977f..152a3e9 100644
--- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
@@ -16,16 +16,15 @@
  */
 package foundation.e.advancedprivacy.trackers.service
 
-import android.content.Context
 import android.content.Intent
 import android.net.VpnService
 import android.os.Build
 import android.os.ParcelFileDescriptor
 import foundation.e.advancedprivacy.core.utils.notificationBuilder
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import foundation.e.advancedprivacy.domain.entities.FeatureState
 import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_TRACKER_FLAG
 import foundation.e.advancedprivacy.domain.entities.NotificationContent
-import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
+import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor
 import foundation.e.advancedprivacy.trackers.service.Config.DNS_SERVER_TO_CATCH_IPV4
 import foundation.e.advancedprivacy.trackers.service.Config.DNS_SERVER_TO_CATCH_IPV6
 import foundation.e.advancedprivacy.trackers.service.Config.SESSION_NAME
@@ -39,18 +38,12 @@ import timber.log.Timber
 class TrackersService : VpnService() {
     companion object {
         var coroutineScope = CoroutineScope(Dispatchers.IO)
-
-        fun start(context: Context) {
-            prepare(context)
-            val intent = Intent(context, TrackersService::class.java)
-            context.startService(intent)
-        }
     }
 
     private val networkDNSAddressRepository: NetworkDNSAddressRepository = get(NetworkDNSAddressRepository::class.java)
-    private val trackersServiceSupervisor: TrackersServiceSupervisorImpl = get(
-        TrackersServiceSupervisor::class.java
-    ) as TrackersServiceSupervisorImpl
+    private val trackersSupervisor: TrackersSupervisorStandalone = get(
+        TrackersSupervisor::class.java
+    ) as TrackersSupervisorStandalone
 
     private val notificationTrackerFlag: NotificationContent = get(NotificationContent::class.java, named("notificationTrackerFlag"))
 
@@ -64,14 +57,14 @@ class TrackersService : VpnService() {
                 content = notificationTrackerFlag
             ).build()
         )
-        trackersServiceSupervisor.state.value = FeatureServiceState.ON
+        trackersSupervisor.mutableState.value = FeatureState.ON
 
         return START_STICKY
     }
 
     override fun onDestroy() {
         networkDNSAddressRepository.stop()
-        trackersServiceSupervisor.state.value = FeatureServiceState.OFF
+        trackersSupervisor.mutableState.value = FeatureState.OFF
         super.onDestroy()
     }
 
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
deleted file mode 100644
index e2a6692..0000000
--- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- *
- * 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 <https://www.gnu.org/licenses/>.
- */
-package foundation.e.advancedprivacy.trackers.service
-
-import android.content.Context
-import android.content.Intent
-import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
-import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
-import foundation.e.advancedprivacy.trackers.service.data.NetworkDNSAddressRepository
-import foundation.e.advancedprivacy.trackers.service.data.RequestDNSRepository
-import foundation.e.advancedprivacy.trackers.service.usecases.ResolveDNSUseCase
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.MutableStateFlow
-import org.koin.core.module.dsl.bind
-import org.koin.core.module.dsl.singleOf
-import org.koin.dsl.module
-import org.pcap4j.packet.DnsPacket
-import java.util.function.Function
-
-class TrackersServiceSupervisorImpl(
-    private val context: Context,
-    private val resolveDNSUseCase: ResolveDNSUseCase
-) : TrackersServiceSupervisor {
-    internal val state: MutableStateFlow<FeatureServiceState> = MutableStateFlow(FeatureServiceState.OFF)
-
-    override fun start(): Boolean {
-        return if (!isRunning()) {
-            state.value = FeatureServiceState.STARTING
-            TrackersService.start(context)
-            true
-        } else false
-    }
-
-    override fun stop(): Boolean {
-        return when (state.value) {
-            FeatureServiceState.ON -> {
-                state.value = FeatureServiceState.STOPPING
-                kotlin.runCatching { TrackersService.coroutineScope.cancel() }
-                context.stopService(Intent(context, TrackersService::class.java))
-                true
-            }
-            else -> false
-        }
-    }
-
-    override fun isRunning(): Boolean {
-        return state.value != FeatureServiceState.OFF
-    }
-
-    override val dnsFilterForIpScrambling = Function<DnsPacket?, DnsPacket?> { dnsRequest -> resolveDNSUseCase.shouldBlock(dnsRequest) }
-}
-
-val trackerServiceModule = module {
-    singleOf(::NetworkDNSAddressRepository)
-    singleOf(::RequestDNSRepository)
-    singleOf(::ResolveDNSUseCase)
-    singleOf(::TunLooper)
-    singleOf(::TrackersServiceSupervisorImpl) { bind<TrackersServiceSupervisor>() }
-}
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt
new file mode 100644
index 0000000..ac06ced
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.trackers.service
+
+import android.content.Context
+import android.content.Intent
+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.data.NetworkDNSAddressRepository
+import foundation.e.advancedprivacy.trackers.service.data.RequestDNSRepository
+import foundation.e.advancedprivacy.trackers.service.usecases.ResolveDNSUseCase
+import foundation.e.advancedprivacy.trackers.service.usecases.VpnSupervisorUseCaseStandalone
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import org.koin.core.module.dsl.bind
+import org.koin.core.module.dsl.singleOf
+import org.koin.dsl.module
+import org.pcap4j.packet.DnsPacket
+import java.util.function.Function
+
+class TrackersSupervisorStandalone(
+    private val context: Context,
+    private val resolveDNSUseCase: ResolveDNSUseCase
+) : TrackersSupervisor {
+    internal val mutableState: MutableStateFlow<FeatureState> = MutableStateFlow(FeatureState.OFF)
+    override val state: StateFlow<FeatureState> = mutableState
+
+    override fun start(): Boolean {
+        return if (!isRunning()) {
+            mutableState.value = FeatureState.STARTING
+            val intent = Intent(context, TrackersService::class.java)
+            context.startService(intent)
+            true
+        } else false
+    }
+
+    override fun stop(): Boolean {
+        return when (mutableState.value) {
+            FeatureState.ON -> {
+                mutableState.value = FeatureState.STOPPING
+                kotlin.runCatching { TrackersService.coroutineScope.cancel() }
+                context.stopService(Intent(context, TrackersService::class.java))
+                true
+            }
+            else -> false
+        }
+    }
+
+    override fun isRunning(): Boolean {
+        return state.value != FeatureState.OFF
+    }
+
+    override val dnsFilterForIpScrambling = Function<DnsPacket?, DnsPacket?> { dnsRequest -> resolveDNSUseCase.shouldBlock(dnsRequest) }
+}
+
+val trackerServiceModule = module {
+    singleOf(::NetworkDNSAddressRepository)
+    singleOf(::RequestDNSRepository)
+    singleOf(::ResolveDNSUseCase)
+    singleOf(::TunLooper)
+    singleOf(::TrackersSupervisorStandalone) { bind<TrackersSupervisor>() }
+    singleOf(::VpnSupervisorUseCaseStandalone) { bind<VpnSupervisorUseCase>() }
+}
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt
index 7813c67..bb349eb 100644
--- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt
@@ -84,6 +84,7 @@ class TunLooper(
                 }
             }
         }
+        closeStreams()
     }
 
     private suspend fun handleIpPacket(buffer: ByteArray, pLen: Int) {
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt
new file mode 100644
index 0000000..683f886
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package foundation.e.advancedprivacy.trackers.service.usecases
+
+import foundation.e.advancedprivacy.domain.entities.FeatureState
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.IpScrambling
+import foundation.e.advancedprivacy.domain.entities.MainFeatures.TrackersControl
+import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase
+import foundation.e.advancedprivacy.externalinterfaces.servicesupervisors.FeatureSupervisor
+import foundation.e.advancedprivacy.ipscrambler.OrbotSupervisor
+import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.launch
+
+class VpnSupervisorUseCaseStandalone(
+    private val localStateRepository: LocalStateRepository,
+    private val trackersSupervisor: TrackersSupervisor,
+    private val orbotSupervisor: OrbotSupervisor,
+    private val scope: CoroutineScope,
+) : VpnSupervisorUseCase {
+    private var applySettingJob: Job? = null
+
+    init {
+        listenSettings()
+    }
+
+    override fun listenSettings() {
+        var previousBlockTrackers: Boolean? = null
+
+        localStateRepository.blockTrackers.combine(
+            localStateRepository.ipScramblingSetting
+        ) { blockTrackers, hideIp ->
+            applySettingJob?.cancel()
+            applySettingJob = scope.launch {
+                when {
+                    blockTrackers && !hideIp ->
+                        launchVpnService(trackersSupervisor)
+
+                    !blockTrackers && !hideIp ->
+                        stopAllServices()
+
+                    else -> {
+                        if (blockTrackers && previousBlockTrackers != true) {
+                            localStateRepository.emitStartVpnDisclaimer(IpScrambling())
+                        }
+
+                        launchVpnService(orbotSupervisor)
+                    }
+                }
+
+                previousBlockTrackers = blockTrackers
+            }
+        }.launchIn(scope)
+    }
+
+    private fun stopAllServices() {
+        listOf(orbotSupervisor, trackersSupervisor).map { stopVpnService(it) }
+    }
+
+    private fun stopVpnService(supervisor: FeatureSupervisor): FeatureSupervisor {
+        when (supervisor.state.value) {
+            FeatureState.ON,
+            FeatureState.STARTING ->
+                supervisor.stop()
+
+            else -> {}
+        }
+        return supervisor
+    }
+
+    private suspend fun launchVpnService(supervisor: FeatureSupervisor) {
+        stopVpnService(otherSupervisor(supervisor)).let { otherSupervisor ->
+            otherSupervisor.state.first { it == FeatureState.OFF }
+        }
+
+        when (supervisor.state.value) {
+            FeatureState.STOPPING -> {
+                supervisor.state.first { it == FeatureState.OFF }
+                initiateStartVpnService(supervisor)
+            }
+
+            FeatureState.OFF -> initiateStartVpnService(supervisor)
+            else -> {}
+        }
+    }
+
+    private fun otherSupervisor(supervisor: FeatureSupervisor): FeatureSupervisor {
+        return when (supervisor) {
+            trackersSupervisor -> orbotSupervisor
+            else -> trackersSupervisor
+        }
+    }
+
+    private fun getSupervisor(feature: MainFeatures): FeatureSupervisor {
+        return when (feature) {
+            is TrackersControl -> trackersSupervisor
+            else -> orbotSupervisor
+        }
+    }
+
+    private suspend fun initiateStartVpnService(supervisor: FeatureSupervisor) {
+        val authorizeVpnIntent = orbotSupervisor.prepareAndroidVpn()
+        val feature = when (supervisor) {
+            trackersSupervisor -> TrackersControl(authorizeVpnIntent)
+            else -> IpScrambling(authorizeVpnIntent)
+        }
+
+        if (authorizeVpnIntent == null) {
+            localStateRepository.emitStartVpnDisclaimer(feature)
+            startVpnService(feature)
+        } else {
+            localStateRepository.emitStartVpnDisclaimer(feature)
+        }
+    }
+
+    override fun startVpnService(feature: MainFeatures) {
+        if (feature is IpScrambling) {
+            localStateRepository.internetPrivacyMode.value = FeatureState.STARTING
+            orbotSupervisor.setDNSFilter(trackersSupervisor.dnsFilterForIpScrambling)
+        }
+
+        getSupervisor(feature).start()
+    }
+
+    override fun cancelStartVpnService(feature: MainFeatures) {
+        when (feature) {
+            is IpScrambling ->
+                localStateRepository.setIpScramblingSetting(enabled = false)
+            is TrackersControl ->
+                trackersSupervisor.stop()
+            else -> {}
+        }
+    }
+}
-- 
cgit v1.2.1