diff options
87 files changed, 975 insertions, 1589 deletions
diff --git a/app/build.gradle b/app/build.gradle index af05ec0..5ce72f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -140,7 +140,7 @@ android { } dependencies { - implementation project(':privacymodule-api') + implementation project(':core') standaloneImplementation project(':permissionsstandalone') eImplementation project(':permissionse') @@ -160,15 +160,13 @@ dependencies { libs.androidx.fragment.ktx, libs.androidx.lifecycle.runtime, libs.androidx.lifecycle.viewmodel, - libs.androidx.work.ktx, + + libs.bundles.koin, libs.google.material, libs.androidx.navigation.fragment, libs.androidx.navigation.ui, - libs.retrofit, - libs.retrofit.scalars, - libs.maplibre, libs.mpandroidcharts, diff --git a/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt b/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt index 9ce0c2b..0af2a0e 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt @@ -18,17 +18,64 @@ package foundation.e.advancedprivacy import android.app.Application +import android.content.Intent +import foundation.e.advancedprivacy.common.WarningDialog +import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase +import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase +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.externalinterfaces.permissions.IPermissionsPrivacyModule +import foundation.e.advancedprivacy.trackers.services.DNSBlockerService +import foundation.e.advancedprivacy.trackers.services.UpdateTrackersWorker import foundation.e.lib.telemetry.Telemetry +import kotlinx.coroutines.CoroutineScope +import org.koin.android.ext.koin.androidContext +import org.koin.core.context.startKoin +import org.koin.java.KoinJavaComponent.get class AdvancedPrivacyApplication : Application() { - - // Initialize the dependency container. - val dependencyContainer: DependencyContainer by lazy { DependencyContainer(this) } - override fun onCreate() { super.onCreate() Telemetry.init(BuildConfig.SENTRY_DSN, this, true) - dependencyContainer.initBackgroundSingletons() + startKoin { + androidContext(this@AdvancedPrivacyApplication) + modules(appModule) + } + initBackgroundSingletons() + } + + private fun initBackgroundSingletons() { + UpdateTrackersWorker.periodicUpdate(this) + + WarningDialog.startListening( + get(ShowFeaturesWarningUseCase::class.java), + get(CoroutineScope::class.java), + this + ) + + Widget.startListening( + this, + get(GetQuickPrivacyStateUseCase::class.java), + get(TrackersStatisticsUseCase::class.java), + ) + + Notifications.startListening( + this, + get(GetQuickPrivacyStateUseCase::class.java), + get(IPermissionsPrivacyModule::class.java), + get(CoroutineScope::class.java), + ) + + get<IpScramblingStateUseCase>(IpScramblingStateUseCase::class.java) + get<FakeLocationStateUseCase>(FakeLocationStateUseCase::class.java) + get<TrackersStateUseCase>(TrackersStateUseCase::class.java) + + val intent = Intent(this, DNSBlockerService::class.java) + intent.action = DNSBlockerService.ACTION_START + intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, false) + startService(intent) } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt b/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt deleted file mode 100644 index f6f2038..0000000 --- a/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt +++ /dev/null @@ -1,210 +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 - -import android.app.Application -import android.content.Context -import android.os.Process -import androidx.lifecycle.DEFAULT_ARGS_KEY -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewmodel.CreationExtras -import foundation.e.advancedprivacy.common.WarningDialog -import foundation.e.advancedprivacy.data.repositories.AppListsRepository -import foundation.e.advancedprivacy.data.repositories.LocalStateRepository -import foundation.e.advancedprivacy.data.repositories.TrackersRepository -import foundation.e.advancedprivacy.domain.usecases.AppListUseCase -import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase -import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase -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.dummy.CityDataSource -import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel -import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyViewModel -import foundation.e.advancedprivacy.features.location.FakeLocationViewModel -import foundation.e.advancedprivacy.features.trackers.TrackersViewModel -import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersFragmentArgs -import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersViewModel -import foundation.e.privacymodules.fakelocation.FakeLocationModule -import foundation.e.privacymodules.ipscrambler.IIpScramblerModule -import foundation.e.privacymodules.ipscrambler.IpScramblerModule -import foundation.e.privacymodules.permissions.PermissionsPrivacyModule -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.permissions.data.ProfileType -import foundation.e.privacymodules.trackers.api.BlockTrackersPrivacyModule -import foundation.e.privacymodules.trackers.api.TrackTrackersPrivacyModule -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope - -/** - * Simple container to hold application wide dependencies. - * - */ -@OptIn(DelicateCoroutinesApi::class) -class DependencyContainer(val app: Application) { - val context: Context by lazy { app.applicationContext } - - // Drivers - private val fakeLocationModule: FakeLocationModule by lazy { FakeLocationModule(app.applicationContext) } - private val permissionsModule by lazy { PermissionsPrivacyModule(app.applicationContext) } - private val ipScramblerModule: IIpScramblerModule by lazy { IpScramblerModule(app.applicationContext) } - - private val appDesc by lazy { - ApplicationDescription( - packageName = context.packageName, - uid = Process.myUid(), - label = context.resources.getString(R.string.app_name), - icon = null, - profileId = -1, - profileType = ProfileType.MAIN - ) - } - - private val blockTrackersPrivacyModule by lazy { BlockTrackersPrivacyModule.getInstance(context) } - private val trackTrackersPrivacyModule by lazy { TrackTrackersPrivacyModule.getInstance(context) } - - // Repositories - private val localStateRepository by lazy { LocalStateRepository(context) } - private val trackersRepository by lazy { TrackersRepository(context) } - private val appListsRepository by lazy { AppListsRepository(permissionsModule, context, GlobalScope) } - - // Usecases - val getQuickPrivacyStateUseCase by lazy { - GetQuickPrivacyStateUseCase(localStateRepository) - } - private val ipScramblingStateUseCase by lazy { - IpScramblingStateUseCase( - ipScramblerModule, permissionsModule, appDesc, localStateRepository, - appListsRepository, GlobalScope - ) - } - private val appListUseCase = AppListUseCase(appListsRepository) - - val trackersStatisticsUseCase by lazy { - TrackersStatisticsUseCase(trackTrackersPrivacyModule, blockTrackersPrivacyModule, appListsRepository, context.resources) - } - - val trackersStateUseCase by lazy { - TrackersStateUseCase(blockTrackersPrivacyModule, trackTrackersPrivacyModule, localStateRepository, trackersRepository, appListsRepository, GlobalScope) - } - - private val fakeLocationStateUseCase by lazy { - FakeLocationStateUseCase( - fakeLocationModule, permissionsModule, localStateRepository, CityDataSource, appDesc, context, GlobalScope - ) - } - - val showFeaturesWarningUseCase by lazy { - ShowFeaturesWarningUseCase(localStateRepository = localStateRepository) - } - - val viewModelsFactory by lazy { - ViewModelsFactory( - getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase, - trackersStatisticsUseCase = trackersStatisticsUseCase, - trackersStateUseCase = trackersStateUseCase, - fakeLocationStateUseCase = fakeLocationStateUseCase, - ipScramblerModule = ipScramblerModule, - ipScramblingStateUseCase = ipScramblingStateUseCase, - appListUseCase = appListUseCase - ) - } - - // Background - fun initBackgroundSingletons() { - trackersStateUseCase - ipScramblingStateUseCase - fakeLocationStateUseCase - - UpdateTrackersWorker.periodicUpdate(context) - - WarningDialog.startListening( - showFeaturesWarningUseCase, - GlobalScope, - context - ) - - Widget.startListening( - context, - getQuickPrivacyStateUseCase, - trackersStatisticsUseCase, - ) - - Notifications.startListening( - context, - getQuickPrivacyStateUseCase, - permissionsModule, - GlobalScope - ) - } -} - -@Suppress("LongParameterList") -class ViewModelsFactory( - private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val trackersStateUseCase: TrackersStateUseCase, - private val fakeLocationStateUseCase: FakeLocationStateUseCase, - private val ipScramblerModule: IIpScramblerModule, - private val ipScramblingStateUseCase: IpScramblingStateUseCase, - private val appListUseCase: AppListUseCase -) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T { - return when (modelClass) { - AppTrackersViewModel::class.java -> { - val app = extras[DEFAULT_ARGS_KEY]?.let { - appListUseCase.getApp(AppTrackersFragmentArgs.fromBundle(it).appUid) - } ?: appListUseCase.dummySystemApp - - AppTrackersViewModel( - app = app, - trackersStateUseCase = trackersStateUseCase, - trackersStatisticsUseCase = trackersStatisticsUseCase, - getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase - ) - } - - TrackersViewModel::class.java -> - TrackersViewModel( - trackersStatisticsUseCase = trackersStatisticsUseCase - ) - FakeLocationViewModel::class.java -> - FakeLocationViewModel( - fakeLocationStateUseCase = fakeLocationStateUseCase - ) - InternetPrivacyViewModel::class.java -> - InternetPrivacyViewModel( - ipScramblerModule = ipScramblerModule, - getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase, - ipScramblingStateUseCase = ipScramblingStateUseCase, - appListUseCase = appListUseCase - ) - DashboardViewModel::class.java -> - DashboardViewModel( - getPrivacyStateUseCase = getQuickPrivacyStateUseCase, - trackersStatisticsUseCase = trackersStatisticsUseCase - ) - else -> throw IllegalArgumentException("Unknown class $modelClass") - } as T - } -} diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt new file mode 100644 index 0000000..534aa6b --- /dev/null +++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt @@ -0,0 +1,124 @@ +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.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.ProfileType +import foundation.e.advancedprivacy.domain.usecases.AppListUseCase +import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase +import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase +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.dummy.CityDataSource +import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule +import foundation.e.advancedprivacy.fakelocation.fakelocationModule +import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel +import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyViewModel +import foundation.e.advancedprivacy.features.location.FakeLocationViewModel +import foundation.e.advancedprivacy.features.trackers.TrackersViewModel +import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersViewModel +import foundation.e.advancedprivacy.ipscrambler.ipScramblerModule +import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModule +import foundation.e.advancedprivacy.trackers.trackersModule +import org.koin.android.ext.koin.androidContext +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.androidx.viewmodel.dsl.viewModelOf +import org.koin.core.module.dsl.singleOf +import org.koin.core.qualifier.named +import org.koin.dsl.module + +val appModule = module { + includes(coreModule, trackersModule, fakelocationModule, ipScramblerModule) + + factory<Resources> { androidContext().resources } + single { + LocalStateRepository(context = androidContext()) + } + + single<ApplicationDescription>(named("AdvancedPrivacy")) { + ApplicationDescription( + packageName = androidContext().packageName, + uid = Process.myUid(), + label = androidContext().resources.getString(R.string.app_name), + icon = null, + profileId = -1, + profileType = ProfileType.MAIN + ) + } + + single<ApplicationDescription>(named("DummySystemApp")) { + ApplicationDescription( + packageName = "foundation.e.dummysystemapp", + uid = -1, + label = androidContext().getString(R.string.dummy_system_app_label), + icon = androidContext().getDrawable(R.drawable.ic_e_app_logo), + profileId = -1, + profileType = ProfileType.MAIN + ) + } + + single<ApplicationDescription>(named("DummyCompatibilityApp")) { + ApplicationDescription( + packageName = "foundation.e.dummyappscompatibilityapp", + uid = -2, + label = androidContext().getString(R.string.dummy_apps_compatibility_app_label), + icon = androidContext().getDrawable(R.drawable.ic_apps_compatibility_components), + profileId = -1, + profileType = ProfileType.MAIN + ) + } + + single { CityDataSource } + + singleOf(::AppListUseCase) + single { + FakeLocationStateUseCase( + fakeLocationModule = get(), + permissionsModule = get(), + localStateRepository = get(), + citiesRepository = get(), + appDesc = get(named("AdvancedPrivacy")), + appContext = androidContext(), + coroutineScope = get() + ) + } + + singleOf(::GetQuickPrivacyStateUseCase) + single { + IpScramblingStateUseCase( + ipScramblerModule = get(), + permissionsPrivacyModule = get(), + appDesc = get(named("AdvancedPrivacy")), + localStateRepository = get(), + appListsRepository = get(), + coroutineScope = get() + ) + } + singleOf(::ShowFeaturesWarningUseCase) + singleOf(::TrackersStateUseCase) + singleOf(::TrackersStatisticsUseCase) + + single<IPermissionsPrivacyModule> { + PermissionsPrivacyModule(context = androidContext()) + } + + viewModel { parameters -> + val appListUseCase: AppListUseCase = get() + val app = appListUseCase.getApp(parameters.get()) + + AppTrackersViewModel( + app = app, + trackersStateUseCase = get(), + trackersStatisticsUseCase = get(), + getQuickPrivacyStateUseCase = get() + ) + } + viewModelOf(::TrackersViewModel) + viewModelOf(::FakeLocationViewModel) + viewModelOf(::InternetPrivacyViewModel) + viewModelOf(::DashboardViewModel) +} diff --git a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt index 291f9bc..cd85e9a 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt @@ -28,8 +28,8 @@ import androidx.core.app.NotificationManagerCompat import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode import foundation.e.advancedprivacy.domain.entities.MainFeatures import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase +import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule import foundation.e.advancedprivacy.main.MainActivity -import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn @@ -69,7 +69,7 @@ object Notifications { fun startListening( appContext: Context, getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - permissionsPrivacyModule: PermissionsPrivacyModule, + permissionsPrivacyModule: IPermissionsPrivacyModule, appScope: CoroutineScope ) { createNotificationFlagChannel( @@ -118,7 +118,7 @@ object Notifications { private fun createNotificationFlagChannel( context: Context, - permissionsPrivacyModule: PermissionsPrivacyModule, + permissionsPrivacyModule: IPermissionsPrivacyModule, channelId: String, @StringRes channelName: Int, @StringRes channelDescription: Int, diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/ToggleAppsAdapter.kt b/app/src/main/java/foundation/e/advancedprivacy/common/ToggleAppsAdapter.kt index d8ee8ea..8a0cc83 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/ToggleAppsAdapter.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/ToggleAppsAdapter.kt @@ -25,7 +25,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import foundation.e.advancedprivacy.R -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription class ToggleAppsAdapter( private val itemsLayout: Int, 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 3f3f66c..1a83692 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt @@ -27,7 +27,6 @@ import android.util.Log import android.view.View import android.widget.CheckBox import androidx.appcompat.app.AlertDialog -import foundation.e.advancedprivacy.AdvancedPrivacyApplication import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.domain.entities.MainFeatures import foundation.e.advancedprivacy.domain.entities.MainFeatures.FAKE_LOCATION @@ -38,6 +37,7 @@ import foundation.e.advancedprivacy.main.MainActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import org.koin.java.KoinJavaComponent.get class WarningDialog : Activity() { companion object { @@ -112,8 +112,7 @@ class WarningDialog : Activity() { } ) { _, _ -> if (checkbox.isChecked()) { - (application as AdvancedPrivacyApplication) - .dependencyContainer.showFeaturesWarningUseCase + get<ShowFeaturesWarningUseCase>(ShowFeaturesWarningUseCase::class.java) .doNotShowAgain(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 3f73c78..ba2836f 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 @@ -18,9 +18,9 @@ package foundation.e.advancedprivacy.data.repositories import android.content.Context +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode import foundation.e.advancedprivacy.domain.entities.LocationMode -import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt deleted file mode 100644 index 568d76b..0000000 --- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt +++ /dev/null @@ -1,133 +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.data.repositories - -import android.content.Context -import com.google.gson.Gson -import foundation.e.privacymodules.trackers.api.Tracker -import retrofit2.Retrofit -import retrofit2.converter.scalars.ScalarsConverterFactory -import retrofit2.http.GET -import timber.log.Timber -import java.io.File -import java.io.FileInputStream -import java.io.FileWriter -import java.io.IOException -import java.io.InputStreamReader -import java.io.PrintWriter - -class TrackersRepository(private val context: Context) { - - private val eTrackerFileName = "e_trackers.json" - private val eTrackerFile = File(context.filesDir.absolutePath, eTrackerFileName) - - var trackers: List<Tracker> = emptyList() - private set - - init { - initTrackersFile() - } - - suspend fun update() { - val api = ETrackersApi.build() - try { - saveData(eTrackerFile, api.trackers()) - initTrackersFile() - } catch (e: Exception) { - Timber.e("While updating trackers", e) - } - } - - private fun initTrackersFile() { - try { - var inputStream = context.assets.open(eTrackerFileName) - if (eTrackerFile.exists()) { - inputStream = FileInputStream(eTrackerFile) - } - val reader = InputStreamReader(inputStream, "UTF-8") - val trackerResponse = - Gson().fromJson(reader, ETrackersApi.ETrackersResponse::class.java) - - trackers = mapper(trackerResponse) - - reader.close() - inputStream.close() - } catch (e: Exception) { - Timber.e("While parsing trackers in assets", e) - } - } - - private fun mapper(response: ETrackersApi.ETrackersResponse): List<Tracker> { - return response.trackers.mapNotNull { - try { - it.toTracker() - } catch (e: Exception) { - null - } - } - } - - private fun ETrackersApi.ETrackersResponse.ETracker.toTracker(): Tracker { - return Tracker( - id = id!!, - hostnames = hostnames!!.toSet(), - label = name!!, - exodusId = exodusId - ) - } - - private fun saveData(file: File, data: String): Boolean { - try { - val fos = FileWriter(file, false) - val ps = PrintWriter(fos) - ps.apply { - print(data) - flush() - close() - } - return true - } catch (e: IOException) { - e.printStackTrace() - } - return false - } -} - -interface ETrackersApi { - companion object { - fun build(): ETrackersApi { - val retrofit = Retrofit.Builder() - .baseUrl("https://gitlab.e.foundation/e/os/tracker-list/-/raw/main/") - .addConverterFactory(ScalarsConverterFactory.create()) - .build() - return retrofit.create(ETrackersApi::class.java) - } - } - - @GET("list/e_trackers.json") - suspend fun trackers(): String - - data class ETrackersResponse(val trackers: List<ETracker>) { - data class ETracker( - val id: String?, - val hostnames: List<String>?, - val name: String?, - val exodusId: String? - ) - } -} diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt index 4169ecc..344e689 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt @@ -19,7 +19,6 @@ package foundation.e.advancedprivacy.domain.entities import android.graphics.drawable.Drawable -import foundation.e.privacymodules.permissions.data.ApplicationDescription data class AppWithCounts( val appDesc: ApplicationDescription, diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt index 8d38ee8..dfd32b6 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt @@ -18,7 +18,7 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import kotlinx.coroutines.flow.Flow class AppListUseCase( 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 8831fff..30c8e6b 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 @@ -26,12 +26,12 @@ 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.dummy.CityDataSource -import foundation.e.privacymodules.fakelocation.IFakeLocationModule -import foundation.e.privacymodules.permissions.PermissionsPrivacyModule -import foundation.e.privacymodules.permissions.data.AppOpModes -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule +import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -41,8 +41,8 @@ import timber.log.Timber import kotlin.random.Random class FakeLocationStateUseCase( - private val fakeLocationModule: IFakeLocationModule, - private val permissionsModule: PermissionsPrivacyModule, + private val fakeLocationModule: FakeLocationModule, + private val permissionsModule: IPermissionsPrivacyModule, private val localStateRepository: LocalStateRepository, private val citiesRepository: CityDataSource, private val appDesc: ApplicationDescription, 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 b82918e..bc4871a 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 @@ -18,11 +18,11 @@ 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.InternetPrivacyMode import foundation.e.advancedprivacy.domain.entities.LocationMode import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState import foundation.e.advancedprivacy.domain.entities.TrackerMode -import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -30,7 +30,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map class GetQuickPrivacyStateUseCase( - private val localStateRepository: LocalStateRepository + private val localStateRepository: LocalStateRepository, ) { val quickPrivacyState: Flow<QuickPrivacyState> = combine( localStateRepository.blockTrackers, 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 70607cf..a7ed660 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 @@ -19,14 +19,14 @@ 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.entities.InternetPrivacyMode import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.HIDE_IP import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.HIDE_IP_LOADING import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.REAL_IP import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.REAL_IP_LOADING -import foundation.e.privacymodules.ipscrambler.IIpScramblerModule -import foundation.e.privacymodules.permissions.IPermissionsPrivacyModule -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule +import foundation.e.advancedprivacy.ipscrambler.IpScramblerModule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose @@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class IpScramblingStateUseCase( - private val ipScramblerModule: IIpScramblerModule, + private val ipScramblerModule: IpScramblerModule, private val permissionsPrivacyModule: IPermissionsPrivacyModule, private val appDesc: ApplicationDescription, private val localStateRepository: LocalStateRepository, @@ -46,8 +46,8 @@ class IpScramblingStateUseCase( private val coroutineScope: CoroutineScope ) { val internetPrivacyMode: StateFlow<InternetPrivacyMode> = callbackFlow { - val listener = object : IIpScramblerModule.Listener { - override fun onStatusChanged(newStatus: IIpScramblerModule.Status) { + val listener = object : IpScramblerModule.Listener { + override fun onStatusChanged(newStatus: IpScramblerModule.Status) { trySend(map(newStatus)) } @@ -169,13 +169,13 @@ class IpScramblingStateUseCase( } } - private fun map(status: IIpScramblerModule.Status): InternetPrivacyMode { + private fun map(status: IpScramblerModule.Status): InternetPrivacyMode { return when (status) { - IIpScramblerModule.Status.OFF -> REAL_IP - IIpScramblerModule.Status.ON -> HIDE_IP - IIpScramblerModule.Status.STARTING -> HIDE_IP_LOADING - IIpScramblerModule.Status.STOPPING, - IIpScramblerModule.Status.START_DISABLED -> REAL_IP_LOADING + IpScramblerModule.Status.OFF -> REAL_IP + IpScramblerModule.Status.ON -> HIDE_IP + IpScramblerModule.Status.STARTING -> HIDE_IP_LOADING + IpScramblerModule.Status.STOPPING, + IpScramblerModule.Status.START_DISABLED -> REAL_IP_LOADING } } } 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 882d53f..ed15a41 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 @@ -19,87 +19,68 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.data.repositories.LocalStateRepository -import foundation.e.advancedprivacy.data.repositories.TrackersRepository -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule -import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule -import foundation.e.privacymodules.trackers.api.Tracker +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch class TrackersStateUseCase( - private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule, - private val trackersPrivacyModule: ITrackTrackersPrivacyModule, + private val whitelistRepository: WhitelistRepository, private val localStateRepository: LocalStateRepository, - private val trackersRepository: TrackersRepository, private val appListsRepository: AppListsRepository, - private val coroutineScope: CoroutineScope + coroutineScope: CoroutineScope ) { init { - trackersPrivacyModule.start( - trackers = trackersRepository.trackers, - getAppByAPId = appListsRepository::getApp, - getAppByUid = appListsRepository::getApp, - enableNotification = false - ) coroutineScope.launch { localStateRepository.blockTrackers.collect { enabled -> - if (enabled) { - blockTrackersPrivacyModule.enableBlocking() - } else { - blockTrackersPrivacyModule.disableBlocking() - } + whitelistRepository.isBlockingEnabled = enabled updateAllTrackersBlockedState() } } } private fun updateAllTrackersBlockedState() { - localStateRepository.areAllTrackersBlocked.value = blockTrackersPrivacyModule.isBlockingEnabled() && - blockTrackersPrivacyModule.isWhiteListEmpty() + localStateRepository.areAllTrackersBlocked.value = whitelistRepository.isBlockingEnabled && + whitelistRepository.areWhiteListEmpty() } fun isWhitelisted(app: ApplicationDescription): Boolean { - return isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule) + return isWhitelisted(app, appListsRepository, whitelistRepository) } fun toggleAppWhitelist(app: ApplicationDescription, isWhitelisted: Boolean) { appListsRepository.applyForHiddenApps(app) { - blockTrackersPrivacyModule.setWhiteListed(it, isWhitelisted) + whitelistRepository.setWhiteListed(it.apId, isWhitelisted) } updateAllTrackersBlockedState() } fun blockTracker(app: ApplicationDescription, tracker: Tracker, isBlocked: Boolean) { appListsRepository.applyForHiddenApps(app) { - blockTrackersPrivacyModule.setWhiteListed(tracker, it, !isBlocked) + whitelistRepository.setWhiteListed(tracker, it.apId, !isBlocked) } updateAllTrackersBlockedState() } fun clearWhitelist(app: ApplicationDescription) { appListsRepository.applyForHiddenApps( - app, - blockTrackersPrivacyModule::clearWhiteList - ) + app + ) { + whitelistRepository.clearWhiteList(it.apId) + } updateAllTrackersBlockedState() } - - fun updateTrackers() = coroutineScope.launch { - trackersRepository.update() - trackersPrivacyModule.start( - trackers = trackersRepository.trackers, - getAppByAPId = appListsRepository::getApp, - getAppByUid = appListsRepository::getApp, - enableNotification = false - ) - } } fun isWhitelisted( app: ApplicationDescription, appListsRepository: AppListsRepository, - blockTrackersPrivacyModule: IBlockTrackersPrivacyModule + whitelistRepository: WhitelistRepository + ): Boolean { - return appListsRepository.anyForHiddenApps(app, blockTrackersPrivacyModule::isWhitelisted) + return appListsRepository.anyForHiddenApps( + app, + whitelistRepository::isAppWhiteListed + ) } diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt index 43e4496..b0c9f39 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt @@ -22,15 +22,14 @@ import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.throttleFirst import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.AppWithCounts +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.TrackersPeriodicStatistics -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule -import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule -import foundation.e.privacymodules.trackers.api.Tracker +import foundation.e.advancedprivacy.trackers.data.TrackersRepository +import foundation.e.advancedprivacy.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker +import foundation.e.advancedprivacy.trackers.domain.usecases.StatisticsUseCase import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart @@ -41,8 +40,9 @@ import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds class TrackersStatisticsUseCase( - private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule, - private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule, + private val statisticsUseCase: StatisticsUseCase, + private val whitelistRepository: WhitelistRepository, + private val trackersRepository: TrackersRepository, private val appListsRepository: AppListsRepository, private val resources: Resources ) { @@ -50,54 +50,45 @@ class TrackersStatisticsUseCase( appListsRepository.apps() } - private fun rawUpdates(): Flow<Unit> = callbackFlow { - val listener = object : ITrackTrackersPrivacyModule.Listener { - override fun onNewData() { - trySend(Unit) - } - } - trackTrackersPrivacyModule.addListener(listener) - awaitClose { trackTrackersPrivacyModule.removeListener(listener) } - } - @OptIn(FlowPreview::class) - fun listenUpdates(debounce: Duration = 1.seconds) = rawUpdates() - .throttleFirst(windowDuration = debounce) - .onStart { emit(Unit) } + fun listenUpdates(debounce: Duration = 1.seconds) = + statisticsUseCase.newDataAvailable + .throttleFirst(windowDuration = debounce) + .onStart { emit(Unit) } fun getDayStatistics(): Pair<TrackersPeriodicStatistics, Int> { return TrackersPeriodicStatistics( - callsBlockedNLeaked = trackTrackersPrivacyModule.getPastDayTrackersCalls(), + callsBlockedNLeaked = statisticsUseCase.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS), periods = buildDayLabels(), - trackersCount = trackTrackersPrivacyModule.getPastDayTrackersCount(), + trackersCount = statisticsUseCase.getActiveTrackersByPeriod(24, ChronoUnit.HOURS), graduations = buildDayGraduations(), - ) to trackTrackersPrivacyModule.getTrackersCount() + ) to statisticsUseCase.getContactedTrackersCount() } fun getNonBlockedTrackersCount(): Flow<Int> { - return if (blockTrackersPrivacyModule.isBlockingEnabled()) + return if (whitelistRepository.isBlockingEnabled) appListsRepository.allApps().map { apps -> val whiteListedTrackers = mutableSetOf<Tracker>() - val whiteListedApps = blockTrackersPrivacyModule.getWhiteListedApp() + val whiteListedApps = whitelistRepository.getWhiteListedApp() apps.forEach { app -> if (app in whiteListedApps) { - whiteListedTrackers.addAll(trackTrackersPrivacyModule.getTrackersForApp(app)) + whiteListedTrackers.addAll(statisticsUseCase.getTrackers(listOf(app))) } else { - whiteListedTrackers.addAll(blockTrackersPrivacyModule.getWhiteList(app)) + whiteListedTrackers.addAll(getWhiteList(app)) } } whiteListedTrackers.size } - else flowOf(trackTrackersPrivacyModule.getTrackersCount()) + else flowOf(statisticsUseCase.getContactedTrackersCount()) } fun getMostLeakedApp(): ApplicationDescription? { - return trackTrackersPrivacyModule.getPastDayMostLeakedApp() + return statisticsUseCase.getMostLeakedApp(24, ChronoUnit.HOURS) } - fun getDayTrackersCalls() = trackTrackersPrivacyModule.getPastDayTrackersCalls() + fun getDayTrackersCalls() = statisticsUseCase.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS) - fun getDayTrackersCount() = trackTrackersPrivacyModule.getPastDayTrackersCount() + fun getDayTrackersCount() = statisticsUseCase.getActiveTrackersByPeriod(24, ChronoUnit.HOURS) private fun buildDayGraduations(): List<String?> { val formatter = DateTimeFormatter.ofPattern( @@ -155,25 +146,23 @@ class TrackersStatisticsUseCase( } fun getDayMonthYearStatistics(): Triple<TrackersPeriodicStatistics, TrackersPeriodicStatistics, TrackersPeriodicStatistics> { - return with(trackTrackersPrivacyModule) { - Triple( - TrackersPeriodicStatistics( - callsBlockedNLeaked = getPastDayTrackersCalls(), - periods = buildDayLabels(), - trackersCount = getPastDayTrackersCount() - ), - TrackersPeriodicStatistics( - callsBlockedNLeaked = getPastMonthTrackersCalls(), - periods = buildMonthLabels(), - trackersCount = getPastMonthTrackersCount() - ), - TrackersPeriodicStatistics( - callsBlockedNLeaked = getPastYearTrackersCalls(), - periods = buildYearLabels(), - trackersCount = getPastYearTrackersCount() - ) + return Triple( + TrackersPeriodicStatistics( + callsBlockedNLeaked = statisticsUseCase.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS), + periods = buildDayLabels(), + trackersCount = statisticsUseCase.getActiveTrackersByPeriod(24, ChronoUnit.HOURS) + ), + TrackersPeriodicStatistics( + callsBlockedNLeaked = statisticsUseCase.getTrackersCallsOnPeriod(30, ChronoUnit.DAYS), + periods = buildMonthLabels(), + trackersCount = statisticsUseCase.getActiveTrackersByPeriod(30, ChronoUnit.DAYS) + ), + TrackersPeriodicStatistics( + callsBlockedNLeaked = statisticsUseCase.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS), + periods = buildYearLabels(), + trackersCount = statisticsUseCase.getActiveTrackersByPeriod(12, ChronoUnit.MONTHS) ) - } + ) } fun getTrackersWithWhiteList(app: ApplicationDescription): List<Pair<Tracker, Boolean>> { @@ -181,8 +170,8 @@ class TrackersStatisticsUseCase( app = app, map = { appDesc: ApplicationDescription -> ( - trackTrackersPrivacyModule.getTrackersForApp(appDesc) to - blockTrackersPrivacyModule.getWhiteList(appDesc) + statisticsUseCase.getTrackers(listOf(appDesc)) to + getWhiteList(appDesc) ) }, reduce = { lists -> @@ -200,7 +189,7 @@ class TrackersStatisticsUseCase( return appListsRepository.mapReduceForHiddenApps( app = app, map = { appDesc: ApplicationDescription -> - blockTrackersPrivacyModule.getWhiteList(appDesc).isEmpty() + getWhiteList(appDesc).isEmpty() }, reduce = { areEmpty -> areEmpty.all { it } } ) @@ -209,7 +198,9 @@ class TrackersStatisticsUseCase( fun getCalls(app: ApplicationDescription): Pair<Int, Int> { return appListsRepository.mapReduceForHiddenApps( app = app, - map = trackTrackersPrivacyModule::getPastDayTrackersCallsForApp, + map = { + statisticsUseCase.getCalls(it, 24, ChronoUnit.HOURS) + }, reduce = { zip -> zip.unzip().let { (blocked, leaked) -> blocked.sum() to leaked.sum() @@ -219,7 +210,7 @@ class TrackersStatisticsUseCase( } fun getAppsWithCounts(): Flow<List<AppWithCounts>> { - val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp() + val trackersCounts = statisticsUseCase.getContactedTrackersCountByApp() val hiddenAppsTrackersWithWhiteList = getTrackersWithWhiteList(appListsRepository.dummySystemApp) val acAppsTrackersWithWhiteList = @@ -227,7 +218,7 @@ class TrackersStatisticsUseCase( return appListsRepository.apps() .map { apps -> - val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps() + val callsByApp = statisticsUseCase.getCallsByApps(24, ChronoUnit.HOURS) apps.map { app -> val calls = appListsRepository.mapReduceForHiddenApps( app = app, @@ -241,8 +232,8 @@ class TrackersStatisticsUseCase( AppWithCounts( app = app, - isWhitelisted = !blockTrackersPrivacyModule.isBlockingEnabled() || - isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule), + isWhitelisted = !whitelistRepository.isBlockingEnabled || + isWhitelisted(app, appListsRepository, whitelistRepository), trackersCount = when (app) { appListsRepository.dummySystemApp -> hiddenAppsTrackersWithWhiteList.size @@ -256,7 +247,7 @@ class TrackersStatisticsUseCase( appListsRepository.dummyCompatibilityApp -> acAppsTrackersWithWhiteList.count { it.second } else -> - blockTrackersPrivacyModule.getWhiteList(app).size + getWhiteList(app).size }, blockedLeaks = calls.first, leaks = calls.second @@ -266,6 +257,12 @@ class TrackersStatisticsUseCase( } } + private fun getWhiteList(app: ApplicationDescription): List<Tracker> { + return whitelistRepository.getWhiteListForApp(app).mapNotNull { + trackersRepository.getTracker(it) + } + } + private val mostLeakedAppsComparator: Comparator<AppWithCounts> = Comparator { o1, o2 -> val leaks = o2.leaks - o1.leaks if (leaks != 0) leaks else { diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt deleted file mode 100644 index 94c734c..0000000 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt +++ /dev/null @@ -1,33 +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.usecases - -import foundation.e.advancedprivacy.data.repositories.LocalStateRepository -import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule - -class UpdateWidgetUseCase( - private val localStateRepository: LocalStateRepository, - private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule, -) { - init { - trackTrackersPrivacyModule.addListener(object : ITrackTrackersPrivacyModule.Listener { - override fun onNewData() { - } - }) - } -} 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 6ca9792..5eb0bb6 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 @@ -25,14 +25,11 @@ import android.view.View import android.widget.Toast import androidx.core.content.ContextCompat.getColor import androidx.core.view.isVisible -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import foundation.e.advancedprivacy.AdvancedPrivacyApplication -import foundation.e.advancedprivacy.DependencyContainer import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.GraphHolder import foundation.e.advancedprivacy.common.NavToolbarFragment @@ -44,15 +41,10 @@ import foundation.e.advancedprivacy.domain.entities.TrackerMode import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel.Action import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel.SingleEvent import kotlinx.coroutines.launch +import org.koin.androidx.viewmodel.ext.android.viewModel class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) { - private val dependencyContainer: DependencyContainer by lazy { - (this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer - } - - private val viewModel: DashboardViewModel by viewModels { - dependencyContainer.viewModelsFactory - } + private val viewModel: DashboardViewModel by viewModel() private var graphHolder: GraphHolder? = 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 35fc1d4..1180af3 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 @@ -23,13 +23,10 @@ import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Toast -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.LinearLayoutManager -import foundation.e.advancedprivacy.AdvancedPrivacyApplication -import foundation.e.advancedprivacy.DependencyContainer import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.NavToolbarFragment import foundation.e.advancedprivacy.common.ToggleAppsAdapter @@ -37,17 +34,12 @@ import foundation.e.advancedprivacy.common.setToolTipForAsterisk import foundation.e.advancedprivacy.databinding.FragmentInternetActivityPolicyBinding import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode import kotlinx.coroutines.launch +import org.koin.androidx.viewmodel.ext.android.viewModel import java.util.Locale class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_activity_policy) { - private val dependencyContainer: DependencyContainer by lazy { - (this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer - } - - private val viewModel: InternetPrivacyViewModel by viewModels { - dependencyContainer.viewModelsFactory - } + private val viewModel: InternetPrivacyViewModel by viewModel() private var _binding: FragmentInternetActivityPolicyBinding? = null private val binding get() = _binding!! 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 e0df73b..4d0fb38 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 @@ -17,8 +17,8 @@ package foundation.e.advancedprivacy.features.internetprivacy +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode -import foundation.e.privacymodules.permissions.data.ApplicationDescription data class InternetPrivacyState( val mode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP, 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 4c707a2..80e00bc 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 @@ -25,7 +25,7 @@ import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode import foundation.e.advancedprivacy.domain.usecases.AppListUseCase import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase -import foundation.e.privacymodules.ipscrambler.IIpScramblerModule +import foundation.e.advancedprivacy.ipscrambler.IpScramblerModule import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableSharedFlow @@ -40,7 +40,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class InternetPrivacyViewModel( - private val ipScramblerModule: IIpScramblerModule, + private val ipScramblerModule: IpScramblerModule, private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, private val ipScramblingStateUseCase: IpScramblingStateUseCase, private val appListUseCase: AppListUseCase diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt index 7d18930..1c629c2 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/location/FakeLocationFragment.kt @@ -31,7 +31,6 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.NonNull import androidx.core.view.isVisible import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -50,8 +49,6 @@ import com.mapbox.mapboxsdk.location.modes.CameraMode import com.mapbox.mapboxsdk.location.modes.RenderMode import com.mapbox.mapboxsdk.maps.MapboxMap import com.mapbox.mapboxsdk.maps.Style -import foundation.e.advancedprivacy.AdvancedPrivacyApplication -import foundation.e.advancedprivacy.DependencyContainer import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.NavToolbarFragment import foundation.e.advancedprivacy.databinding.FragmentFakeLocationBinding @@ -60,19 +57,14 @@ import foundation.e.advancedprivacy.features.location.FakeLocationViewModel.Acti import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) { private var isFirstLaunch: Boolean = true - private val dependencyContainer: DependencyContainer by lazy { - (this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer - } - - private val viewModel: FakeLocationViewModel by viewModels { - dependencyContainer.viewModelsFactory - } + private val viewModel: FakeLocationViewModel by viewModel() private var _binding: FragmentFakeLocationBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt index f486114..132fa3b 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt @@ -31,14 +31,11 @@ import android.view.View import android.widget.Toast import androidx.core.content.ContextCompat import androidx.core.view.isVisible -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager -import foundation.e.advancedprivacy.AdvancedPrivacyApplication -import foundation.e.advancedprivacy.DependencyContainer import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.AppsAdapter import foundation.e.advancedprivacy.common.GraphHolder @@ -48,14 +45,10 @@ import foundation.e.advancedprivacy.databinding.FragmentTrackersBinding import foundation.e.advancedprivacy.databinding.TrackersItemGraphBinding import foundation.e.advancedprivacy.domain.entities.TrackersPeriodicStatistics import kotlinx.coroutines.launch +import org.koin.androidx.viewmodel.ext.android.viewModel class TrackersFragment : NavToolbarFragment(R.layout.fragment_trackers) { - - private val dependencyContainer: DependencyContainer by lazy { - (this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer - } - - private val viewModel: TrackersViewModel by viewModels { dependencyContainer.viewModelsFactory } + private val viewModel: TrackersViewModel by viewModel() private var _binding: FragmentTrackersBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt index 457a02a..7fb9ca6 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersFragment.kt @@ -24,27 +24,23 @@ import android.os.Bundle import android.view.View import android.widget.Toast import androidx.core.view.isVisible -import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar -import foundation.e.advancedprivacy.AdvancedPrivacyApplication -import foundation.e.advancedprivacy.DependencyContainer import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.NavToolbarFragment import foundation.e.advancedprivacy.databinding.ApptrackersFragmentBinding import kotlinx.coroutines.launch +import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { - private val dependencyContainer: DependencyContainer by lazy { - (this.requireActivity().application as AdvancedPrivacyApplication).dependencyContainer - } - private val viewModel: AppTrackersViewModel by viewModels { - dependencyContainer.viewModelsFactory - } + private val args: AppTrackersFragmentArgs by navArgs() + private val viewModel: AppTrackersViewModel by viewModel { parametersOf(args.appUid) } private var _binding: ApptrackersFragmentBinding? = null private val binding get() = _binding!! diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersState.kt index 2a9e6e8..a597da6 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersState.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersState.kt @@ -18,8 +18,8 @@ package foundation.e.advancedprivacy.features.trackers.apptrackers -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.api.Tracker +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker data class AppTrackersState( val appDesc: ApplicationDescription? = null, diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersViewModel.kt index cda4b4b..8740779 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersViewModel.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/AppTrackersViewModel.kt @@ -22,12 +22,12 @@ import android.net.Uri import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.TrackerMode import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersStateUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.api.Tracker +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/ToggleTrackersAdapter.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/ToggleTrackersAdapter.kt index 3696939..ef845b6 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/ToggleTrackersAdapter.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/apptrackers/ToggleTrackersAdapter.kt @@ -27,7 +27,7 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import foundation.e.advancedprivacy.R -import foundation.e.privacymodules.trackers.api.Tracker +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker class ToggleTrackersAdapter( private val itemsLayout: Int, diff --git a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt index 9021125..917cbda 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt @@ -20,11 +20,12 @@ package foundation.e.advancedprivacy.widget import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import foundation.e.advancedprivacy.AdvancedPrivacyApplication +import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase +import org.koin.java.KoinJavaComponent.get class WidgetCommandReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - val getQuickPrivacyStateUseCase = (context?.applicationContext as? AdvancedPrivacyApplication)?.dependencyContainer?.getQuickPrivacyStateUseCase + val getQuickPrivacyStateUseCase = get<GetQuickPrivacyStateUseCase>(GetQuickPrivacyStateUseCase::class.java) val featureEnabled = intent?.extras?.let { bundle -> if (bundle.containsKey(PARAM_FEATURE_ENABLED)) @@ -32,9 +33,9 @@ class WidgetCommandReceiver : BroadcastReceiver() { else null } when (intent?.action) { - ACTION_TOGGLE_TRACKERS -> getQuickPrivacyStateUseCase?.toggleTrackers(featureEnabled) - ACTION_TOGGLE_LOCATION -> getQuickPrivacyStateUseCase?.toggleLocation(featureEnabled) - ACTION_TOGGLE_IPSCRAMBLING -> getQuickPrivacyStateUseCase?.toggleIpScrambling(featureEnabled) + ACTION_TOGGLE_TRACKERS -> getQuickPrivacyStateUseCase.toggleTrackers(featureEnabled) + ACTION_TOGGLE_LOCATION -> getQuickPrivacyStateUseCase.toggleLocation(featureEnabled) + ACTION_TOGGLE_IPSCRAMBLING -> getQuickPrivacyStateUseCase.toggleIpScrambling(featureEnabled) else -> {} } } diff --git a/privacymodule-api/.gitignore b/core/.gitignore index 42afabf..42afabf 100644 --- a/privacymodule-api/.gitignore +++ b/core/.gitignore diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 0000000..0e53f22 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,52 @@ +/* + * 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/>. + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +group 'foundation.e' + +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 { + implementation( + libs.androidx.core.ktx, + libs.bundles.koin, + libs.kotlinx.coroutines + ) +} diff --git a/privacymodule-api/consumer-rules.pro b/core/consumer-rules.pro index e69de29..e69de29 100644 --- a/privacymodule-api/consumer-rules.pro +++ b/core/consumer-rules.pro diff --git a/privacymodule-api/proguard-rules.pro b/core/proguard-rules.pro index 481bb43..481bb43 100644 --- a/privacymodule-api/proguard-rules.pro +++ b/core/proguard-rules.pro diff --git a/privacymodule-api/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml index 937e285..a29e84c 100644 --- a/privacymodule-api/src/main/AndroidManifest.xml +++ b/core/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- + Copyright (C) 2023 MURENA SAS Copyright (C) 2022 E FOUNDATION This program is free software: you can redistribute it and/or modify @@ -16,6 +17,6 @@ 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.privacymodules.api" + package="foundation.e.advancedprivacy.core" > </manifest>
\ No newline at end of file diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/KoinModule.kt b/core/src/main/java/foundation/e/advancedprivacy/core/KoinModule.kt new file mode 100644 index 0000000..141da86 --- /dev/null +++ b/core/src/main/java/foundation/e/advancedprivacy/core/KoinModule.kt @@ -0,0 +1,23 @@ +package foundation.e.advancedprivacy.core + +import foundation.e.advancedprivacy.data.repositories.AppListsRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import org.koin.android.ext.koin.androidContext +import org.koin.core.qualifier.named +import org.koin.dsl.module + +@OptIn(DelicateCoroutinesApi::class) +val coreModule = module { + single<CoroutineScope> { GlobalScope } + single { + AppListsRepository( + permissionsModule = get(), + dummySystemApp = get(named("DummySystemApp")), + dummyCompatibilityApp = get(named("DummyCompatibilityApp")), + context = androidContext(), + coroutineScope = get() + ) + } +} diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/utils/CoroutinesUtils.kt b/core/src/main/java/foundation/e/advancedprivacy/core/utils/CoroutinesUtils.kt new file mode 100644 index 0000000..5344c6a --- /dev/null +++ b/core/src/main/java/foundation/e/advancedprivacy/core/utils/CoroutinesUtils.kt @@ -0,0 +1,45 @@ +/* + * 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.core.utils + +import kotlinx.coroutines.CancellationException +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +@OptIn(ExperimentalContracts::class) +inline fun <T> runSuspendCatching(block: () -> T): Result<T> { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + + return runCatching(block).onFailure { + if (it is CancellationException) { + throw it + } + } +} + +inline fun <R, T : R> Result<T>.recoverSuspendCatching( + transform: (exception: Throwable) -> R +): Result<R> { + return when (val exception = exceptionOrNull()) { + null -> this + else -> runSuspendCatching { transform(exception) } + } +} diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt b/core/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt index 2d7651d..f29bb8a 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt @@ -22,10 +22,9 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import foundation.e.advancedprivacy.R -import foundation.e.privacymodules.permissions.PermissionsPrivacyModule -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.permissions.data.ProfileType +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.ProfileType +import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -36,7 +35,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking class AppListsRepository( - private val permissionsModule: PermissionsPrivacyModule, + private val permissionsModule: IPermissionsPrivacyModule, + val dummySystemApp: ApplicationDescription, + val dummyCompatibilityApp: ApplicationDescription, private val context: Context, private val coroutineScope: CoroutineScope ) { @@ -51,24 +52,6 @@ class AppListsRepository( ) } - val dummySystemApp = ApplicationDescription( - packageName = "foundation.e.dummysystemapp", - uid = -1, - label = context.getString(R.string.dummy_system_app_label), - icon = context.getDrawable(R.drawable.ic_e_app_logo), - profileId = -1, - profileType = ProfileType.MAIN - ) - - val dummyCompatibilityApp = ApplicationDescription( - packageName = "foundation.e.dummyappscompatibilityapp", - uid = -2, - label = context.getString(R.string.dummy_apps_compatibility_app_label), - icon = context.getDrawable(R.drawable.ic_apps_compatibility_components), - profileId = -1, - profileType = ProfileType.MAIN - ) - private suspend fun fetchAppDescriptions(fetchMissingIcons: Boolean = false) { val launcherPackageNames = context.packageManager.queryIntentActivities( Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) }, diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/AppOpModes.kt index 4764596..3e0a261 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/AppOpModes.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.permissions.data +package foundation.e.advancedprivacy.domain.entities import android.app.AppOpsManager.MODE_ALLOWED import android.app.AppOpsManager.MODE_DEFAULT diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/ApplicationDescription.kt index 4fa1bb9..90b637f 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/ApplicationDescription.kt @@ -16,7 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.permissions.data +package foundation.e.advancedprivacy.domain.entities import android.graphics.drawable.Drawable diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/PermissionDescription.kt index 127192b..c3899a9 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/PermissionDescription.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.permissions.data +package foundation.e.advancedprivacy.domain.entities data class PermissionDescription( val name: String, diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/APermissionsPrivacyModule.kt index 64b2292..78f424b 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/APermissionsPrivacyModule.kt @@ -16,7 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.permissions +package foundation.e.advancedprivacy.externalinterfaces.permissions import android.app.AppOpsManager import android.content.Context @@ -27,10 +27,10 @@ import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS import android.graphics.drawable.Drawable import android.os.Build import android.util.Log -import foundation.e.privacymodules.permissions.data.AppOpModes -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.permissions.data.PermissionDescription -import foundation.e.privacymodules.permissions.data.ProfileType +import foundation.e.advancedprivacy.domain.entities.AppOpModes +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.PermissionDescription +import foundation.e.advancedprivacy.domain.entities.ProfileType /** * Implementation of the commons functionality between privileged and standard diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/IPermissionsPrivacyModule.kt index 39c726a..da11769 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/IPermissionsPrivacyModule.kt @@ -15,17 +15,16 @@ * 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.privacymodules.permissions +package foundation.e.advancedprivacy.externalinterfaces.permissions import android.app.NotificationChannel import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.graphics.drawable.Drawable -import foundation.e.privacymodules.permissions.data.AppOpModes -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.permissions.data.PermissionDescription -import foundation.e.privacymodules.permissions.data.ProfileType +import foundation.e.advancedprivacy.domain.entities.AppOpModes +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.PermissionDescription +import foundation.e.advancedprivacy.domain.entities.ProfileType /** * List applications and manage theirs permissions. diff --git a/fakelocation/build.gradle b/fakelocation/build.gradle index 91f8031..64fc633 100644 --- a/fakelocation/build.gradle +++ b/fakelocation/build.gradle @@ -46,7 +46,11 @@ android { } dependencies { - implementation (libs.bundles.kotlin.android.coroutines) - implementation project(':privacymodule-api') + implementation( + libs.bundles.koin, + libs.bundles.kotlin.android.coroutines + ) + implementation project(':core') + } diff --git a/fakelocation/fakelocationdemo/build.gradle b/fakelocation/fakelocationdemo/build.gradle index 0145eca..6cb9af3 100644 --- a/fakelocation/fakelocationdemo/build.gradle +++ b/fakelocation/fakelocationdemo/build.gradle @@ -54,7 +54,7 @@ android { } dependencies { - implementation project(':privacymodule-api') + implementation project(':core') implementation project(':fakelocation') implementation project(':permissionsstandalone') diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt index b7c9ced..f2e10a4 100644 --- a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt +++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt @@ -33,12 +33,12 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil -import foundation.e.privacymodules.fakelocation.FakeLocationModule +import foundation.e.advancedprivacy.domain.entities.AppOpModes +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.ProfileType +import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule +import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModule import foundation.e.privacymodules.fakelocationdemo.databinding.ActivityMainBinding -import foundation.e.privacymodules.permissions.PermissionsPrivacyModule -import foundation.e.privacymodules.permissions.data.AppOpModes -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.permissions.data.ProfileType class MainActivity : AppCompatActivity() { companion object { diff --git a/fakelocation/src/main/AndroidManifest.xml b/fakelocation/src/main/AndroidManifest.xml index 5077c24..fde371c 100644 --- a/fakelocation/src/main/AndroidManifest.xml +++ b/fakelocation/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- + ~ Copyright (C) 2023 MURENA SAS ~ Copyright (C) 2022 E FOUNDATION ~ ~ This program is free software: you can redistribute it and/or modify @@ -26,7 +27,7 @@ tools:ignore="MockLocation,ProtectedPermissions" /> <application> - <service android:name="foundation.e.privacymodules.fakelocation.FakeLocationService" + <service android:name="foundation.e.advancedprivacy.fakelocation.services.FakeLocationService" android:enabled="true" /> </application> </manifest>
\ No newline at end of file diff --git a/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/KoinModule.kt b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/KoinModule.kt new file mode 100644 index 0000000..b833181 --- /dev/null +++ b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/KoinModule.kt @@ -0,0 +1,9 @@ +package foundation.e.advancedprivacy.fakelocation + +import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val fakelocationModule = module { + singleOf(::FakeLocationModule) +} diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/domain/usecases/FakeLocationModule.kt index 4245836..c9aac0a 100644 --- a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt +++ b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/domain/usecases/FakeLocationModule.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.fakelocation +package foundation.e.advancedprivacy.fakelocation.domain.usecases import android.content.Context import android.content.Context.LOCATION_SERVICE @@ -27,6 +27,7 @@ import android.location.provider.ProviderProperties import android.os.Build import android.os.SystemClock import android.util.Log +import foundation.e.advancedprivacy.fakelocation.services.FakeLocationService /** * Implementation of the functionality of fake location. @@ -34,7 +35,7 @@ import android.util.Log * * @param context an Android context, to retrieve system services for example. */ -class FakeLocationModule(private val context: Context) : IFakeLocationModule { +class FakeLocationModule(private val context: Context) { companion object { private const val TAG = "FakeLocationModule" } @@ -56,7 +57,7 @@ class FakeLocationModule(private val context: Context) : IFakeLocationModule { * @see IFakeLocationModule.startFakeLocation */ @Synchronized - override fun startFakeLocation() { + fun startFakeLocation() { providers.forEach { provider -> try { locationManager.removeTestProvider(provider) @@ -84,7 +85,7 @@ class FakeLocationModule(private val context: Context) : IFakeLocationModule { } } - override fun setFakeLocation(latitude: Double, longitude: Double) { + fun setFakeLocation(latitude: Double, longitude: Double) { context.startService(FakeLocationService.buildFakeLocationIntent(context, latitude, longitude)) } @@ -118,7 +119,7 @@ class FakeLocationModule(private val context: Context) : IFakeLocationModule { /** * @see IFakeLocationModule.stopFakeLocation */ - override fun stopFakeLocation() { + fun stopFakeLocation() { context.stopService(FakeLocationService.buildStopIntent(context)) providers.forEach { provider -> try { diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/services/FakeLocationService.kt index 34620fe..6eaae54 100644 --- a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt +++ b/fakelocation/src/main/java/foundation/e/advancedprivacy/fakelocation/services/FakeLocationService.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.fakelocation +package foundation.e.advancedprivacy.fakelocation.services import android.app.Service import android.content.Context @@ -23,6 +23,7 @@ import android.content.Intent import android.os.CountDownTimer import android.os.IBinder import android.util.Log +import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule class FakeLocationService : Service() { diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/IFakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/IFakeLocationModule.kt deleted file mode 100644 index 32906f8..0000000 --- a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/IFakeLocationModule.kt +++ /dev/null @@ -1,41 +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.privacymodules.fakelocation - -/** - * Manage a fake location on the device. - */ -interface IFakeLocationModule { - /** - * Start to fake the location module. Call [setFakeLocation] after to set the fake - * position. - */ - fun startFakeLocation() - - /** - * Set or update the faked position. - * @param latitude the latitude of the fake position in degrees. - * @param longitude the longitude of the fake position in degrees. - */ - fun setFakeLocation(latitude: Double, longitude: Double) - - /** - * Stop the fake location module, giving back hand to the true location modules. - */ - fun stopFakeLocation() -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e85838d..b9925d4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] +koin = "3.2.0" kotlinx-coroutines = "1.6.1" kotlin = "1.6.10" androidx-navigation = "2.5.3" @@ -31,7 +32,10 @@ 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" } 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" } +koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } +koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version = "2.9.1" } @@ -44,6 +48,7 @@ timber = { group = "com.jakewharton.timber", name = "timber", version = "5.0.1" [bundles] +koin = ["koin-core", "koin-android"] kotlin-android-coroutines = ["androidx-core-ktx", "kotlinx-coroutines"] [plugins] diff --git a/ipscrambling/build.gradle b/ipscrambling/build.gradle index 0e293df..39efce7 100644 --- a/ipscrambling/build.gradle +++ b/ipscrambling/build.gradle @@ -47,6 +47,7 @@ android { dependencies { implementation( + libs.bundles.koin, libs.bundles.kotlin.android.coroutines, libs.androidx.localbroadcast, ) diff --git a/ipscrambling/orbotservice b/ipscrambling/orbotservice deleted file mode 160000 -Subproject 1930a046eff2dd37d23ffd83f0064f60334468a diff --git a/ipscrambling/src/main/AndroidManifest.xml b/ipscrambling/src/main/AndroidManifest.xml index e948147..d6496f0 100644 --- a/ipscrambling/src/main/AndroidManifest.xml +++ b/ipscrambling/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="foundation.e.privacymodules.tor" + package="foundation.e.advancedprivacy.ipscrambler" android:installLocation="internalOnly" > diff --git a/ipscrambling/src/main/java/foundation/e/privacymodules/ipscrambler/IpScramblerModule.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/IpScramblerModule.kt index 1c39330..d1f01a0 100644 --- a/ipscrambling/src/main/java/foundation/e/privacymodules/ipscrambler/IpScramblerModule.kt +++ b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/IpScramblerModule.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.ipscrambler +package foundation.e.advancedprivacy.ipscrambler import android.annotation.SuppressLint import android.content.BroadcastReceiver @@ -29,8 +29,6 @@ import android.os.Looper import android.os.Message import android.util.Log import androidx.localbroadcastmanager.content.LocalBroadcastManager -import foundation.e.privacymodules.ipscrambler.IIpScramblerModule.Listener -import foundation.e.privacymodules.ipscrambler.IIpScramblerModule.Status import org.torproject.android.service.OrbotConstants import org.torproject.android.service.OrbotConstants.ACTION_STOP_FOREGROUND_TASK import org.torproject.android.service.OrbotService @@ -38,7 +36,16 @@ import org.torproject.android.service.util.Prefs import java.security.InvalidParameterException @SuppressLint("CommitPrefEdits") -class IpScramblerModule(private val context: Context) : IIpScramblerModule { +class IpScramblerModule(private val context: Context) { + interface Listener { + fun onStatusChanged(newStatus: Status) + fun log(message: String) + fun onTrafficUpdate(upload: Long, download: Long, read: Long, write: Long) + } + + enum class Status { + OFF, ON, STARTING, STOPPING, START_DISABLED + } companion object { const val TAG = "IpScramblerModule" @@ -221,11 +228,11 @@ class IpScramblerModule(private val context: Context) : IIpScramblerModule { return if (raw.isEmpty()) raw else raw.slice(1..2) } - override fun prepareAndroidVpn(): Intent? { + fun prepareAndroidVpn(): Intent? { return VpnService.prepare(context) } - override fun start(enableNotification: Boolean) { + fun start(enableNotification: Boolean) { Prefs.enableNotification(enableNotification) Prefs.putUseVpn(true) Prefs.putStartOnBoot(true) @@ -234,7 +241,7 @@ class IpScramblerModule(private val context: Context) : IIpScramblerModule { sendIntentToService(OrbotConstants.ACTION_START_VPN) } - override fun stop() { + fun stop() { updateStatus(Status.STOPPING) Prefs.putUseVpn(false) @@ -261,7 +268,7 @@ class IpScramblerModule(private val context: Context) : IIpScramblerModule { ) } - override fun requestStatus() { + fun requestStatus() { if (isServiceRunning()) { sendIntentToService(OrbotConstants.ACTION_STATUS) } else { @@ -269,33 +276,33 @@ class IpScramblerModule(private val context: Context) : IIpScramblerModule { } } - override var appList: Set<String> + var appList: Set<String> get() = getTorifiedApps() set(value) = saveTorifiedApps(value) - override var exitCountry: String + var exitCountry: String get() = getExitCountryCode() set(value) = setExitCountryCode(value) - override fun getAvailablesLocations(): Set<String> = EXIT_COUNTRY_CODES + fun getAvailablesLocations(): Set<String> = EXIT_COUNTRY_CODES - override var httpProxyPort: Int = -1 + var httpProxyPort: Int = -1 private set - override var socksProxyPort: Int = -1 + var socksProxyPort: Int = -1 private set - override fun addListener(listener: Listener) { + fun addListener(listener: Listener) { listeners.add(listener) } - override fun removeListener(listener: Listener) { + fun removeListener(listener: Listener) { listeners.remove(listener) } - override fun clearListeners() { + fun clearListeners() { listeners.clear() } - override fun onCleared() { + fun onCleared() { LocalBroadcastManager.getInstance(context).unregisterReceiver(localBroadcastReceiver) } } diff --git a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt new file mode 100644 index 0000000..4f80ef4 --- /dev/null +++ b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt @@ -0,0 +1,8 @@ +package foundation.e.advancedprivacy.ipscrambler + +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val ipScramblerModule = module { + singleOf(::IpScramblerModule) +} diff --git a/ipscrambling/src/main/java/foundation/e/privacymodules/ipscrambler/IIpScramblerModule.kt b/ipscrambling/src/main/java/foundation/e/privacymodules/ipscrambler/IIpScramblerModule.kt deleted file mode 100644 index 859319a..0000000 --- a/ipscrambling/src/main/java/foundation/e/privacymodules/ipscrambler/IIpScramblerModule.kt +++ /dev/null @@ -1,54 +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.privacymodules.ipscrambler - -import android.content.Intent - -interface IIpScramblerModule { - fun prepareAndroidVpn(): Intent? - - fun start(enableNotification: Boolean = true) - - fun stop() - - fun requestStatus() - - var appList: Set<String> - - var exitCountry: String - fun getAvailablesLocations(): Set<String> - - val httpProxyPort: Int - val socksProxyPort: Int - - fun addListener(listener: Listener) - fun removeListener(listener: Listener) - fun clearListeners() - - fun onCleared() - - interface Listener { - fun onStatusChanged(newStatus: Status) - fun log(message: String) - fun onTrafficUpdate(upload: Long, download: Long, read: Long, write: Long) - } - - enum class Status { - OFF, ON, STARTING, STOPPING, START_DISABLED - } -} diff --git a/permissionse/build.gradle b/permissionse/build.gradle index 90e0622..7b6ff48 100644 --- a/permissionse/build.gradle +++ b/permissionse/build.gradle @@ -27,6 +27,6 @@ dependencies { compileOnly project(':permissionse:libs:hidden-apis-stub') implementation(libs.bundles.kotlin.android.coroutines) + implementation project(':core') - implementation project(':privacymodule-api') } diff --git a/permissionse/src/main/AndroidManifest.xml b/permissionse/src/main/AndroidManifest.xml index 3625087..4766007 100644 --- a/permissionse/src/main/AndroidManifest.xml +++ b/permissionse/src/main/AndroidManifest.xml @@ -1,6 +1,23 @@ +<!-- + ~ 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.privacymodules.e"> + package="foundation.e.advancedprivacy.permissions.e"> <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" tools:ignore="ProtectedPermissions" /> diff --git a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt index 6d0a17c..59a20dd 100644 --- a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.permissions +package foundation.e.advancedprivacy.permissions.externalinterfaces import android.annotation.TargetApi import android.app.AppOpsManager @@ -35,10 +35,11 @@ import android.os.ServiceManager import android.os.UserHandle import android.os.UserManager import android.util.Log -import foundation.e.privacymodules.permissions.data.AppOpModes -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.permissions.data.ProfileType.MAIN -import foundation.e.privacymodules.permissions.data.ProfileType.WORK +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.APermissionsPrivacyModule /** * Implements [IPermissionsPrivacyModule] with all privileges of a system app. diff --git a/permissionsstandalone/build.gradle b/permissionsstandalone/build.gradle index cf3563e..e330d31 100644 --- a/permissionsstandalone/build.gradle +++ b/permissionsstandalone/build.gradle @@ -47,7 +47,7 @@ android { dependencies { implementation(libs.bundles.kotlin.android.coroutines) - implementation project(':privacymodule-api') + implementation project(':core') testImplementation libs.junit diff --git a/permissionsstandalone/src/main/AndroidManifest.xml b/permissionsstandalone/src/main/AndroidManifest.xml index 662ea44..60ff274 100644 --- a/permissionsstandalone/src/main/AndroidManifest.xml +++ b/permissionsstandalone/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- + ~ Copyright (C) 2023 MURENA SAS ~ Copyright (C) 2022 E FOUNDATION ~ ~ This program is free software: you can redistribute it and/or modify @@ -17,7 +18,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="foundation.e.privacymodules.permissionsstandalone" + package="foundation.e.advancedprivacy.permissions.standalone" > </manifest>
\ No newline at end of file diff --git a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModule.kt index 283b417..95f5ff0 100644 --- a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModule.kt @@ -15,15 +15,16 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.permissions +package foundation.e.advancedprivacy.permissions.externalinterfaces import android.app.NotificationChannel import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable -import foundation.e.privacymodules.permissions.data.AppOpModes -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.AppOpModes +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.externalinterfaces.permissions.APermissionsPrivacyModule /** * Implements [IPermissionsPrivacyModule] using only API authorized on the PlayStore. diff --git a/privacymodule-api/build.gradle b/privacymodule-api/build.gradle deleted file mode 100644 index 259672b..0000000 --- a/privacymodule-api/build.gradle +++ /dev/null @@ -1,111 +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/>. - */ - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'maven-publish' - -group 'foundation.e' - -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 { - implementation( - libs.androidx.core.ktx, - libs.kotlinx.coroutines - ) -} - -//url "https://gitlab.e.foundation/api/v4/groups/e/privacy-central/-/packages/maven" - -publishing { - publications { - maven(MavenPublication) { - groupId 'foundation.e' - //You can either define these here or get them from project conf elsewhere - artifactId 'privacymodule-api' - version buildConfig.version.name - artifact "$buildDir/outputs/aar/privacymodule-api-release.aar" - //aar artifact you want to publish - - //generate pom nodes for dependencies - pom.withXml { - def dependenciesNode = asNode().appendNode('dependencies') - configurations.implementation.allDependencies.each { dependency -> - if (dependency.name != 'unspecified') { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', dependency.group) - dependencyNode.appendNode('artifactId', dependency.name) - dependencyNode.appendNode('version', dependency.version) - } - } - } - repositories { - def ciJobToken = System.getenv("CI_JOB_TOKEN") - def ciApiV4Url = System.getenv("CI_API_V4_URL") - if (ciJobToken != null) { - maven { - url "${ciApiV4Url}/projects/900/packages/maven" - credentials(HttpHeaderCredentials) { - name = 'Job-Token' - value = ciJobToken - } - authentication { - header(HttpHeaderAuthentication) - } - } - } else { - maven { -// url "https://gitlab.e.foundation/api/v4/projects/900/packages/maven" - // Use privacymodule-e repository (id = 781) for now, - // because repository not activated on Advanced Privacy (id = 900) - url "https://gitlab.e.foundation/api/v4/projects/781/packages/maven" - credentials(HttpHeaderCredentials) { - name = "Private-Token" - value = gitLabPrivateToken - // the variable resides in ~/.gradle/gradle.properties - } - authentication { - header(HttpHeaderAuthentication) - } - } - } - } - } - } -} diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt deleted file mode 100644 index 9bf8aba..0000000 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt +++ /dev/null @@ -1,31 +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.privacymodules - -import foundation.e.privacymodules.trackers.IDNSBlocker - -object DependencyInjector { - fun initialize( - dnsBlocker: IDNSBlocker - ) { - this.dnsBlocker = dnsBlocker - } - - lateinit var dnsBlocker: IDNSBlocker - private set -} diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt deleted file mode 100644 index a132aef..0000000 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt +++ /dev/null @@ -1,26 +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.privacymodules.trackers - -interface IDNSBlocker { - companion object { - const val DUMMY_APP_UID = -1 - } - - fun shouldBlock(hostname: String, appUid: Int): Boolean -} diff --git a/settings.gradle b/settings.gradle index c9b8f06..1b54f0d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,13 +12,13 @@ include ':app' rootProject.name = "AdvancedPrivacy" include ':fakelocation' include ':fakelocation:fakelocationdemo' -include ':privacymodule-api' +include ':core' include ':permissionsstandalone' include ':trackers' include ':permissionse' include ':permissionse:libs:hidden-apis-stub' include ':ipscrambling' -include ':ipscrambling:orbotservice' +//include ':ipscrambling:orbotservice' dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) diff --git a/trackers/build.gradle b/trackers/build.gradle index bb9489a..737db5a 100644 --- a/trackers/build.gradle +++ b/trackers/build.gradle @@ -1,4 +1,5 @@ /* + Copyright (C) 2023 MURENA SAS Copyright (C) 2022 ECORP This program is free software; you can redistribute it and/or @@ -42,9 +43,15 @@ android { } dependencies { - implementation project(':privacymodule-api') + implementation project(':core') implementation( + libs.androidx.work.ktx, + libs.bundles.koin, libs.bundles.kotlin.android.coroutines, + libs.google.gson, + libs.retrofit, + libs.retrofit.scalars, + libs.timber ) } diff --git a/trackers/src/main/AndroidManifest.xml b/trackers/src/main/AndroidManifest.xml index debdf61..615d310 100644 --- a/trackers/src/main/AndroidManifest.xml +++ b/trackers/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <?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 @@ -16,7 +17,7 @@ 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.privacymodules.trackers"> + package="foundation.e.advancedprivacy.trackers"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> @@ -29,7 +30,7 @@ <application> <service - android:name="foundation.e.privacymodules.trackers.DNSBlockerService" + android:name="foundation.e.advancedprivacy.trackers.services.DNSBlockerService" android:enabled="true" android:exported="true" /> </application> diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt new file mode 100644 index 0000000..0cfb69c --- /dev/null +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt @@ -0,0 +1,72 @@ +/* + * 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 + +import foundation.e.advancedprivacy.data.repositories.RemoteTrackersListRepository +import foundation.e.advancedprivacy.trackers.data.StatsDatabase +import foundation.e.advancedprivacy.trackers.data.TrackersRepository +import foundation.e.advancedprivacy.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.trackers.domain.usecases.DNSBlocker +import foundation.e.advancedprivacy.trackers.domain.usecases.StatisticsUseCase +import foundation.e.advancedprivacy.trackers.domain.usecases.TrackersLogger +import foundation.e.advancedprivacy.trackers.domain.usecases.UpdateTrackerListUseCase +import org.koin.android.ext.koin.androidContext +import org.koin.core.module.dsl.factoryOf +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +val trackersModule = module { + + factoryOf(::RemoteTrackersListRepository) + factoryOf(::UpdateTrackerListUseCase) + + singleOf(::TrackersRepository) + single { + StatsDatabase( + context = androidContext(), + trackersRepository = get() + ) + } + + single { + StatisticsUseCase( + database = get(), + appListsRepository = get() + ) + } + + single { + WhitelistRepository( + context = androidContext(), + appListsRepository = get() + ) + } + + factory { + DNSBlocker( + context = androidContext(), + trackersLogger = get(), + trackersRepository = get(), + whitelistRepository = get() + ) + } + + factory { + TrackersLogger(statisticsUseCase = get()) + } +} diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt new file mode 100644 index 0000000..1b38ecf --- /dev/null +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/ETrackersResponse.kt @@ -0,0 +1,10 @@ +package foundation.e.advancedprivacy.trackers.data + +data class ETrackersResponse(val trackers: List<ETracker>) { + data class ETracker( + val id: String?, + val hostnames: List<String>?, + val name: String?, + val exodusId: String? + ) +} diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt new file mode 100644 index 0000000..c2c0768 --- /dev/null +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt @@ -0,0 +1,61 @@ +/* + * 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.data.repositories + +import retrofit2.Retrofit +import retrofit2.converter.scalars.ScalarsConverterFactory +import retrofit2.http.GET +import timber.log.Timber +import java.io.File +import java.io.FileWriter +import java.io.IOException +import java.io.PrintWriter + +class RemoteTrackersListRepository { + + fun saveData(file: File, data: String): Boolean { + try { + val fos = FileWriter(file, false) + val ps = PrintWriter(fos) + ps.apply { + print(data) + flush() + close() + } + return true + } catch (e: IOException) { + Timber.e("While saving tracker file.", e) + } + return false + } +} + +interface ETrackersApi { + companion object { + fun build(): ETrackersApi { + val retrofit = Retrofit.Builder() + .baseUrl("https://gitlab.e.foundation/e/os/tracker-list/-/raw/main/") + .addConverterFactory(ScalarsConverterFactory.create()) + .build() + return retrofit.create(ETrackersApi::class.java) + } + } + + @GET("list/e_trackers.json") + suspend fun trackers(): String +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt index 4d287d4..6aa76cf 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt @@ -16,7 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers.data +package foundation.e.advancedprivacy.trackers.data import android.content.ContentValues import android.content.Context @@ -25,13 +25,13 @@ import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import android.provider.BaseColumns import androidx.core.database.getStringOrNull -import foundation.e.privacymodules.trackers.api.Tracker -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APPID -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TIMESTAMP -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TRACKER -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.TABLE_NAME +import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APPID +import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED +import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED +import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TIMESTAMP +import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TRACKER +import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.TABLE_NAME +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import timber.log.Timber import java.time.ZonedDateTime import java.time.format.DateTimeFormatter @@ -39,7 +39,10 @@ import java.time.temporal.ChronoUnit import java.time.temporal.TemporalUnit import java.util.concurrent.TimeUnit -class StatsDatabase(context: Context) : +class StatsDatabase( + context: Context, + private val trackersRepository: TrackersRepository +) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { companion object { @@ -84,7 +87,6 @@ class StatsDatabase(context: Context) : ) private val lock = Any() - private val trackersRepository = TrackersRepository.getInstance() override fun onCreate(db: SQLiteDatabase) { db.execSQL(SQL_CREATE_TABLE) diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt new file mode 100644 index 0000000..a7d5e49 --- /dev/null +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt @@ -0,0 +1,109 @@ +/* + * 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.data + +import android.content.Context +import com.google.gson.Gson +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import timber.log.Timber +import java.io.File +import java.io.FileInputStream +import java.io.InputStreamReader + +class TrackersRepository( + private val context: Context, + coroutineScope: CoroutineScope +) { + + private var trackersById: Map<String, Tracker> = HashMap() + private var hostnameToId: Map<String, String> = HashMap() + + private val eTrackerFileName = "e_trackers.json" + val eTrackerFile = File(context.filesDir.absolutePath, eTrackerFileName) + + init { + coroutineScope.launch(Dispatchers.IO) { + initTrackersFile() + } + } + fun initTrackersFile() { + try { + var inputStream = context.assets.open(eTrackerFileName) + if (eTrackerFile.exists()) { + inputStream = FileInputStream(eTrackerFile) + } + val reader = InputStreamReader(inputStream, "UTF-8") + val trackerResponse = + Gson().fromJson(reader, ETrackersResponse::class.java) + + setTrackersList(mapper(trackerResponse)) + + reader.close() + inputStream.close() + } catch (e: Exception) { + Timber.e("While parsing trackers in assets", e) + } + } + + private fun mapper(response: ETrackersResponse): List<Tracker> { + return response.trackers.mapNotNull { + try { + it.toTracker() + } catch (e: Exception) { + null + } + } + } + + private fun ETrackersResponse.ETracker.toTracker(): Tracker { + return Tracker( + id = id!!, + hostnames = hostnames!!.toSet(), + label = name!!, + exodusId = exodusId + ) + } + + private fun setTrackersList(list: List<Tracker>) { + val trackersById: MutableMap<String, Tracker> = HashMap() + val hostnameToId: MutableMap<String, String> = HashMap() + list.forEach { tracker -> + trackersById[tracker.id] = tracker + for (hostname in tracker.hostnames) { + hostnameToId[hostname] = tracker.id + } + } + this.trackersById = trackersById + this.hostnameToId = hostnameToId + } + + fun isTracker(hostname: String?): Boolean { + return hostnameToId.containsKey(hostname) + } + + fun getTrackerId(hostname: String?): String? { + return hostnameToId[hostname] + } + + fun getTracker(id: String?): Tracker? { + return trackersById[id] + } +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/WhitelistRepository.kt index 2763d06..429c5e9 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/WhitelistRepository.kt @@ -16,15 +16,19 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers.data +package foundation.e.advancedprivacy.trackers.data import android.content.Context import android.content.SharedPreferences -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.api.Tracker +import foundation.e.advancedprivacy.data.repositories.AppListsRepository +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import java.io.File -class WhitelistRepository private constructor(context: Context) { +class WhitelistRepository( + context: Context, + private val appListsRepository: AppListsRepository +) { private var appsWhitelist: Set<String> = HashSet() private var appUidsWhitelist: Set<Int> = HashSet() @@ -32,7 +36,6 @@ class WhitelistRepository private constructor(context: Context) { private var trackersWhitelistByUid: Map<Int, MutableSet<String>> = HashMap() private val prefs: SharedPreferences - private var getAppByAPId: ((String) -> ApplicationDescription?)? = null companion object { private const val SHARED_PREFS_FILE = "trackers_whitelist_v2" @@ -41,30 +44,17 @@ class WhitelistRepository private constructor(context: Context) { private const val KEY_APP_TRACKERS_WHITELIST_PREFIX = "app_trackers_whitelist_" private const val SHARED_PREFS_FILE_V1 = "trackers_whitelist.prefs" - - private var instance: WhitelistRepository? = null - fun getInstance(context: Context): WhitelistRepository { - return instance ?: WhitelistRepository(context).apply { instance = this } - } } init { prefs = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE) reloadCache() + migrate(context) } - fun setAppGetters( - context: Context, - getAppByAPId: (String) -> ApplicationDescription?, - getAppByUid: (Int) -> ApplicationDescription? - ) { - this.getAppByAPId = getAppByAPId - migrate(context, getAppByUid) - } - - private fun migrate(context: Context, getAppByUid: (Int) -> ApplicationDescription?) { + private fun migrate(context: Context) { if (context.sharedPreferencesExists(SHARED_PREFS_FILE_V1)) { - migrate1To2(context, getAppByUid) + migrate1To2(context) } } @@ -74,7 +64,7 @@ class WhitelistRepository private constructor(context: Context) { ).exists() } - private fun migrate1To2(context: Context, getAppByUid: (Int) -> ApplicationDescription?) { + private fun migrate1To2(context: Context) { val prefsV1 = context.getSharedPreferences(SHARED_PREFS_FILE_V1, Context.MODE_PRIVATE) val editorV2 = prefs.edit() @@ -83,7 +73,7 @@ class WhitelistRepository private constructor(context: Context) { val apIds = prefsV1.getStringSet(KEY_APPS_WHITELIST, HashSet())?.mapNotNull { try { val uid = it.toInt() - getAppByUid(uid)?.apId + appListsRepository.getApp(uid)?.apId } catch (e: Exception) { null } }?.toSet() ?: HashSet() @@ -93,7 +83,7 @@ class WhitelistRepository private constructor(context: Context) { if (key.startsWith(KEY_APP_TRACKERS_WHITELIST_PREFIX)) { try { val uid = key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length).toInt() - val apId = getAppByUid(uid)?.apId + val apId = appListsRepository.getApp(uid)?.apId apId?.let { val trackers = prefsV1.getStringSet(key, emptySet()) editorV2.putStringSet(buildAppTrackersKey(apId), trackers) @@ -117,13 +107,13 @@ class WhitelistRepository private constructor(context: Context) { private fun reloadAppsWhiteList() { appsWhitelist = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet()) ?: HashSet() appUidsWhitelist = appsWhitelist - .mapNotNull { apId -> getAppByAPId?.invoke(apId)?.uid } + .mapNotNull { apId -> appListsRepository.getApp(apId)?.uid } .toSet() } private fun refreshAppUidTrackersWhiteList() { trackersWhitelistByUid = trackersWhitelistByApp.mapNotNull { (apId, value) -> - getAppByAPId?.invoke(apId)?.uid?.let { uid -> + appListsRepository.getApp(apId)?.uid?.let { uid -> uid to value } }.toMap() @@ -190,9 +180,7 @@ class WhitelistRepository private constructor(context: Context) { } fun getWhiteListedApp(): List<ApplicationDescription> { - return getAppByAPId?.let { - appsWhitelist.mapNotNull(it) - } ?: emptyList() + return appsWhitelist.mapNotNull(appListsRepository::getApp) } fun getWhiteListForApp(app: ApplicationDescription): List<String> { diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/entities/Tracker.kt index 2da5b16..5c31294 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/entities/Tracker.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers.api +package foundation.e.advancedprivacy.trackers.domain.entities /** * Describe a tracker. diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/DNSBlocker.kt index 44793a4..fb08910 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/DNSBlocker.kt @@ -16,7 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers +package foundation.e.advancedprivacy.trackers.domain.usecases import android.content.Context import android.content.pm.PackageManager @@ -24,40 +24,38 @@ import android.net.LocalServerSocket import android.system.ErrnoException import android.system.Os import android.system.OsConstants -import android.util.Log -import foundation.e.privacymodules.trackers.data.TrackersRepository -import foundation.e.privacymodules.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.core.utils.runSuspendCatching +import foundation.e.advancedprivacy.trackers.data.TrackersRepository +import foundation.e.advancedprivacy.trackers.data.WhitelistRepository +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.IOException import java.io.InputStreamReader import java.io.PrintWriter -class DNSBlockerRunnable( +class DNSBlocker( context: Context, - private val trackersLogger: TrackersLogger, + val trackersLogger: TrackersLogger, private val trackersRepository: TrackersRepository, private val whitelistRepository: WhitelistRepository -) : Runnable { - var resolverReceiver: LocalServerSocket? = null - var stopped = false +) { + private var resolverReceiver: LocalServerSocket? = null private var eBrowserAppUid = -1 companion object { private const val SOCKET_NAME = "foundation.e.advancedprivacy" private const val E_BROWSER_DOT_SERVER = "chrome.cloudflare-dns.com" - private const val TAG = "DNSBlockerRunnable" } init { initEBrowserDoTFix(context) } - @Synchronized - fun stop() { - stopped = true - closeSocket() - } - private fun closeSocket() { // Known bug and workaround that LocalServerSocket::close is not working well // https://issuetracker.google.com/issues/36945762 @@ -68,29 +66,29 @@ class DNSBlockerRunnable( resolverReceiver = null } catch (e: ErrnoException) { if (e.errno != OsConstants.EBADF) { - Log.w(TAG, "Socket already closed") + Timber.w("Socket already closed") } else { - Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e) + Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !") } } catch (e: Exception) { - Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e) + Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !") } } } - override fun run() { - val resolverReceiver = try { + fun listenJob(scope: CoroutineScope): Job = scope.launch(Dispatchers.IO) { + val resolverReceiver = runSuspendCatching { LocalServerSocket(SOCKET_NAME) - } catch (eio: IOException) { - Log.e(TAG, "Exception:Cannot open DNS port $SOCKET_NAME !", eio) - return + }.getOrElse { + Timber.e(it, "Exception: cannot open DNS port on $SOCKET_NAME") + return@launch } - this.resolverReceiver = resolverReceiver - Log.d(TAG, "DNSFilterProxy running on port $SOCKET_NAME !") + this@DNSBlocker.resolverReceiver = resolverReceiver + Timber.d("DNSFilterProxy running on port $SOCKET_NAME") - while (!stopped) { - try { + while (isActive) { + runSuspendCatching { val socket = resolverReceiver.accept() val reader = BufferedReader(InputStreamReader(socket.inputStream)) val line = reader.readLine() @@ -114,9 +112,13 @@ class DNSBlockerRunnable( writer.println("pass") } socket.close() - // Printing bufferedreader data - } catch (e: IOException) { - Log.w(TAG, "Exception while listening DNS resolver", e) + }.onFailure { + if (it is CancellationException) { + closeSocket() + throw it + } else { + Timber.w(it, "Exception while listening DNS resolver") + } } } } @@ -126,7 +128,7 @@ class DNSBlockerRunnable( eBrowserAppUid = context.packageManager.getApplicationInfo("foundation.e.browser", 0).uid } catch (e: PackageManager.NameNotFoundException) { - Log.i(TAG, "no E Browser package found.") + Timber.i(e, "no E Browser package found.") } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt index 8f02adb..55efeb9 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt @@ -16,46 +16,27 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers.data - -import android.content.Context -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.api.Tracker +package foundation.e.advancedprivacy.trackers.domain.usecases + +import foundation.e.advancedprivacy.data.repositories.AppListsRepository +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.trackers.data.StatsDatabase +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import java.time.temporal.TemporalUnit -class StatsRepository private constructor(context: Context) { - private val database: StatsDatabase - private var newDataCallback: (() -> Unit)? = null - private var getAppByUid: ((Int) -> ApplicationDescription?)? = null - private var getAppByAPId: ((String) -> ApplicationDescription?)? = null - - companion object { - private var instance: StatsRepository? = null - fun getInstance(context: Context): StatsRepository { - return instance ?: StatsRepository(context).apply { instance = this } - } - } - - fun setAppGetters( - getAppByUid: (Int) -> ApplicationDescription?, - getAppByAPId: (String) -> ApplicationDescription? - ) { - this.getAppByUid = getAppByUid - this.getAppByAPId = getAppByAPId - } - - init { - database = StatsDatabase(context) - } - - fun setNewDataCallback(callback: () -> Unit) { - newDataCallback = callback - } +class StatisticsUseCase( + private val database: StatsDatabase, + private val appListsRepository: AppListsRepository +) { + private val _newDataAvailable = MutableSharedFlow<Unit>() + val newDataAvailable: SharedFlow<Unit> = _newDataAvailable - fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) { - getAppByUid?.invoke(appUid)?.let { app -> + suspend fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) { + appListsRepository.getApp(appUid)?.let { app -> database.logAccess(trackerId, app.apId, blocked) - newDataCallback?.invoke() + _newDataAvailable.emit(Unit) } } @@ -94,12 +75,12 @@ class StatsRepository private constructor(context: Context) { } fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): ApplicationDescription? { - return getAppByAPId?.invoke(database.getMostLeakedAppId(periodCount, periodUnit)) + return appListsRepository.getApp(database.getMostLeakedAppId(periodCount, periodUnit)) } private fun <K> Map<String, K>.mapByAppIdToApp(): Map<ApplicationDescription, K> { return entries.mapNotNull { (apId, value) -> - getAppByAPId?.invoke(apId)?.let { it to value } + appListsRepository.getApp(apId)?.let { it to value } }.toMap() } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/TrackersLogger.kt index f3c4745..411b4ab 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/TrackersLogger.kt @@ -16,49 +16,40 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers - -import android.content.Context -import android.util.Log -import foundation.e.privacymodules.trackers.data.StatsRepository +package foundation.e.advancedprivacy.trackers.domain.usecases + +import foundation.e.advancedprivacy.core.utils.runSuspendCatching +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.util.concurrent.LinkedBlockingQueue -class TrackersLogger(context: Context) { - private val statsRepository = StatsRepository.getInstance(context) +class TrackersLogger( + private val statisticsUseCase: StatisticsUseCase, +) { private val queue = LinkedBlockingQueue<DetectedTracker>() - private var stopped = false - - companion object { - private const val TAG = "TrackerModule" - } - - init { - startWriteLogLoop() - } - - fun stop() { - stopped = true - } fun logAccess(trackerId: String?, appUid: Int, wasBlocked: Boolean) { queue.offer(DetectedTracker(trackerId, appUid, wasBlocked)) } - private fun startWriteLogLoop() { - val writeLogRunner = Runnable { - while (!stopped) { - try { + fun writeLogJob(scope: CoroutineScope): Job { + return scope.launch(Dispatchers.IO) { + while (isActive) { + runSuspendCatching { logAccess(queue.take()) - } catch (e: InterruptedException) { - Log.e(TAG, "writeLogLoop detectedTrackersQueue.take() interrupted: ", e) + }.onFailure { + Timber.e(it, "writeLogLoop detectedTrackersQueue.take() interrupted: ") } } } - Thread(writeLogRunner).start() } - fun logAccess(detectedTracker: DetectedTracker) { - statsRepository.logAccess( + private suspend fun logAccess(detectedTracker: DetectedTracker) { + statisticsUseCase.logAccess( detectedTracker.trackerId, detectedTracker.appUid, detectedTracker.wasBlocked diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt new file mode 100644 index 0000000..3593dbb --- /dev/null +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt @@ -0,0 +1,29 @@ +package foundation.e.advancedprivacy.trackers.domain.usecases + +import foundation.e.advancedprivacy.data.repositories.ETrackersApi +import foundation.e.advancedprivacy.data.repositories.RemoteTrackersListRepository +import foundation.e.advancedprivacy.trackers.data.TrackersRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import timber.log.Timber + +class UpdateTrackerListUseCase( + private val remoteTrackersListRepository: RemoteTrackersListRepository, + private val trackersRepository: TrackersRepository, + private val coroutineScope: CoroutineScope, + +) { + fun updateTrackers() = coroutineScope.launch { + update() + } + + suspend fun update() { + val api = ETrackersApi.build() + try { + remoteTrackersListRepository.saveData(trackersRepository.eTrackerFile, api.trackers()) + trackersRepository.initTrackersFile() + } catch (e: Exception) { + Timber.e("While updating trackers", e) + } + } +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/DNSBlockerService.kt index c2ad16b..25539e1 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/DNSBlockerService.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 @@ -15,28 +16,28 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers +package foundation.e.advancedprivacy.trackers.services import android.app.Service import android.content.Intent import android.os.IBinder -import android.util.Log -import foundation.e.privacymodules.trackers.data.TrackersRepository -import foundation.e.privacymodules.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.trackers.domain.usecases.DNSBlocker +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import org.koin.java.KoinJavaComponent.get class DNSBlockerService : Service() { - private var trackersLogger: TrackersLogger? = null - companion object { - private const val TAG = "DNSBlockerService" - private var sDNSBlocker: DNSBlockerRunnable? = null const val ACTION_START = "foundation.e.privacymodules.trackers.intent.action.START" const val EXTRA_ENABLE_NOTIFICATION = "foundation.e.privacymodules.trackers.intent.extra.ENABLED_NOTIFICATION" } + private var coroutineScope = CoroutineScope(Dispatchers.IO) + private var dnsBlocker: DNSBlocker? = null + override fun onBind(intent: Intent): IBinder? { - // TODO: Return the communication channel to the service. throw UnsupportedOperationException("Not yet implemented") } @@ -52,28 +53,16 @@ class DNSBlockerService : Service() { } private fun start() { - try { - val trackersLogger = TrackersLogger(this) - this.trackersLogger = trackersLogger - - sDNSBlocker = DNSBlockerRunnable( - this, - trackersLogger, - TrackersRepository.getInstance(), - WhitelistRepository.getInstance(this) - ) - Thread(sDNSBlocker).start() - } catch (e: Exception) { - Log.e(TAG, "Error while starting DNSBlocker service", e) - stop() + coroutineScope = CoroutineScope(Dispatchers.IO) + get<DNSBlocker>(DNSBlocker::class.java).apply { + this@DNSBlockerService.dnsBlocker = this + trackersLogger.writeLogJob(coroutineScope) + listenJob(coroutineScope) } } private fun stop() { - sDNSBlocker?.stop() - sDNSBlocker = null - - trackersLogger?.stop() - trackersLogger = null + kotlin.runCatching { coroutineScope.cancel() } + dnsBlocker = null } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/ForegroundStarter.kt index 69b4f28..a0cea43 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/ForegroundStarter.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.privacymodules.trackers +package foundation.e.advancedprivacy.trackers.services import android.app.Notification import android.app.NotificationChannel diff --git a/app/src/main/java/foundation/e/advancedprivacy/UpdateTrackersWorker.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/UpdateTrackersWorker.kt index 418f75b..50aa082 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/UpdateTrackersWorker.kt +++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/UpdateTrackersWorker.kt @@ -15,7 +15,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -package foundation.e.advancedprivacy +package foundation.e.advancedprivacy.trackers.services import android.content.Context import androidx.work.Constraints @@ -25,16 +25,17 @@ import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters +import foundation.e.advancedprivacy.trackers.domain.usecases.UpdateTrackerListUseCase +import org.koin.java.KoinJavaComponent.get import java.util.concurrent.TimeUnit class UpdateTrackersWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) { override suspend fun doWork(): Result { - val trackersStateUseCase = (applicationContext as AdvancedPrivacyApplication) - .dependencyContainer.trackersStateUseCase + val updateTrackersUsecase: UpdateTrackerListUseCase = get(UpdateTrackerListUseCase::class.java) - trackersStateUseCase.updateTrackers() + updateTrackersUsecase.updateTrackers() return Result.success() } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt deleted file mode 100644 index 7463b22..0000000 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt +++ /dev/null @@ -1,98 +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.privacymodules.trackers.api - -import android.content.Context -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.data.TrackersRepository -import foundation.e.privacymodules.trackers.data.WhitelistRepository - -class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule { - private val mListeners = mutableListOf<IBlockTrackersPrivacyModule.Listener>() - private val trackersRepository = TrackersRepository.getInstance() - private val whitelistRepository = WhitelistRepository.getInstance(context) - - companion object { - private var instance: BlockTrackersPrivacyModule? = null - - fun getInstance(context: Context): BlockTrackersPrivacyModule { - return instance ?: BlockTrackersPrivacyModule(context).apply { instance = this } - } - } - - override fun addListener(listener: IBlockTrackersPrivacyModule.Listener) { - mListeners.add(listener) - } - - override fun clearListeners() { - mListeners.clear() - } - - override fun disableBlocking() { - whitelistRepository.isBlockingEnabled = false - mListeners.forEach { listener -> listener.onBlockingToggle(false) } - } - - override fun enableBlocking() { - whitelistRepository.isBlockingEnabled = true - mListeners.forEach { listener -> listener.onBlockingToggle(true) } - } - - override fun getWhiteList(app: ApplicationDescription): List<Tracker> { - return whitelistRepository.getWhiteListForApp(app).mapNotNull { - trackersRepository.getTracker(it) - } - } - - override fun getWhiteListedApp(): List<ApplicationDescription> { - return whitelistRepository.getWhiteListedApp() - } - - override fun isBlockingEnabled(): Boolean { - return whitelistRepository.isBlockingEnabled - } - - override fun isWhiteListEmpty(): Boolean { - return whitelistRepository.areWhiteListEmpty() - } - - override fun isWhitelisted(app: ApplicationDescription): Boolean { - return whitelistRepository.isAppWhiteListed(app) - } - - override fun removeListener(listener: IBlockTrackersPrivacyModule.Listener) { - mListeners.remove(listener) - } - - override fun setWhiteListed( - tracker: Tracker, - app: ApplicationDescription, - isWhiteListed: Boolean - ) { - whitelistRepository.setWhiteListed(tracker, app.apId, isWhiteListed) - } - - override fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean) { - whitelistRepository.setWhiteListed(app.apId, isWhiteListed) - } - - override fun clearWhiteList(app: ApplicationDescription) { - whitelistRepository.clearWhiteList(app.apId) - } -} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt deleted file mode 100644 index 3547b8e..0000000 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt +++ /dev/null @@ -1,98 +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.privacymodules.trackers.api - -import foundation.e.privacymodules.permissions.data.ApplicationDescription - -/** - * Manage trackers blocking and whitelisting. - */ -interface IBlockTrackersPrivacyModule { - - /** - * Get the state of the blockin module - * @return true when blocking is enabled, false otherwise. - */ - fun isBlockingEnabled(): Boolean - - /** - * Enable blocking, using the previously configured whitelists - */ - fun enableBlocking() - - /** - * Disable blocking - */ - fun disableBlocking() - - /** - * Set or unset in whitelist the App with the specified uid. - * @param app the ApplicationDescription of the app - * @param isWhiteListed true, the app will appears in whitelist, false, it won't - */ - fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean) - - /** - * Set or unset in whitelist the specifid tracked, for the App specified by its uid. - * @param tracker the tracker - * @param app the ApplicationDescription of the app - * @param isWhiteListed true, the app will appears in whitelist, false, it won't - */ - fun setWhiteListed(tracker: Tracker, app: ApplicationDescription, isWhiteListed: Boolean) - - /** - * Return true if nothing has been added to the whitelist : everything is blocked. - */ - fun isWhiteListEmpty(): Boolean - - /** - * Return the white listed App, by their UID - */ - fun getWhiteListedApp(): List<ApplicationDescription> - - /** - * Return true if the App is whitelisted for trackers blocking. - */ - fun isWhitelisted(app: ApplicationDescription): Boolean - - /** - * List the white listed trackers for an App specified by it uid - */ - fun getWhiteList(app: ApplicationDescription): List<Tracker> - - fun clearWhiteList(app: ApplicationDescription) - - /** - * Callback interface to get updates about the state of the Block trackers module. - */ - interface Listener { - - /** - * Called when the trackers blocking is activated or deactivated. - * @param isBlocking true when activated, false otherwise. - */ - fun onBlockingToggle(isBlocking: Boolean) - } - - fun addListener(listener: Listener) - - fun removeListener(listener: Listener) - - fun clearListeners() -} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt deleted file mode 100644 index 8aaed4a..0000000 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt +++ /dev/null @@ -1,110 +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.privacymodules.trackers.api - -import foundation.e.privacymodules.permissions.data.ApplicationDescription - -/** - * Get reporting about trackers calls. - */ -interface ITrackTrackersPrivacyModule { - - fun start( - trackers: List<Tracker>, - getAppByUid: (Int) -> ApplicationDescription?, - getAppByAPId: (String) -> ApplicationDescription?, - enableNotification: Boolean = true - ) - - /** - * List all the trackers encountered for a specific app. - */ - fun getTrackersForApp(app: ApplicationDescription): List<Tracker> - - /** - * List all the trackers encountere trackers since "ever", for the given [appUids], - * or all apps if [appUids] is null - */ - fun getTrackers(apps: List<ApplicationDescription>? = null): List<Tracker> - - /** - * Return the number of encountered trackers since "ever", for the given [appUids], - * or all apps if [appUids] is null - */ - fun getTrackersCount(): Int - - /** - * Return the number of encountere trackers since "ever", for each app uid. - */ - fun getTrackersCountByApp(): Map<ApplicationDescription, Int> - - /** - * Return the number of encountered trackers for the last 24 hours - */ - fun getPastDayTrackersCount(): Int - - /** - * Return the number of encountered trackers for the last month - */ - fun getPastMonthTrackersCount(): Int - - /** - * Return the number of encountered trackers for the last year - */ - fun getPastYearTrackersCount(): Int - - /** - * Return number of trackers calls by hours, for the last 24hours. - * @return list of 24 numbers of trackers calls by hours - */ - fun getPastDayTrackersCalls(): List<Pair<Int, Int>> - - /** - * Return number of trackers calls by day, for the last 30 days. - * @return list of 30 numbers of trackers calls by day - */ - fun getPastMonthTrackersCalls(): List<Pair<Int, Int>> - - /** - * Return number of trackers calls by month, for the last 12 month. - * @return list of 12 numbers of trackers calls by month - */ - fun getPastYearTrackersCalls(): List<Pair<Int, Int>> - - fun getPastDayTrackersCallsByApps(): Map<ApplicationDescription, Pair<Int, Int>> - - fun getPastDayTrackersCallsForApp(app: ApplicationDescription): Pair<Int, Int> - - fun getPastDayMostLeakedApp(): ApplicationDescription? - - interface Listener { - - /** - * Called when a new tracker attempt is logged. Consumer may choose to call other methods - * to refresh the data. - */ - fun onNewData() - } - - fun addListener(listener: Listener) - - fun removeListener(listener: Listener) - - fun clearListeners() -} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt deleted file mode 100644 index 5fc5b6b..0000000 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt +++ /dev/null @@ -1,126 +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.privacymodules.trackers.api - -import android.content.Context -import android.content.Intent -import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.DNSBlockerService -import foundation.e.privacymodules.trackers.data.StatsRepository -import foundation.e.privacymodules.trackers.data.TrackersRepository -import foundation.e.privacymodules.trackers.data.WhitelistRepository -import java.time.temporal.ChronoUnit - -class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersPrivacyModule { - private val statsRepository = StatsRepository.getInstance(context) - private val listeners: MutableList<ITrackTrackersPrivacyModule.Listener> = mutableListOf() - - companion object { - private var instance: TrackTrackersPrivacyModule? = null - - fun getInstance(context: Context): TrackTrackersPrivacyModule { - return instance ?: TrackTrackersPrivacyModule(context).apply { instance = this } - } - } - - init { - statsRepository.setNewDataCallback { - listeners.forEach { listener -> listener.onNewData() } - } - } - - override fun start( - trackers: List<Tracker>, - getAppByUid: (Int) -> ApplicationDescription?, - getAppByAPId: (String) -> ApplicationDescription?, - enableNotification: Boolean - ) { - TrackersRepository.getInstance().setTrackersList(trackers) - StatsRepository.getInstance(context).setAppGetters(getAppByUid, getAppByAPId) - WhitelistRepository.getInstance(context).setAppGetters(context, getAppByAPId, getAppByUid) - val intent = Intent(context, DNSBlockerService::class.java) - intent.action = DNSBlockerService.ACTION_START - intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, enableNotification) - context.startService(intent) - } - - override fun getPastDayTrackersCalls(): List<Pair<Int, Int>> { - return statsRepository.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS) - } - - override fun getPastMonthTrackersCalls(): List<Pair<Int, Int>> { - return statsRepository.getTrackersCallsOnPeriod(30, ChronoUnit.DAYS) - } - - override fun getPastYearTrackersCalls(): List<Pair<Int, Int>> { - return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS) - } - - override fun getTrackersCount(): Int { - return statsRepository.getContactedTrackersCount() - } - - override fun getTrackersCountByApp(): Map<ApplicationDescription, Int> { - return statsRepository.getContactedTrackersCountByApp() - } - - override fun getTrackersForApp(app: ApplicationDescription): List<Tracker> { - return statsRepository.getTrackers(listOf(app)) - } - - override fun getTrackers(apps: List<ApplicationDescription>?): List<Tracker> { - return statsRepository.getTrackers(apps) - } - - override fun getPastDayTrackersCount(): Int { - return statsRepository.getActiveTrackersByPeriod(24, ChronoUnit.HOURS) - } - - override fun getPastMonthTrackersCount(): Int { - return statsRepository.getActiveTrackersByPeriod(30, ChronoUnit.DAYS) - } - - override fun getPastYearTrackersCount(): Int { - return statsRepository.getActiveTrackersByPeriod(12, ChronoUnit.MONTHS) - } - - override fun getPastDayMostLeakedApp(): ApplicationDescription? { - return statsRepository.getMostLeakedApp(24, ChronoUnit.HOURS) - } - - override fun getPastDayTrackersCallsByApps(): Map<ApplicationDescription, Pair<Int, Int>> { - return statsRepository.getCallsByApps(24, ChronoUnit.HOURS) - } - - override fun getPastDayTrackersCallsForApp(app: ApplicationDescription): Pair<Int, Int> { - return statsRepository.getCalls(app, 24, ChronoUnit.HOURS) - } - - override fun addListener(listener: ITrackTrackersPrivacyModule.Listener) { - listeners.add(listener) - } - - override fun removeListener(listener: ITrackTrackersPrivacyModule.Listener) { - listeners.remove(listener) - } - - override fun clearListeners() { - listeners.clear() - } -} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt deleted file mode 100644 index 994bccf..0000000 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt +++ /dev/null @@ -1,57 +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.privacymodules.trackers.data - -import foundation.e.privacymodules.trackers.api.Tracker - -class TrackersRepository private constructor() { - private var trackersById: Map<String, Tracker> = HashMap() - private var hostnameToId: Map<String, String> = HashMap() - - companion object { - private var instance: TrackersRepository? = null - fun getInstance(): TrackersRepository { - return instance ?: TrackersRepository().apply { instance = this } - } - } - - fun setTrackersList(list: List<Tracker>) { - val trackersById: MutableMap<String, Tracker> = HashMap() - val hostnameToId: MutableMap<String, String> = HashMap() - list.forEach { tracker -> - trackersById[tracker.id] = tracker - for (hostname in tracker.hostnames) { - hostnameToId[hostname] = tracker.id - } - } - this.trackersById = trackersById - this.hostnameToId = hostnameToId - } - - fun isTracker(hostname: String?): Boolean { - return hostnameToId.containsKey(hostname) - } - - fun getTrackerId(hostname: String?): String? { - return hostnameToId[hostname] - } - - fun getTracker(id: String?): Tracker? { - return trackersById[id] - } -} |