Fix per-app proxy managed mode
This commit is contained in:
@@ -3,14 +3,11 @@ package io.nekohasekai.sfa.bg
|
|||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import io.nekohasekai.sfa.R
|
import io.nekohasekai.sfa.R
|
||||||
import io.nekohasekai.sfa.compose.screen.profileoverride.PerAppProxyScanner
|
import io.nekohasekai.sfa.compose.screen.profileoverride.PerAppProxyScanner
|
||||||
import io.nekohasekai.sfa.database.Settings
|
import io.nekohasekai.sfa.database.Settings
|
||||||
import io.nekohasekai.sfa.vendor.PackageQueryManager
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -48,29 +45,7 @@ class AppChangeReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
private suspend fun rescanAllApps() {
|
private suspend fun rescanAllApps() {
|
||||||
Log.d(TAG, "rescanning all apps")
|
Log.d(TAG, "rescanning all apps")
|
||||||
val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
val chinaApps = PerAppProxyScanner.scanAllChinaApps()
|
||||||
PackageManager.MATCH_UNINSTALLED_PACKAGES or
|
|
||||||
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or
|
|
||||||
PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
PackageManager.GET_UNINSTALLED_PACKAGES or
|
|
||||||
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or
|
|
||||||
PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
|
||||||
}
|
|
||||||
val retryFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_PERMISSIONS
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_PERMISSIONS
|
|
||||||
}
|
|
||||||
val installedPackages = PackageQueryManager.getInstalledPackages(packageManagerFlags, retryFlags)
|
|
||||||
val chinaApps = mutableSetOf<String>()
|
|
||||||
for (packageInfo in installedPackages) {
|
|
||||||
if (PerAppProxyScanner.scanChinaPackage(packageInfo)) {
|
|
||||||
chinaApps.add(packageInfo.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Settings.perAppProxyManagedList = chinaApps
|
Settings.perAppProxyManagedList = chinaApps
|
||||||
Log.d(TAG, "rescan complete, found ${chinaApps.size} china apps")
|
Log.d(TAG, "rescan complete, found ${chinaApps.size} china apps")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class BoxService(private val service: Service, private val platformInterface: Pl
|
|||||||
autoRedirect = Settings.autoRedirect
|
autoRedirect = Settings.autoRedirect
|
||||||
if (Vendor.isPerAppProxyAvailable() && Settings.perAppProxyEnabled) {
|
if (Vendor.isPerAppProxyAvailable() && Settings.perAppProxyEnabled) {
|
||||||
val appList = Settings.getEffectivePerAppProxyList()
|
val appList = Settings.getEffectivePerAppProxyList()
|
||||||
if (Settings.perAppProxyMode == Settings.PER_APP_PROXY_INCLUDE) {
|
if (Settings.getEffectivePerAppProxyMode() == Settings.PER_APP_PROXY_INCLUDE) {
|
||||||
includePackage =
|
includePackage =
|
||||||
PlatformInterfaceWrapper.StringArray(appList.iterator())
|
PlatformInterfaceWrapper.StringArray(appList.iterator())
|
||||||
} else {
|
} else {
|
||||||
@@ -223,7 +223,7 @@ class BoxService(private val service: Service, private val platformInterface: Pl
|
|||||||
autoRedirect = Settings.autoRedirect
|
autoRedirect = Settings.autoRedirect
|
||||||
if (Vendor.isPerAppProxyAvailable() && Settings.perAppProxyEnabled) {
|
if (Vendor.isPerAppProxyAvailable() && Settings.perAppProxyEnabled) {
|
||||||
val appList = Settings.getEffectivePerAppProxyList()
|
val appList = Settings.getEffectivePerAppProxyList()
|
||||||
if (Settings.perAppProxyMode == Settings.PER_APP_PROXY_INCLUDE) {
|
if (Settings.getEffectivePerAppProxyMode() == Settings.PER_APP_PROXY_INCLUDE) {
|
||||||
includePackage = PlatformInterfaceWrapper.StringArray(appList.iterator())
|
includePackage = PlatformInterfaceWrapper.StringArray(appList.iterator())
|
||||||
} else {
|
} else {
|
||||||
excludePackage = PlatformInterfaceWrapper.StringArray(appList.iterator())
|
excludePackage = PlatformInterfaceWrapper.StringArray(appList.iterator())
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.net.ProxyInfo
|
|||||||
import android.net.VpnService
|
import android.net.VpnService
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
import android.util.Log
|
||||||
import io.nekohasekai.libbox.Notification
|
import io.nekohasekai.libbox.Notification
|
||||||
import io.nekohasekai.libbox.TunOptions
|
import io.nekohasekai.libbox.TunOptions
|
||||||
import io.nekohasekai.sfa.database.Settings
|
import io.nekohasekai.sfa.database.Settings
|
||||||
@@ -130,8 +131,11 @@ class VPNService :
|
|||||||
if (includePackage.hasNext()) {
|
if (includePackage.hasNext()) {
|
||||||
while (includePackage.hasNext()) {
|
while (includePackage.hasNext()) {
|
||||||
try {
|
try {
|
||||||
builder.addAllowedApplication(includePackage.next())
|
val nextPackage = includePackage.next()
|
||||||
} catch (_: NameNotFoundException) {
|
builder.addAllowedApplication(nextPackage)
|
||||||
|
Log.d("VPNService", "addAllowedApplication: $nextPackage")
|
||||||
|
} catch (e: NameNotFoundException) {
|
||||||
|
Log.e("VPNService", "addAllowedApplication failed", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,8 +144,11 @@ class VPNService :
|
|||||||
if (excludePackage.hasNext()) {
|
if (excludePackage.hasNext()) {
|
||||||
while (excludePackage.hasNext()) {
|
while (excludePackage.hasNext()) {
|
||||||
try {
|
try {
|
||||||
builder.addDisallowedApplication(excludePackage.next())
|
val nextPackage = excludePackage.next()
|
||||||
} catch (_: NameNotFoundException) {
|
builder.addDisallowedApplication(nextPackage)
|
||||||
|
Log.d("VPNService", "addDisallowedApplication: $nextPackage")
|
||||||
|
} catch (e: NameNotFoundException) {
|
||||||
|
Log.e("VPNService", "addDisallowedApplication failed", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.nekohasekai.sfa.compose.screen.profileoverride
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import io.nekohasekai.sfa.Application
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
@@ -1275,8 +1276,40 @@ object PerAppProxyScanner {
|
|||||||
("(" + chinaAppPrefixList.joinToString("|").replace(".", "\\.") + ").*").toRegex()
|
("(" + chinaAppPrefixList.joinToString("|").replace(".", "\\.") + ").*").toRegex()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun scanAllChinaApps(): Set<String> = withContext(Dispatchers.Default) {
|
||||||
|
val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
PackageManager.MATCH_UNINSTALLED_PACKAGES or
|
||||||
|
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or
|
||||||
|
PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
PackageManager.GET_UNINSTALLED_PACKAGES or
|
||||||
|
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or
|
||||||
|
PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
||||||
|
}
|
||||||
|
val retryFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_PERMISSIONS
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_PERMISSIONS
|
||||||
|
}
|
||||||
|
val installedPackages = PackageQueryManager.getInstalledPackages(packageManagerFlags, retryFlags)
|
||||||
|
val chinaApps = mutableSetOf<String>()
|
||||||
|
installedPackages.map { packageInfo ->
|
||||||
|
async {
|
||||||
|
if (scanChinaPackage(packageInfo)) {
|
||||||
|
synchronized(chinaApps) {
|
||||||
|
chinaApps.add(packageInfo.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.awaitAll()
|
||||||
|
chinaApps.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
fun scanChinaPackage(packageInfo: PackageInfo): Boolean {
|
fun scanChinaPackage(packageInfo: PackageInfo): Boolean {
|
||||||
val packageName = packageInfo.packageName
|
val packageName = packageInfo.packageName
|
||||||
|
if (packageName == Application.application.packageName) return false
|
||||||
skipPrefixList.forEach {
|
skipPrefixList.forEach {
|
||||||
if (packageName == it || packageName.startsWith("$it.")) return false
|
if (packageName == it || packageName.startsWith("$it.")) return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package io.nekohasekai.sfa.compose.screen.settings
|
package io.nekohasekai.sfa.compose.screen.settings
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -64,8 +62,6 @@ import io.nekohasekai.sfa.compose.topbar.OverrideTopBar
|
|||||||
import io.nekohasekai.sfa.database.Settings
|
import io.nekohasekai.sfa.database.Settings
|
||||||
import io.nekohasekai.sfa.vendor.PackageQueryManager
|
import io.nekohasekai.sfa.vendor.PackageQueryManager
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.awaitAll
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@@ -93,6 +89,18 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
var perAppProxyEnabled by remember { mutableStateOf(Settings.perAppProxyEnabled) }
|
var perAppProxyEnabled by remember { mutableStateOf(Settings.perAppProxyEnabled) }
|
||||||
var managedModeEnabled by remember { mutableStateOf(Settings.perAppProxyManagedMode) }
|
var managedModeEnabled by remember { mutableStateOf(Settings.perAppProxyManagedMode) }
|
||||||
var isScanning by remember { mutableStateOf(false) }
|
var isScanning by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
fun scanAndSaveManagedList() {
|
||||||
|
isScanning = true
|
||||||
|
scope.launch {
|
||||||
|
val chinaApps = PerAppProxyScanner.scanAllChinaApps()
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
Settings.perAppProxyManagedList = chinaApps
|
||||||
|
}
|
||||||
|
isScanning = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var showShizukuDialog by remember { mutableStateOf(false) }
|
var showShizukuDialog by remember { mutableStateOf(false) }
|
||||||
var showRootDialog by remember { mutableStateOf(false) }
|
var showRootDialog by remember { mutableStateOf(false) }
|
||||||
var showModeDialog by remember { mutableStateOf(false) }
|
var showModeDialog by remember { mutableStateOf(false) }
|
||||||
@@ -150,12 +158,7 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
Settings.perAppProxyEnabled = true
|
Settings.perAppProxyEnabled = true
|
||||||
}
|
}
|
||||||
if (managedModeEnabled) {
|
if (managedModeEnabled) {
|
||||||
isScanning = true
|
scanAndSaveManagedList()
|
||||||
val chinaApps = scanAllChinaApps()
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Settings.perAppProxyManagedList = chinaApps
|
|
||||||
}
|
|
||||||
isScanning = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -352,14 +355,7 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
Settings.perAppProxyEnabled = checked
|
Settings.perAppProxyEnabled = checked
|
||||||
}
|
}
|
||||||
if (checked && managedModeEnabled) {
|
if (checked && managedModeEnabled) {
|
||||||
isScanning = true
|
scanAndSaveManagedList()
|
||||||
scope.launch {
|
|
||||||
val chinaApps = scanAllChinaApps()
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Settings.perAppProxyManagedList = chinaApps
|
|
||||||
}
|
|
||||||
isScanning = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -465,18 +461,10 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
onCheckedChange = { checked ->
|
onCheckedChange = { checked ->
|
||||||
if (checked) {
|
if (checked) {
|
||||||
managedModeEnabled = true
|
managedModeEnabled = true
|
||||||
isScanning = true
|
scope.launch(Dispatchers.IO) {
|
||||||
scope.launch {
|
Settings.perAppProxyManagedMode = true
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Settings.perAppProxyManagedMode = true
|
|
||||||
Settings.perAppProxyMode = Settings.PER_APP_PROXY_EXCLUDE
|
|
||||||
}
|
|
||||||
val chinaApps = scanAllChinaApps()
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Settings.perAppProxyManagedList = chinaApps
|
|
||||||
}
|
|
||||||
isScanning = false
|
|
||||||
}
|
}
|
||||||
|
scanAndSaveManagedList()
|
||||||
} else {
|
} else {
|
||||||
managedModeEnabled = false
|
managedModeEnabled = false
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
@@ -518,14 +506,7 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
Settings.perAppProxyEnabled = true
|
Settings.perAppProxyEnabled = true
|
||||||
}
|
}
|
||||||
if (managedModeEnabled) {
|
if (managedModeEnabled) {
|
||||||
isScanning = true
|
scanAndSaveManagedList()
|
||||||
scope.launch {
|
|
||||||
val chinaApps = scanAllChinaApps()
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Settings.perAppProxyManagedList = chinaApps
|
|
||||||
}
|
|
||||||
isScanning = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@@ -601,12 +582,7 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
Settings.perAppProxyEnabled = true
|
Settings.perAppProxyEnabled = true
|
||||||
}
|
}
|
||||||
if (managedModeEnabled) {
|
if (managedModeEnabled) {
|
||||||
isScanning = true
|
scanAndSaveManagedList()
|
||||||
val chinaApps = scanAllChinaApps()
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Settings.perAppProxyManagedList = chinaApps
|
|
||||||
}
|
|
||||||
isScanning = false
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showRootDialog = false
|
showRootDialog = false
|
||||||
@@ -694,37 +670,3 @@ fun ProfileOverrideScreen(navController: NavController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun scanAllChinaApps(): Set<String> = withContext(Dispatchers.Default) {
|
|
||||||
val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
PackageManager.MATCH_UNINSTALLED_PACKAGES or
|
|
||||||
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or
|
|
||||||
PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
PackageManager.GET_UNINSTALLED_PACKAGES or
|
|
||||||
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or
|
|
||||||
PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
|
||||||
}
|
|
||||||
val retryFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_PERMISSIONS
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_PERMISSIONS
|
|
||||||
}
|
|
||||||
|
|
||||||
val installedPackages = PackageQueryManager.getInstalledPackages(packageManagerFlags, retryFlags)
|
|
||||||
|
|
||||||
val chinaApps = mutableSetOf<String>()
|
|
||||||
installedPackages.map { packageInfo ->
|
|
||||||
async {
|
|
||||||
if (PerAppProxyScanner.scanChinaPackage(packageInfo)) {
|
|
||||||
synchronized(chinaApps) {
|
|
||||||
chinaApps.add(packageInfo.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.awaitAll()
|
|
||||||
|
|
||||||
chinaApps.toSet()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -82,8 +82,14 @@ object Settings {
|
|||||||
const val PACKAGE_QUERY_MODE_ROOT = "ROOT"
|
const val PACKAGE_QUERY_MODE_ROOT = "ROOT"
|
||||||
var perAppProxyPackageQueryMode by dataStore.string(SettingsKey.PER_APP_PROXY_PACKAGE_QUERY_MODE) { PACKAGE_QUERY_MODE_SHIZUKU }
|
var perAppProxyPackageQueryMode by dataStore.string(SettingsKey.PER_APP_PROXY_PACKAGE_QUERY_MODE) { PACKAGE_QUERY_MODE_SHIZUKU }
|
||||||
|
|
||||||
|
fun getEffectivePerAppProxyMode(): Int = if (perAppProxyManagedMode) {
|
||||||
|
PER_APP_PROXY_EXCLUDE
|
||||||
|
} else {
|
||||||
|
perAppProxyMode
|
||||||
|
}
|
||||||
|
|
||||||
fun getEffectivePerAppProxyList(): Set<String> = if (perAppProxyManagedMode) {
|
fun getEffectivePerAppProxyList(): Set<String> = if (perAppProxyManagedMode) {
|
||||||
perAppProxyList union perAppProxyManagedList
|
perAppProxyManagedList
|
||||||
} else {
|
} else {
|
||||||
perAppProxyList
|
perAppProxyList
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user