Skip to content

Commit c6ae3d9

Browse files
committed
Move system app categories to remote config
1 parent 587ad2c commit c6ae3d9

File tree

6 files changed

+186
-30
lines changed

6 files changed

+186
-30
lines changed

network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/VpnRemoteFeatures.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import kotlinx.coroutines.launch
4040
scope = AppScope::class,
4141
featureName = "networkProtection",
4242
toggleStore = VpnRemoteFeaturesStore::class,
43+
settingsStore = VpnRemoteSettingsStore::class,
4344
)
4445
interface VpnRemoteFeatures {
4546
@Toggle.DefaultValue(true)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (c) 2024 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.networkprotection.impl
18+
19+
import com.duckduckgo.app.di.AppCoroutineScope
20+
import com.duckduckgo.common.utils.DispatcherProvider
21+
import com.duckduckgo.di.scopes.AppScope
22+
import com.duckduckgo.feature.toggles.api.FeatureSettings
23+
import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed
24+
import com.duckduckgo.networkprotection.api.NetworkProtectionState
25+
import com.duckduckgo.networkprotection.store.db.CategorizedSystemApp
26+
import com.duckduckgo.networkprotection.store.db.CategorizedSystemAppsDao
27+
import com.squareup.anvil.annotations.ContributesBinding
28+
import com.squareup.moshi.Json
29+
import com.squareup.moshi.Moshi
30+
import dagger.SingleInstanceIn
31+
import javax.inject.Inject
32+
import kotlinx.coroutines.CoroutineScope
33+
import kotlinx.coroutines.launch
34+
import logcat.LogPriority
35+
import logcat.asLog
36+
import logcat.logcat
37+
38+
@ContributesBinding(AppScope::class)
39+
@RemoteFeatureStoreNamed(VpnRemoteFeatures::class)
40+
@SingleInstanceIn(AppScope::class)
41+
class VpnRemoteSettingsStore @Inject constructor(
42+
@AppCoroutineScope private val coroutineScope: CoroutineScope,
43+
private val dispatcherProvider: DispatcherProvider,
44+
private val networkProtectionState: NetworkProtectionState,
45+
private val categorizedSystemAppsDao: CategorizedSystemAppsDao,
46+
) : FeatureSettings.Store {
47+
48+
private val jsonAdapter = Moshi.Builder().build().adapter(SettingsModel::class.java)
49+
50+
override fun store(jsonString: String) {
51+
logcat { "Received configuration: $jsonString" }
52+
53+
runCatching {
54+
jsonAdapter.fromJson(jsonString)?.let { model ->
55+
model.systemAppCategories.also {
56+
if (it.isNotEmpty()) {
57+
categorizedSystemAppsDao.upsertSystemAppCategories(it)
58+
}
59+
}
60+
61+
// Restart VPN now that the lists were updated
62+
coroutineScope.launch(dispatcherProvider.io()) {
63+
networkProtectionState.restart()
64+
}
65+
}
66+
}.onFailure {
67+
logcat(LogPriority.WARN) { it.asLog() }
68+
}
69+
}
70+
71+
data class SettingsModel(
72+
@field:Json(name = "systemAppCategories")
73+
val systemAppCategories: List<CategorizedSystemApp>,
74+
)
75+
}

network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/di/NetworkProtectionModule.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.duckduckgo.networkprotection.store.NetworkProtectionPrefs
3131
import com.duckduckgo.networkprotection.store.RealNetPExclusionListRepository
3232
import com.duckduckgo.networkprotection.store.RealNetPGeoswitchingRepository
3333
import com.duckduckgo.networkprotection.store.RealNetworkProtectionPrefs
34+
import com.duckduckgo.networkprotection.store.db.CategorizedSystemAppsDao
3435
import com.duckduckgo.networkprotection.store.db.NetPDatabase
3536
import com.duckduckgo.networkprotection.store.remote_config.NetPConfigTogglesDao
3637
import com.squareup.anvil.annotations.ContributesTo
@@ -74,6 +75,14 @@ object DataModule {
7475
): NetPGeoswitchingRepository {
7576
return RealNetPGeoswitchingRepository(networkProtectionPrefs, database.geoswitchingDao(), dispatcherProvider)
7677
}
78+
79+
@Provides
80+
@SingleInstanceIn(AppScope::class)
81+
fun provideCategorizedSystemAppsDao(
82+
database: NetPDatabase,
83+
): CategorizedSystemAppsDao {
84+
return database.categorizedSystemAppsDao()
85+
}
7786
}
7887

7988
@Module

network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/exclusion/systemapps/SystemAppsExclusionRepository.kt

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import com.duckduckgo.networkprotection.impl.exclusion.systemapps.SystemAppsExcl
3232
import com.duckduckgo.networkprotection.impl.exclusion.systemapps.SystemAppsExclusionRepository.SystemAppCategory.Networking
3333
import com.duckduckgo.networkprotection.impl.exclusion.systemapps.SystemAppsExclusionRepository.SystemAppCategory.Others
3434
import com.duckduckgo.networkprotection.impl.settings.NetPSettingsLocalConfig
35+
import com.duckduckgo.networkprotection.store.db.CategorizedSystemAppsDao
3536
import com.squareup.anvil.annotations.ContributesBinding
3637
import dagger.SingleInstanceIn
3738
import javax.inject.Inject
@@ -68,6 +69,7 @@ class RealSystemAppsExclusionRepository @Inject constructor(
6869
private val packageManager: PackageManager,
6970
private val systemAppOverridesProvider: SystemAppOverridesProvider,
7071
private val dispatcherProvider: DispatcherProvider,
72+
private val categorizedSystemAppsDao: CategorizedSystemAppsDao,
7173
) : SystemAppsExclusionRepository {
7274
private val preferences: SharedPreferences by lazy {
7375
sharedPreferencesProvider.getSharedPreferences(
@@ -147,38 +149,16 @@ class RealSystemAppsExclusionRepository @Inject constructor(
147149
includeCategory(Others)
148150
}
149151

150-
private fun getCommunicationSystemApps(): Set<String> {
151-
return setOf(
152-
"com.android.calllogbackup",
153-
"com.android.cellbroadcastreceiver",
154-
"com.android.mms.service",
155-
"com.android.phone",
156-
"com.android.providers.contacts",
157-
"com.android.providers.telephony",
158-
"com.android.service.ims",
159-
"com.google.android.apps.messaging",
160-
"com.google.android.gms",
161-
"com.google.android.telephony",
162-
"org.codeaurora.ims",
163-
)
152+
private fun getCommunicationSystemApps(): List<String> {
153+
return categorizedSystemAppsDao.getCommunicationSystemApps().map { it.packageName }
164154
}
165155

166-
private fun getNetworkingSystemApps(): Set<String> {
167-
return setOf(
168-
"com.android.bluetooth",
169-
"com.android.nfc",
170-
"com.google.android.networkstack",
171-
"com.google.android.networkstack.tethering",
172-
)
156+
private fun getNetworkingSystemApps(): List<String> {
157+
return categorizedSystemAppsDao.getNetworkingSystemApps().map { it.packageName }
173158
}
174159

175-
private fun getMediaSystemApps(): Set<String> {
176-
return setOf(
177-
"com.android.providers.media",
178-
"com.google.android.providers.media.module",
179-
"com.google.android.music",
180-
"com.google.android.videos",
181-
)
160+
private fun getMediaSystemApps(): List<String> {
161+
return categorizedSystemAppsDao.getMediaSystemApps().map { it.packageName }
182162
}
183163

184164
private fun getOtherSystemApps(): Set<String> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) 2024 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.networkprotection.store.db
18+
19+
import androidx.room.Dao
20+
import androidx.room.Entity
21+
import androidx.room.Insert
22+
import androidx.room.OnConflictStrategy
23+
import androidx.room.PrimaryKey
24+
import androidx.room.Query
25+
import androidx.room.Transaction
26+
27+
@Dao
28+
interface CategorizedSystemAppsDao {
29+
@Query("SELECT * from netp_system_apps_categories where category = 'COMMUNICATION'")
30+
fun getCommunicationSystemApps(): List<CategorizedSystemApp>
31+
32+
@Query("SELECT * from netp_system_apps_categories where category = 'NETWORKING'")
33+
fun getNetworkingSystemApps(): List<CategorizedSystemApp>
34+
35+
@Query("SELECT * from netp_system_apps_categories where category = 'MEDIA'")
36+
fun getMediaSystemApps(): List<CategorizedSystemApp>
37+
38+
@Transaction
39+
fun upsertSystemAppCategories(
40+
systemAppCategories: List<CategorizedSystemApp>,
41+
) {
42+
deleteSystemAppCategories()
43+
insertSystemAppOverrides(systemAppCategories)
44+
}
45+
46+
@Insert(onConflict = OnConflictStrategy.REPLACE)
47+
fun insertSystemAppOverrides(
48+
systemAppCategories: List<CategorizedSystemApp>,
49+
)
50+
51+
@Query("DELETE from netp_system_apps_categories")
52+
fun deleteSystemAppCategories()
53+
}
54+
55+
@Entity(tableName = "netp_system_apps_categories")
56+
data class CategorizedSystemApp(
57+
@PrimaryKey val packageName: String,
58+
val category: SystemAppCategory,
59+
)
60+
61+
enum class SystemAppCategory {
62+
COMMUNICATION,
63+
NETWORKING,
64+
MEDIA,
65+
OTHERS,
66+
}

network-protection/network-protection-store/src/main/java/com/duckduckgo/networkprotection/store/db/NetPDatabase.kt

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import androidx.room.RoomDatabase
2121
import androidx.room.TypeConverter
2222
import androidx.room.TypeConverters
2323
import androidx.room.migration.Migration
24+
import androidx.sqlite.db.SupportSQLiteDatabase
2425
import com.duckduckgo.networkprotection.store.remote_config.NetPConfigToggle
2526
import com.duckduckgo.networkprotection.store.remote_config.NetPConfigTogglesDao
2627
import com.squareup.moshi.JsonAdapter
@@ -29,22 +30,32 @@ import com.squareup.moshi.Types
2930

3031
@Database(
3132
exportSchema = true,
32-
version = 3,
33+
version = 4,
3334
entities = [
3435
NetPManuallyExcludedApp::class,
3536
NetPConfigToggle::class,
3637
NetPGeoswitchingLocation::class,
38+
CategorizedSystemApp::class,
3739
],
3840
)
3941
@TypeConverters(NetpDatabaseConverters::class)
4042
abstract class NetPDatabase : RoomDatabase() {
4143
abstract fun exclusionListDao(): NetPExclusionListDao
4244
abstract fun configTogglesDao(): NetPConfigTogglesDao
4345
abstract fun geoswitchingDao(): NetPGeoswitchingDao
46+
abstract fun categorizedSystemAppsDao(): CategorizedSystemAppsDao
4447

4548
companion object {
4649
val ALL_MIGRATIONS: List<Migration>
47-
get() = emptyList()
50+
get() = listOf(MIGRATION_3_4)
51+
private val MIGRATION_3_4: Migration = object : Migration(3, 4) {
52+
override fun migrate(db: SupportSQLiteDatabase) {
53+
db.execSQL(
54+
"CREATE TABLE IF NOT EXISTS `netp_system_apps_categories` (`packageName` TEXT NOT NULL," +
55+
" `category` TEXT NOT NULL, PRIMARY KEY(`packageName`))",
56+
)
57+
}
58+
}
4859
}
4960
}
5061

@@ -64,4 +75,18 @@ object NetpDatabaseConverters {
6475
fun fromStringList(value: List<String>): String {
6576
return stringListAdapter.toJson(value)
6677
}
78+
79+
@TypeConverter
80+
fun toSystemAppCategory(category: String): SystemAppCategory {
81+
return try {
82+
SystemAppCategory.valueOf(category)
83+
} catch (ex: IllegalArgumentException) {
84+
SystemAppCategory.OTHERS
85+
}
86+
}
87+
88+
@TypeConverter
89+
fun fromStage(stage: SystemAppCategory): String {
90+
return stage.name
91+
}
6792
}

0 commit comments

Comments
 (0)