Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move system app categories to remote config #5100

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import kotlinx.coroutines.launch
scope = AppScope::class,
featureName = "networkProtection",
toggleStore = VpnRemoteFeaturesStore::class,
settingsStore = VpnRemoteSettingsStore::class,
)
interface VpnRemoteFeatures {
@Toggle.DefaultValue(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.networkprotection.impl

import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.feature.toggles.api.FeatureSettings
import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed
import com.duckduckgo.networkprotection.api.NetworkProtectionState
import com.duckduckgo.networkprotection.store.db.CategorizedSystemApp
import com.duckduckgo.networkprotection.store.db.CategorizedSystemAppsDao
import com.squareup.anvil.annotations.ContributesBinding
import com.squareup.moshi.Json
import com.squareup.moshi.Moshi
import dagger.SingleInstanceIn
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import logcat.LogPriority
import logcat.asLog
import logcat.logcat

@ContributesBinding(AppScope::class)
@RemoteFeatureStoreNamed(VpnRemoteFeatures::class)
@SingleInstanceIn(AppScope::class)
class VpnRemoteSettingsStore @Inject constructor(
@AppCoroutineScope private val coroutineScope: CoroutineScope,
private val dispatcherProvider: DispatcherProvider,
private val networkProtectionState: NetworkProtectionState,
private val categorizedSystemAppsDao: CategorizedSystemAppsDao,
) : FeatureSettings.Store {

private val jsonAdapter = Moshi.Builder().build().adapter(SettingsModel::class.java)

override fun store(jsonString: String) {
logcat { "Received configuration: $jsonString" }

runCatching {
jsonAdapter.fromJson(jsonString)?.let { model ->
model.systemAppCategories.also {
if (it.isNotEmpty()) {
categorizedSystemAppsDao.upsertSystemAppCategories(it)
}
}

// Restart VPN now that the lists were updated
coroutineScope.launch(dispatcherProvider.io()) {
networkProtectionState.restart()
}
}
}.onFailure {
logcat(LogPriority.WARN) { it.asLog() }
}
}

data class SettingsModel(
@field:Json(name = "systemAppCategories")
val systemAppCategories: List<CategorizedSystemApp>,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.duckduckgo.networkprotection.store.NetworkProtectionPrefs
import com.duckduckgo.networkprotection.store.RealNetPExclusionListRepository
import com.duckduckgo.networkprotection.store.RealNetPGeoswitchingRepository
import com.duckduckgo.networkprotection.store.RealNetworkProtectionPrefs
import com.duckduckgo.networkprotection.store.db.CategorizedSystemAppsDao
import com.duckduckgo.networkprotection.store.db.NetPDatabase
import com.duckduckgo.networkprotection.store.remote_config.NetPConfigTogglesDao
import com.squareup.anvil.annotations.ContributesTo
Expand Down Expand Up @@ -74,6 +75,14 @@ object DataModule {
): NetPGeoswitchingRepository {
return RealNetPGeoswitchingRepository(networkProtectionPrefs, database.geoswitchingDao(), dispatcherProvider)
}

@Provides
@SingleInstanceIn(AppScope::class)
fun provideCategorizedSystemAppsDao(
database: NetPDatabase,
): CategorizedSystemAppsDao {
return database.categorizedSystemAppsDao()
}
}

@Module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.duckduckgo.networkprotection.impl.exclusion.systemapps.SystemAppsExcl
import com.duckduckgo.networkprotection.impl.exclusion.systemapps.SystemAppsExclusionRepository.SystemAppCategory.Networking
import com.duckduckgo.networkprotection.impl.exclusion.systemapps.SystemAppsExclusionRepository.SystemAppCategory.Others
import com.duckduckgo.networkprotection.impl.settings.NetPSettingsLocalConfig
import com.duckduckgo.networkprotection.store.db.CategorizedSystemAppsDao
import com.squareup.anvil.annotations.ContributesBinding
import dagger.SingleInstanceIn
import javax.inject.Inject
Expand Down Expand Up @@ -68,6 +69,7 @@ class RealSystemAppsExclusionRepository @Inject constructor(
private val packageManager: PackageManager,
private val systemAppOverridesProvider: SystemAppOverridesProvider,
private val dispatcherProvider: DispatcherProvider,
private val categorizedSystemAppsDao: CategorizedSystemAppsDao,
) : SystemAppsExclusionRepository {
private val preferences: SharedPreferences by lazy {
sharedPreferencesProvider.getSharedPreferences(
Expand Down Expand Up @@ -147,38 +149,16 @@ class RealSystemAppsExclusionRepository @Inject constructor(
includeCategory(Others)
}

private fun getCommunicationSystemApps(): Set<String> {
return setOf(
"com.android.calllogbackup",
"com.android.cellbroadcastreceiver",
"com.android.mms.service",
"com.android.phone",
"com.android.providers.contacts",
"com.android.providers.telephony",
"com.android.service.ims",
"com.google.android.apps.messaging",
"com.google.android.gms",
"com.google.android.telephony",
"org.codeaurora.ims",
)
private fun getCommunicationSystemApps(): List<String> {
return categorizedSystemAppsDao.getCommunicationSystemApps().map { it.packageName }
}

private fun getNetworkingSystemApps(): Set<String> {
return setOf(
"com.android.bluetooth",
"com.android.nfc",
"com.google.android.networkstack",
"com.google.android.networkstack.tethering",
)
private fun getNetworkingSystemApps(): List<String> {
return categorizedSystemAppsDao.getNetworkingSystemApps().map { it.packageName }
}

private fun getMediaSystemApps(): Set<String> {
return setOf(
"com.android.providers.media",
"com.google.android.providers.media.module",
"com.google.android.music",
"com.google.android.videos",
)
private fun getMediaSystemApps(): List<String> {
return categorizedSystemAppsDao.getMediaSystemApps().map { it.packageName }
}

private fun getOtherSystemApps(): Set<String> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2024 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.networkprotection.store.db

import androidx.room.Dao
import androidx.room.Entity
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.Transaction

@Dao
interface CategorizedSystemAppsDao {
@Query("SELECT * from netp_system_apps_categories where category = 'COMMUNICATION'")
fun getCommunicationSystemApps(): List<CategorizedSystemApp>

@Query("SELECT * from netp_system_apps_categories where category = 'NETWORKING'")
fun getNetworkingSystemApps(): List<CategorizedSystemApp>

@Query("SELECT * from netp_system_apps_categories where category = 'MEDIA'")
fun getMediaSystemApps(): List<CategorizedSystemApp>

@Transaction
fun upsertSystemAppCategories(
systemAppCategories: List<CategorizedSystemApp>,
) {
deleteSystemAppCategories()
insertSystemAppOverrides(systemAppCategories)
}

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertSystemAppOverrides(
systemAppCategories: List<CategorizedSystemApp>,
)

@Query("DELETE from netp_system_apps_categories")
fun deleteSystemAppCategories()
}

@Entity(tableName = "netp_system_apps_categories")
data class CategorizedSystemApp(
@PrimaryKey val packageName: String,
val category: SystemAppCategory,
)

enum class SystemAppCategory {
COMMUNICATION,
NETWORKING,
MEDIA,
OTHERS,
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.room.RoomDatabase
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.duckduckgo.networkprotection.store.remote_config.NetPConfigToggle
import com.duckduckgo.networkprotection.store.remote_config.NetPConfigTogglesDao
import com.squareup.moshi.JsonAdapter
Expand All @@ -29,22 +30,32 @@ import com.squareup.moshi.Types

@Database(
exportSchema = true,
version = 3,
version = 4,
entities = [
NetPManuallyExcludedApp::class,
NetPConfigToggle::class,
NetPGeoswitchingLocation::class,
CategorizedSystemApp::class,
],
)
@TypeConverters(NetpDatabaseConverters::class)
abstract class NetPDatabase : RoomDatabase() {
abstract fun exclusionListDao(): NetPExclusionListDao
abstract fun configTogglesDao(): NetPConfigTogglesDao
abstract fun geoswitchingDao(): NetPGeoswitchingDao
abstract fun categorizedSystemAppsDao(): CategorizedSystemAppsDao

companion object {
val ALL_MIGRATIONS: List<Migration>
get() = emptyList()
get() = listOf(MIGRATION_3_4)
private val MIGRATION_3_4: Migration = object : Migration(3, 4) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"CREATE TABLE IF NOT EXISTS `netp_system_apps_categories` (`packageName` TEXT NOT NULL," +
" `category` TEXT NOT NULL, PRIMARY KEY(`packageName`))",
)
}
}
}
}

Expand All @@ -64,4 +75,18 @@ object NetpDatabaseConverters {
fun fromStringList(value: List<String>): String {
return stringListAdapter.toJson(value)
}

@TypeConverter
fun toSystemAppCategory(category: String): SystemAppCategory {
return try {
SystemAppCategory.valueOf(category)
} catch (ex: IllegalArgumentException) {
SystemAppCategory.OTHERS
}
}

@TypeConverter
fun fromStage(stage: SystemAppCategory): String {
return stage.name
}
}
Loading