From 07a50a6b87a0df7d25659ee355667447a00e617a Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 23 Jan 2025 20:24:22 +0530 Subject: [PATCH 1/2] parental: Remove testOnly --- app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5525eeb..2bf68c8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,6 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" - android:testOnly="true" android:supportsRtl="true" android:enableOnBackInvokedCallback="true" android:theme="@style/Theme.ParentalControl" -- GitLab From 4399d1fbb73912f736082bf64d1cfb123af0d00b Mon Sep 17 00:00:00 2001 From: althafvly Date: Wed, 29 Jan 2025 11:24:25 +0530 Subject: [PATCH 2/2] parental: Use mullvad as primary dns --- app/src/main/AndroidManifest.xml | 5 + .../e/parentalcontrol/DeviceAdmin.kt | 8 ++ .../e/parentalcontrol/DnsCheckService.kt | 114 ++++++++++++++++++ .../e/parentalcontrol/data/DnsManager.kt | 30 +++++ .../e/parentalcontrol/ui/view/MainUI.kt | 27 +++-- .../e/parentalcontrol/utils/Constants.kt | 5 + .../e/parentalcontrol/utils/PrefsUtils.kt | 11 ++ .../e/parentalcontrol/utils/SystemUtils.kt | 21 ++++ app/src/main/res/values/config.xml | 1 - app/src/main/res/values/debug_strings.xml | 2 +- 10 files changed, 210 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/foundation/e/parentalcontrol/DnsCheckService.kt create mode 100644 app/src/main/java/foundation/e/parentalcontrol/data/DnsManager.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2bf68c8..b8864cd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:sharedUserId="android.uid.system"> + + + @@ -67,6 +70,8 @@ + + { + if (isAdminActive(context)) { + val mainUI = MainUI(context) + mainUI.setPrivateDns(DnsManager.FORCE_UPDATE) + } + } else -> super.onReceive(context, intent) } } @@ -81,6 +88,7 @@ class DeviceAdmin : DeviceAdminReceiver() { val mainUI = MainUI(context) mainUI.setDefaultRestrictions() mainUI.setDefaultMessages() + context.startService(Intent(context, DnsCheckService::class.java)) } } diff --git a/app/src/main/java/foundation/e/parentalcontrol/DnsCheckService.kt b/app/src/main/java/foundation/e/parentalcontrol/DnsCheckService.kt new file mode 100644 index 0000000..b8e0ad7 --- /dev/null +++ b/app/src/main/java/foundation/e/parentalcontrol/DnsCheckService.kt @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 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 . + * + */ +package foundation.e.parentalcontrol + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.net.ConnectivityManager +import android.net.Network +import android.os.IBinder +import android.util.Log +import foundation.e.parentalcontrol.utils.Constants +import foundation.e.parentalcontrol.utils.PrefsUtils +import foundation.e.parentalcontrol.utils.SystemUtils +import java.net.InetSocketAddress +import java.net.Socket +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class DnsCheckService : Service() { + private val tag = "DnsCheckService" + private val dnsPort = 53 + + private lateinit var connectivityManager: ConnectivityManager + private lateinit var networkCallback: ConnectivityManager.NetworkCallback + + override fun onCreate() { + super.onCreate() + + // Register the network callback + networkCallback = + object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + CoroutineScope(Dispatchers.IO).launch { + delay(5000) // Wait for 5 seconds + if (SystemUtils.isNetworkAvailable(this@DnsCheckService)) { + val activeDns = + when { + isDnsReachable(Constants.DEFAULT_DNS_SERVER) -> + Constants.DEFAULT_DNS_SERVER + isDnsReachable(Constants.FALLBACK_DNS_SERVER) -> + Constants.FALLBACK_DNS_SERVER + else -> Constants.DEFAULT_DNS_SERVER + } + + Log.d(tag, "Active DNS server: $activeDns") + resetPrivateDns(activeDns) + } else { + Log.d(tag, "Internet is not working, no DNS check performed.") + resetPrivateDns(Constants.DEFAULT_DNS_SERVER) + } + } + } + + override fun onLost(network: Network) { + Log.d(tag, "Network lost, stopping DNS checks.") + } + } + + connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + connectivityManager.registerDefaultNetworkCallback(networkCallback) + + PrefsUtils.init(this) + } + + override fun onDestroy() { + super.onDestroy() + connectivityManager.unregisterNetworkCallback(networkCallback) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_STICKY + + private fun resetPrivateDns(dnsHost: String) { + PrefsUtils.setDefaultPrivateDns(dnsHost) + val intent = Intent(this, DeviceAdmin::class.java) + intent.action = Constants.RESET_PRIVATE_DNS + this.sendBroadcast(intent) + } + + private suspend fun isDnsReachable(host: String, port: Int = dnsPort): Boolean { + return withContext(Dispatchers.IO) { + try { + Socket().use { socket -> + val socketAddress = InetSocketAddress(host, port) + socket.connect(socketAddress, 2000) // 2-second timeout + true + } + } catch (e: Exception) { + e.printStackTrace() + false + } + } + } + + override fun onBind(intent: Intent?): IBinder? = null +} diff --git a/app/src/main/java/foundation/e/parentalcontrol/data/DnsManager.kt b/app/src/main/java/foundation/e/parentalcontrol/data/DnsManager.kt new file mode 100644 index 0000000..3423df8 --- /dev/null +++ b/app/src/main/java/foundation/e/parentalcontrol/data/DnsManager.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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 . + * + */ +package foundation.e.parentalcontrol.data + +import androidx.annotation.IntDef + +class DnsManager { + + companion object { + const val DEFAULT = 0 + const val FORCE_UPDATE = 1 + } + + @IntDef(DEFAULT, FORCE_UPDATE) @Retention(AnnotationRetention.SOURCE) annotation class DnsMode +} diff --git a/app/src/main/java/foundation/e/parentalcontrol/ui/view/MainUI.kt b/app/src/main/java/foundation/e/parentalcontrol/ui/view/MainUI.kt index b980069..fbff7d8 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/ui/view/MainUI.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/ui/view/MainUI.kt @@ -36,6 +36,7 @@ import com.android.net.module.util.ConnectivitySettingsUtils import foundation.e.parentalcontrol.BuildConfig import foundation.e.parentalcontrol.DeviceAdmin import foundation.e.parentalcontrol.R +import foundation.e.parentalcontrol.data.DnsManager import foundation.e.parentalcontrol.data.LoginStatus import foundation.e.parentalcontrol.providers.AppLoungeData import foundation.e.parentalcontrol.ui.buttons.ToggleWithText @@ -154,10 +155,12 @@ class MainUI(context: Context) { } } - private fun setPrivateDns() { + fun setPrivateDns(@DnsManager.DnsMode mode: Int = DnsManager.DEFAULT) { + if (!dA.isAdminActive(mContext)) return + // Set default private dns - if (!isThisRestrictionSet(dnsSettingsRestriction)) { - val cloudflareDnsHostName = mContext.getString(R.string.family_cloudflare_dns_com) + if (!isThisRestrictionSet(dnsSettingsRestriction) || mode == DnsManager.FORCE_UPDATE) { + val defaultDnsHostName = PrefsUtils.getDefaultPrivateDns() val currentDnsHostMode = Settings.Global.getString( mContext.contentResolver, @@ -184,7 +187,7 @@ class MainUI(context: Context) { Settings.Global.putString( mContext.contentResolver, ConnectivitySettingsUtils.PRIVATE_DNS_SPECIFIER, - cloudflareDnsHostName + defaultDnsHostName ) } @@ -300,30 +303,30 @@ class MainUI(context: Context) { } ) - val cloudflareDnsHostName = stringResource(R.string.family_cloudflare_dns_com) - val cloudflareToggleState = remember { + val defaultDnsHostName = Constants.DEFAULT_DNS_SERVER + val defaultToggleState = remember { val isHostname = Settings.Global.getString( mContext.contentResolver, ConnectivitySettingsUtils.PRIVATE_DNS_MODE ) == ConnectivitySettingsUtils.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME_STRING - val isCloudflareDns = + val isDefaultDns = Settings.Global.getString( mContext.contentResolver, ConnectivitySettingsUtils.PRIVATE_DNS_SPECIFIER - ) == cloudflareDnsHostName - mutableStateOf(isHostname && isCloudflareDns && dnsSettingsToggleState) + ) == defaultDnsHostName + mutableStateOf(isHostname && isDefaultDns && dnsSettingsToggleState) } ToggleWithText( - text = stringResource(R.string.block_malware_and_adult_content_with_cloudflare), - isChecked = cloudflareToggleState.value, + text = stringResource(R.string.block_malware_and_adult_content_with_dns), + isChecked = defaultToggleState.value, onCheckedChange = { isActive -> if (isActive) { setPrivateDns() } else { removePrivateDns() } - cloudflareToggleState.value = isActive + defaultToggleState.value = isActive } ) diff --git a/app/src/main/java/foundation/e/parentalcontrol/utils/Constants.kt b/app/src/main/java/foundation/e/parentalcontrol/utils/Constants.kt index 320e47b..4197c71 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/utils/Constants.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/utils/Constants.kt @@ -25,8 +25,10 @@ object Constants { const val PREF_AGE = "age" const val PREF_DNS_HOST_MODE = "dns_host_mode" const val PREF_DNS_HOST_NAME = "dns_host_name" + const val PREF_DEFAULT_DNS_HOST_NAME = "dns_default_host_name" const val RESTART_SERVICE = "foundation.e.parental_control.RESTART_SERVICE" + const val RESET_PRIVATE_DNS = "foundation.e.parental_control.RESET_PRIVATE_DNS" const val AUTHORITY = "foundation.e.parentalcontrol.provider" const val APP_LOUNGE_PKG = "foundation.e.apps" @@ -34,4 +36,7 @@ object Constants { const val REQUEST_GPLAY_LOGIN = "request_gplay_login" const val ACTION_PARENTAL_CONTROL_APP_LOUNGE_LOGIN = "foundation.e.parentalcontrol.action.APP_LOUNGE_LOGIN" + + const val DEFAULT_DNS_SERVER = "all.dns.mullvad.net" + const val FALLBACK_DNS_SERVER = "kids.dns0.eu" } diff --git a/app/src/main/java/foundation/e/parentalcontrol/utils/PrefsUtils.kt b/app/src/main/java/foundation/e/parentalcontrol/utils/PrefsUtils.kt index b57d62f..a53ff8a 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/utils/PrefsUtils.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/utils/PrefsUtils.kt @@ -80,6 +80,17 @@ object PrefsUtils { return Gson().fromJson(json, type) } + fun getDefaultPrivateDns(): String? { + return sharedPreferences.getString( + Constants.PREF_DEFAULT_DNS_HOST_NAME, + Constants.DEFAULT_DNS_SERVER + ) + } + + fun setDefaultPrivateDns(dns: String) { + getEdit().apply { putString(Constants.PREF_DEFAULT_DNS_HOST_NAME, dns) }.apply() + } + fun clearPassword() { val editPref = sharedPreferences.edit() editPref.remove(Constants.PREF_PIN_SET) diff --git a/app/src/main/java/foundation/e/parentalcontrol/utils/SystemUtils.kt b/app/src/main/java/foundation/e/parentalcontrol/utils/SystemUtils.kt index bd16f9b..7284ecd 100644 --- a/app/src/main/java/foundation/e/parentalcontrol/utils/SystemUtils.kt +++ b/app/src/main/java/foundation/e/parentalcontrol/utils/SystemUtils.kt @@ -21,6 +21,9 @@ import android.annotation.SuppressLint import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities import android.os.SystemProperties import android.os.UserHandle import android.provider.Settings @@ -73,4 +76,22 @@ object SystemUtils { (app.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0 } } + + fun isNetworkAvailable(context: Context): Boolean { + val connectivityManager = context.getSystemService(ConnectivityManager::class.java) + val activeNetwork: Network? = connectivityManager.activeNetwork + val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) + if ( + networkCapabilities != null && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + ) { + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB) || + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) || + networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + } + return false + } } diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml index 9392111..cb55489 100644 --- a/app/src/main/res/values/config.xml +++ b/app/src/main/res/values/config.xml @@ -1,5 +1,4 @@ - family.cloudflare-dns.com https://doc.e.foundation/support-topics/parental-control \ No newline at end of file diff --git a/app/src/main/res/values/debug_strings.xml b/app/src/main/res/values/debug_strings.xml index 56d0346..9c2f0b6 100644 --- a/app/src/main/res/values/debug_strings.xml +++ b/app/src/main/res/values/debug_strings.xml @@ -6,6 +6,6 @@ Disable factory reset device Disable multiple users Disable apps rated for adults - Block malware and adult content with cloudflare + Block malware and adult content with dns Parental Control restarted \ No newline at end of file -- GitLab