ViewModel refactoring: Network part

(#178)
This commit is contained in:
BinTianqi
2025-10-06 15:10:07 +08:00
parent 43b1314e3a
commit 59556ae23d
11 changed files with 1652 additions and 1319 deletions

View File

@@ -1,5 +1,6 @@
package android.app.admin;
import android.content.ComponentName;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
@@ -14,4 +15,5 @@ public interface IDevicePolicyManager extends IInterface {
throw new UnsupportedOperationException();
}
}
int setGlobalPrivateDns(ComponentName who, int mode, String privateDnsHost);
}

View File

@@ -62,8 +62,6 @@ import com.bintianqi.owndroid.dpm.AddApnSetting
import com.bintianqi.owndroid.dpm.AddApnSettingScreen
import com.bintianqi.owndroid.dpm.AddDelegatedAdmin
import com.bintianqi.owndroid.dpm.AddDelegatedAdminScreen
import com.bintianqi.owndroid.dpm.AddNetwork
import com.bintianqi.owndroid.dpm.AddNetworkScreen
import com.bintianqi.owndroid.dpm.AddPreferentialNetworkServiceConfig
import com.bintianqi.owndroid.dpm.AddPreferentialNetworkServiceConfigScreen
import com.bintianqi.owndroid.dpm.AffiliationId
@@ -164,6 +162,7 @@ import com.bintianqi.owndroid.dpm.PermittedAccessibilityServices
import com.bintianqi.owndroid.dpm.PermittedAsAndImPackages
import com.bintianqi.owndroid.dpm.PermittedInputMethods
import com.bintianqi.owndroid.dpm.PreferentialNetworkService
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceInfo
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceScreen
import com.bintianqi.owndroid.dpm.PrivateDns
import com.bintianqi.owndroid.dpm.PrivateDnsScreen
@@ -198,6 +197,8 @@ import com.bintianqi.owndroid.dpm.TransferOwnership
import com.bintianqi.owndroid.dpm.TransferOwnershipScreen
import com.bintianqi.owndroid.dpm.UninstallApp
import com.bintianqi.owndroid.dpm.UninstallAppScreen
import com.bintianqi.owndroid.dpm.UpdateNetwork
import com.bintianqi.owndroid.dpm.UpdateNetworkScreen
import com.bintianqi.owndroid.dpm.UserInfo
import com.bintianqi.owndroid.dpm.UserInfoScreen
import com.bintianqi.owndroid.dpm.UserOperation
@@ -393,28 +394,62 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
composable<WipeData> { WipeDataScreen(vm::wipeData, ::navigateUp) }
composable<Network> { NetworkScreen(::navigateUp, ::navigate) }
composable<WiFi> { WifiScreen(::navigateUp, ::navigate) { navController.navigate(AddNetwork, it)} }
composable<NetworkOptions> { NetworkOptionsScreen(::navigateUp) }
composable<AddNetwork> { AddNetworkScreen(it.arguments!!, ::navigateUp) }
composable<WifiSecurityLevel> { WifiSecurityLevelScreen(::navigateUp) }
composable<WifiSsidPolicyScreen> { WifiSsidPolicyScreen(::navigateUp) }
composable<WiFi> {
WifiScreen(vm, ::navigateUp, ::navigate) { navController.navigate(UpdateNetwork(it)) }
}
composable<NetworkOptions> {
NetworkOptionsScreen(vm::getLanEnabled, vm::setLanEnabled, ::navigateUp)
}
composable<UpdateNetwork> {
val info = vm.configuredNetworks.collectAsStateWithLifecycle().value[
(it.toRoute() as UpdateNetwork).index
]
UpdateNetworkScreen(info, vm::setWifi, ::navigateUp)
}
composable<WifiSecurityLevel> {
WifiSecurityLevelScreen(vm::getMinimumWifiSecurityLevel,
vm::setMinimumWifiSecurityLevel, ::navigateUp)
}
composable<WifiSsidPolicyScreen> {
WifiSsidPolicyScreen(vm::getSsidPolicy, vm::setSsidPolicy, ::navigateUp)
}
composable<QueryNetworkStats> {
NetworkStatsScreen(vm.chosenPackage, ::choosePackage, ::navigateUp, ::navigate)
NetworkStatsScreen(vm.chosenPackage, ::choosePackage, vm::getPackageUid,
vm::queryNetworkStats, ::navigateUp) { navController.navigate(NetworkStatsViewer) }
}
composable<NetworkStatsViewer>(mapOf(serializableNavTypePair<List<NetworkStatsViewer.Data>>())) {
NetworkStatsViewerScreen(it.toRoute(), ::navigateUp)
composable<NetworkStatsViewer> {
NetworkStatsViewerScreen(vm.networkStatsData, vm::clearNetworkStats, ::navigateUp)
}
composable<PrivateDns> {
PrivateDnsScreen(vm::getPrivateDns, vm::setPrivateDns, ::navigateUp)
}
composable<PrivateDns> { PrivateDnsScreen(::navigateUp) }
composable<AlwaysOnVpnPackage> {
AlwaysOnVpnPackageScreen(vm.chosenPackage, ::choosePackage, ::navigateUp)
AlwaysOnVpnPackageScreen(vm::getAlwaysOnVpnPackage, vm::getAlwaysOnVpnLockdown,
vm::setAlwaysOnVpn, vm.chosenPackage, ::choosePackage, ::navigateUp)
}
composable<RecommendedGlobalProxy> {
RecommendedGlobalProxyScreen(vm::setRecommendedGlobalProxy, ::navigateUp)
}
composable<RecommendedGlobalProxy> { RecommendedGlobalProxyScreen(::navigateUp) }
composable<NetworkLogging> { NetworkLoggingScreen(::navigateUp) }
composable<WifiAuthKeypair> { WifiAuthKeypairScreen(::navigateUp) }
composable<PreferentialNetworkService> { PreferentialNetworkServiceScreen(::navigateUp, ::navigate) }
composable<AddPreferentialNetworkServiceConfig> { AddPreferentialNetworkServiceConfigScreen(it.toRoute(), ::navigateUp) }
composable<OverrideApn> { OverrideApnScreen(::navigateUp) { navController.navigate(AddApnSetting, it) } }
composable<AddApnSetting> { AddApnSettingScreen(it.arguments?.getParcelable("setting"), ::navigateUp) }
//composable<WifiAuthKeypair> { WifiAuthKeypairScreen(::navigateUp) }
composable<PreferentialNetworkService> {
PreferentialNetworkServiceScreen(vm::getPnsEnabled, vm::setPnsEnabled, vm.pnsConfigs,
vm::getPnsConfigs, ::navigateUp, ::navigate)
}
composable<AddPreferentialNetworkServiceConfig> {
val info = vm.pnsConfigs.collectAsStateWithLifecycle().value.getOrNull(
it.toRoute<AddPreferentialNetworkServiceConfig>().index
) ?: PreferentialNetworkServiceInfo()
AddPreferentialNetworkServiceConfigScreen(info, vm::setPnsConfig, ::navigateUp)
}
composable<OverrideApn> {
OverrideApnScreen(vm.apnConfigs, vm::getApnConfigs, vm::getApnEnabled,
vm::setApnEnabled, ::navigateUp) { navController.navigate(AddApnSetting(it)) }
}
composable<AddApnSetting> {
val origin = vm.apnConfigs.collectAsStateWithLifecycle().value.getOrNull((it.toRoute() as AddApnSetting).index)
AddApnSettingScreen(vm::setApnConfig, vm::removeApnConfig, origin, ::navigateUp)
}
composable<WorkProfile> { WorkProfileScreen(::navigateUp, ::navigate) }
composable<OrganizationOwnedProfile> {
@@ -542,7 +577,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
composable<UserRestrictionEditor> {
UserRestrictionEditorScreen(vm.userRestrictions, vm::setUserRestriction, ::navigateUp)
}
composable<UserRestrictionOptions>(mapOf(serializableNavTypePair<List<Restriction>>())) {
composable<UserRestrictionOptions> {
UserRestrictionOptionsScreen(it.toRoute(), vm.userRestrictions,
vm::setUserRestriction, ::navigateUp)
}

View File

@@ -1,6 +1,7 @@
package com.bintianqi.owndroid
import android.accounts.Account
import android.annotation.SuppressLint
import android.app.ActivityOptions
import android.app.Application
import android.app.PendingIntent
@@ -9,9 +10,14 @@ import android.app.admin.DeviceAdminReceiver
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback
import android.app.admin.FactoryResetProtectionPolicy
import android.app.admin.IDevicePolicyManager
import android.app.admin.PackagePolicy
import android.app.admin.PreferentialNetworkServiceConfig
import android.app.admin.SystemUpdateInfo
import android.app.admin.SystemUpdatePolicy
import android.app.admin.WifiSsidPolicy
import android.app.usage.NetworkStats
import android.app.usage.NetworkStatsManager
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
@@ -21,23 +27,36 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.IpConfiguration
import android.net.LinkAddress
import android.net.ProxyInfo
import android.net.StaticIpConfiguration
import android.net.Uri
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiManager
import android.net.wifi.WifiSsid
import android.os.Binder
import android.os.Build.VERSION
import android.os.HardwarePropertiesManager
import android.os.UserHandle
import android.os.UserManager
import android.telephony.data.ApnSetting
import androidx.annotation.RequiresApi
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toDrawable
import androidx.core.net.toUri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.application
import androidx.lifecycle.viewModelScope
import com.bintianqi.owndroid.Privilege.DAR
import com.bintianqi.owndroid.Privilege.DPM
import com.bintianqi.owndroid.dpm.ACTIVATE_DEVICE_OWNER_COMMAND
import com.bintianqi.owndroid.dpm.ApnAuthType
import com.bintianqi.owndroid.dpm.ApnConfig
import com.bintianqi.owndroid.dpm.ApnMvnoType
import com.bintianqi.owndroid.dpm.ApnProtocol
import com.bintianqi.owndroid.dpm.AppStatus
import com.bintianqi.owndroid.dpm.CaCertInfo
import com.bintianqi.owndroid.dpm.CreateUserResult
@@ -48,10 +67,24 @@ import com.bintianqi.owndroid.dpm.FrpPolicyInfo
import com.bintianqi.owndroid.dpm.HardwareProperties
import com.bintianqi.owndroid.dpm.IntentFilterDirection
import com.bintianqi.owndroid.dpm.IntentFilterOptions
import com.bintianqi.owndroid.dpm.IpMode
import com.bintianqi.owndroid.dpm.NetworkStatsData
import com.bintianqi.owndroid.dpm.NetworkStatsTarget
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceInfo
import com.bintianqi.owndroid.dpm.PrivateDnsConfiguration
import com.bintianqi.owndroid.dpm.ProxyMode
import com.bintianqi.owndroid.dpm.ProxyType
import com.bintianqi.owndroid.dpm.QueryNetworkStatsParams
import com.bintianqi.owndroid.dpm.RecommendedProxyConf
import com.bintianqi.owndroid.dpm.SsidPolicy
import com.bintianqi.owndroid.dpm.SsidPolicyType
import com.bintianqi.owndroid.dpm.SystemOptionsStatus
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyInfo
import com.bintianqi.owndroid.dpm.UserInformation
import com.bintianqi.owndroid.dpm.WifiInfo
import com.bintianqi.owndroid.dpm.WifiSecurity
import com.bintianqi.owndroid.dpm.WifiStatus
import com.bintianqi.owndroid.dpm.activateOrgProfileCommand
import com.bintianqi.owndroid.dpm.delegatedScopesList
import com.bintianqi.owndroid.dpm.getPackageInstaller
@@ -70,6 +103,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.InetAddress
import java.security.MessageDigest
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
@@ -77,6 +112,8 @@ import java.security.cert.X509Certificate
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.concurrent.Executors
import kotlin.reflect.jvm.jvmErasure
import kotlin.system.measureTimeMillis
class MyViewModel(application: Application): AndroidViewModel(application) {
val myRepo = getApplication<MyApplication>().myRepo
@@ -1246,6 +1283,368 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
fun logoutUser(): Int {
return getUserOperationResultText(DPM.logoutUser(DAR))
}
val WM = application.getSystemService(Context.WIFI_SERVICE) as WifiManager
// Lockdown admin configured networks
@RequiresApi(30)
fun getLanEnabled(): Boolean {
return DPM.hasLockdownAdminConfiguredNetworks(DAR)
}
@RequiresApi(30)
fun setLanEnabled(state: Boolean) {
DPM.setConfiguredNetworksLockdownState(DAR, state)
}
fun setWifiEnabled(enabled: Boolean): Boolean {
return WM.setWifiEnabled(enabled)
}
fun disconnectWifi(): Boolean {
return WM.disconnect()
}
fun reconnectWifi(): Boolean {
return WM.reconnect()
}
@RequiresApi(24)
fun getWifiMac(): String? {
return DPM.getWifiMacAddress(DAR)
}
val configuredNetworks = MutableStateFlow(emptyList<WifiInfo>())
fun getConfiguredNetworks() {
configuredNetworks.value = WM.configuredNetworks.distinctBy { it.networkId }.map { conf ->
WifiInfo(
conf.networkId, conf.SSID.removeSurrounding("\""), null, conf.BSSID ?: "", null,
WifiStatus.entries.find { it.id == conf.status }!!, null, "", null, null, null, null
)
}
}
fun enableNetwork(id: Int): Boolean {
return WM.enableNetwork(id, false)
}
fun disableNetwork(id: Int): Boolean {
return WM.disableNetwork(id)
}
fun removeNetwork(id: Int): Boolean{
return WM.removeNetwork(id)
}
fun setWifi(info: WifiInfo): Boolean {
val conf = WifiConfiguration()
conf.SSID = "\"" + info.ssid + "\""
info.hiddenSsid?.let { conf.hiddenSSID = it }
if (VERSION.SDK_INT >= 30) info.security?.let { conf.setSecurityParams(it.id) }
if (info.security == WifiSecurity.Psk) conf.preSharedKey = info.password
if (VERSION.SDK_INT >= 33) info.macRandomization?.let { conf.macRandomizationSetting = it.id }
if (VERSION.SDK_INT >= 33 && info.ipMode != null) {
val ipConf = if (info.ipMode == IpMode.Static && info.ipConf != null) {
val constructor = LinkAddress::class.constructors.find {
it.parameters.size == 1 && it.parameters[0].type.jvmErasure == String::class
}
val address = constructor!!.call(info.ipConf.address)
val staticIpConf = StaticIpConfiguration.Builder()
.setIpAddress(address)
.setGateway(InetAddress.getByName(info.ipConf.gateway))
.setDnsServers(info.ipConf.dns.map { InetAddress.getByName(it) })
.build()
IpConfiguration.Builder().setStaticIpConfiguration(staticIpConf).build()
} else null
conf.setIpConfiguration(ipConf)
}
if (VERSION.SDK_INT >= 26 && info.proxyMode != null) {
val proxy = if (info.proxyMode == ProxyMode.Http) {
info.proxyConf?.let {
ProxyInfo.buildDirectProxy(it.host, it.port, it.exclude)
}
} else null
conf.httpProxy = proxy
}
val result = if (info.id != -1) {
conf.networkId = info.id
WM.updateNetwork(conf)
} else {
WM.addNetwork(conf)
}
if (result != -1) {
when (info.status) {
WifiStatus.Current -> WM.enableNetwork(result, true)
WifiStatus.Enabled -> WM.enableNetwork(result, false)
WifiStatus.Disabled -> WM.disableNetwork(result)
}
}
return result != -1
}
@RequiresApi(33)
fun getMinimumWifiSecurityLevel(): Int {
return DPM.minimumRequiredWifiSecurityLevel
}
@RequiresApi(33)
fun setMinimumWifiSecurityLevel(level: Int) {
DPM.minimumRequiredWifiSecurityLevel = level
}
@RequiresApi(33)
fun getSsidPolicy(): SsidPolicy {
val policy = DPM.wifiSsidPolicy
return SsidPolicy(
SsidPolicyType.entries.find { it.id == policy?.policyType } ?: SsidPolicyType.None,
policy?.ssids?.map { it.bytes.decodeToString() } ?: emptyList()
)
}
@RequiresApi(33)
fun setSsidPolicy(policy: SsidPolicy) {
val newPolicy = if (policy.type != SsidPolicyType.None) {
WifiSsidPolicy(
policy.type.id, policy.list.map { WifiSsid.fromBytes(it.encodeToByteArray()) }.toSet()
)
} else null
DPM.wifiSsidPolicy = newPolicy
}
@RequiresApi(24)
fun getPackageUid(name: String): Int {
return PM.getPackageUid(name, 0)
}
var networkStatsData = emptyList<NetworkStatsData>()
@RequiresApi(23)
fun readNetworkStats(stats: NetworkStats): List<NetworkStatsData> {
val list = mutableListOf<NetworkStatsData>()
while (stats.hasNextBucket()) {
val bucket = NetworkStats.Bucket()
stats.getNextBucket(bucket)
list += readNetworkStatsBucket(bucket)
}
stats.close()
return list
}
@RequiresApi(23)
fun readNetworkStatsBucket(bucket: NetworkStats.Bucket): NetworkStatsData {
return NetworkStatsData(
bucket.rxBytes, bucket.rxPackets, bucket.txBytes, bucket.txPackets,
bucket.uid, bucket.state, bucket.startTimeStamp, bucket.endTimeStamp,
if (VERSION.SDK_INT >= 24) bucket.tag else null,
if (VERSION.SDK_INT >= 24) bucket.roaming else null,
if (VERSION.SDK_INT >= 26) bucket.metered else null
)
}
@Suppress("NewApi")
fun queryNetworkStats(params: QueryNetworkStatsParams, callback: (String?) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
val nsm = application.getSystemService(NetworkStatsManager::class.java)
try {
val data = when (params.target) {
NetworkStatsTarget.Device -> listOf(readNetworkStatsBucket(
nsm.querySummaryForDevice(
params.networkType.type, null, params.startTime, params.endTime
)
))
NetworkStatsTarget.User -> listOf(readNetworkStatsBucket(
nsm.querySummaryForUser(
params.networkType.type, null, params.startTime, params.endTime
)
))
NetworkStatsTarget.Uid -> readNetworkStats(nsm.queryDetailsForUid(
params.networkType.type, null, params.startTime, params.endTime, params.uid
))
NetworkStatsTarget.UidTag -> readNetworkStats(nsm.queryDetailsForUidTag(
params.networkType.type, null, params.startTime, params.endTime,
params.uid, params.tag
))
NetworkStatsTarget.UidTagState -> readNetworkStats(
nsm.queryDetailsForUidTagState(
params.networkType.type, null, params.startTime, params.endTime,
params.uid, params.tag, params.state.id
)
)
}
networkStatsData = data
withContext(Dispatchers.Main) {
if (data.isEmpty()) {
callback(application.getString(R.string.no_data))
} else {
callback(null)
}
}
} catch(e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
callback(e.message ?: "")
}
}
}
}
fun clearNetworkStats() {
networkStatsData = emptyList()
}
@RequiresApi(29)
fun getPrivateDns(): PrivateDnsConfiguration {
return PrivateDnsConfiguration(
DPM.getGlobalPrivateDnsMode(DAR), DPM.getGlobalPrivateDnsHost(DAR) ?: ""
)
}
@Suppress("PrivateApi")
@RequiresApi(29)
fun setPrivateDns(conf: PrivateDnsConfiguration): Boolean {
return try {
val field = DevicePolicyManager::class.java.getDeclaredField("mService")
field.isAccessible = true
val dpm = field.get(DPM) as IDevicePolicyManager
val result = dpm.setGlobalPrivateDns(DAR, conf.mode, conf.host)
result == DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR
} catch (e: Exception) {
e.printStackTrace()
false
}
}
@RequiresApi(24)
fun getAlwaysOnVpnPackage(): String {
return DPM.getAlwaysOnVpnPackage(DAR) ?: ""
}
@RequiresApi(29)
fun getAlwaysOnVpnLockdown(): Boolean {
return DPM.isAlwaysOnVpnLockdownEnabled(DAR)
}
@RequiresApi(24)
fun setAlwaysOnVpn(name: String?, lockdown: Boolean): Int {
return try {
DPM.setAlwaysOnVpnPackage(DAR, name, lockdown)
R.string.succeeded
} catch (_: UnsupportedOperationException) {
R.string.unsupported
} catch (_: PackageManager.NameNotFoundException) {
R.string.not_installed
}
}
fun setRecommendedGlobalProxy(conf: RecommendedProxyConf) {
val info = when (conf.type) {
ProxyType.Off -> null
ProxyType.Pac -> {
if (VERSION.SDK_INT >= 30 && conf.specifyPort) {
ProxyInfo.buildPacProxy(conf.url.toUri(), conf.port)
} else {
ProxyInfo.buildPacProxy(conf.url.toUri())
}
}
ProxyType.Direct -> {
ProxyInfo.buildDirectProxy(conf.host, conf.port, conf.exclude)
}
}
DPM.setRecommendedGlobalProxy(DAR, info)
}
// PNS: preferential network service
@RequiresApi(31)
fun getPnsEnabled(): Boolean {
return DPM.isPreferentialNetworkServiceEnabled
}
@RequiresApi(31)
fun setPnsEnabled(enabled: Boolean) {
DPM.isPreferentialNetworkServiceEnabled = enabled
}
val pnsConfigs = MutableStateFlow(emptyList<PreferentialNetworkServiceInfo>())
@RequiresApi(33)
fun getPnsConfigs() {
pnsConfigs.value = DPM.preferentialNetworkServiceConfigs.map {
PreferentialNetworkServiceInfo(
it.isEnabled, it.networkId, it.isFallbackToDefaultConnectionAllowed,
if (VERSION.SDK_INT >= 34) it.shouldBlockNonMatchingNetworks() else false,
it.excludedUids.toList(), it.includedUids.toList()
)
}
}
@RequiresApi(33)
fun buildPnsConfig(
info: PreferentialNetworkServiceInfo
): PreferentialNetworkServiceConfig {
return PreferentialNetworkServiceConfig.Builder().apply {
setEnabled(info.enabled)
@Suppress("WrongConstant")
setNetworkId(info.id)
setFallbackToDefaultConnectionAllowed(info.allowFallback)
if (VERSION.SDK_INT >= 34) setShouldBlockNonMatchingNetworks(info.blockNonMatching)
setIncludedUids(info.includedUids.toIntArray())
setExcludedUids(info.excludedUids.toIntArray())
}.build()
}
@RequiresApi(33)
fun setPnsConfig(info: PreferentialNetworkServiceInfo, state: Boolean) {
val configs = pnsConfigs.value.run {
if (state) plus(info) else minus(info)
}.map { buildPnsConfig(it) }
DPM.preferentialNetworkServiceConfigs = configs
}
val apnConfigs = MutableStateFlow(listOf<ApnConfig>())
@RequiresApi(28)
fun getApnEnabled(): Boolean {
return DPM.isOverrideApnEnabled(DAR)
}
@RequiresApi(28)
fun setApnEnabled(enabled: Boolean) {
DPM.setOverrideApnsEnabled(DAR, enabled)
}
@RequiresApi(28)
fun getApnConfigs() {
apnConfigs.value = DPM.getOverrideApns(DAR).map {
val proxy = if (VERSION.SDK_INT >= 29) it.proxyAddressAsString else it.proxyAddress.hostName
val mmsProxy = if (VERSION.SDK_INT >= 29) it.mmsProxyAddressAsString else it.mmsProxyAddress.hostName
ApnConfig(
it.isEnabled, it.entryName, it.apnName, proxy, it.proxyPort,
it.user, it.password, it.apnTypeBitmask, it.mmsc.toString(),
mmsProxy, it.mmsProxyPort,
ApnAuthType.entries.find { type -> type.id == it.authType }!!,
ApnProtocol.entries.find { protocol -> protocol.id == it.protocol }!!,
ApnProtocol.entries.find { protocol -> protocol.id == it.roamingProtocol }!!,
it.networkTypeBitmask,
if (VERSION.SDK_INT >= 33) it.profileId else 0,
if (VERSION.SDK_INT >= 29) it.carrierId else 0,
if (VERSION.SDK_INT >= 33) it.mtuV4 else 0,
if (VERSION.SDK_INT >= 33) it.mtuV6 else 0,
ApnMvnoType.entries.find { type -> type.id == it.mvnoType }!!,
it.operatorNumeric,
if (VERSION.SDK_INT >= 33) it.isPersistent else true,
if (VERSION.SDK_INT >= 35) it.isAlwaysOn else true,
it.id
)
}
}
@RequiresApi(28)
fun buildApnSetting(config: ApnConfig): ApnSetting? {
val builder = ApnSetting.Builder()
builder.setCarrierEnabled(config.enabled)
builder.setEntryName(config.name)
builder.setApnName(config.apn)
if (VERSION.SDK_INT >= 29) builder.setProxyAddress(config.proxy)
else builder.setProxyAddress(InetAddress.getByName(config.proxy))
config.port?.let { builder.setProxyPort(it) }
builder.setUser(config.username)
builder.setPassword(config.password)
builder.setApnTypeBitmask(config.apnType)
builder.setMmsc(config.mmsc.toUri())
if (VERSION.SDK_INT >= 29) builder.setMmsProxyAddress(config.mmsProxy)
else builder.setMmsProxyAddress(InetAddress.getByName(config.mmsProxy))
builder.setAuthType(config.authType.id)
builder.setProtocol(config.protocol.id)
builder.setRoamingProtocol(config.roamingProtocol.id)
builder.setNetworkTypeBitmask(config.networkType)
if (VERSION.SDK_INT >= 33) config.profileId?.let { builder.setProfileId(it) }
if (VERSION.SDK_INT >= 29) config.carrierId?.let { builder.setCarrierId(it) }
if (VERSION.SDK_INT >= 33) {
config.mtuV4?.let { builder.setMtuV4(it) }
config.mtuV6?.let { builder.setMtuV6(it) }
}
builder.setMvnoType(config.mvno.id)
builder.setOperatorNumeric(config.operatorNumeric)
if (VERSION.SDK_INT >= 33) builder.setPersistent(config.persistent)
if (VERSION.SDK_INT >= 35) builder.setAlwaysOn(config.alwaysOn)
return builder.build()
}
@RequiresApi(28)
fun setApnConfig(config: ApnConfig): Boolean {
val settings = buildApnSetting(config)
if (settings == null) return false
return if (config.id == -1) {
DPM.addOverrideApn(DAR, settings) != -1
} else {
DPM.updateOverrideApn(DAR, config.id, settings)
}
}
@RequiresApi(28)
fun removeApnConfig(id: Int): Boolean {
return DPM.removeOverrideApn(DAR, id)
}
}
data class ThemeSettings(

View File

@@ -6,11 +6,13 @@ import android.app.admin.DeviceAdminReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Binder
import android.os.Build.VERSION
import android.os.UserHandle
import android.os.UserManager
import androidx.core.app.NotificationCompat
import com.bintianqi.owndroid.dpm.handleNetworkLogs
import com.bintianqi.owndroid.dpm.handlePrivilegeChange
import com.bintianqi.owndroid.dpm.processSecurityLogs
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -34,6 +36,7 @@ class Receiver : DeviceAdminReceiver() {
override fun onEnabled(context: Context, intent: Intent) {
super.onEnabled(context, intent)
Privilege.updateStatus()
if (Binder.getCallingUid() / 100000 != 0) handlePrivilegeChange(context)
}
override fun onDisabled(context: Context, intent: Intent) {

View File

@@ -7,14 +7,11 @@ import android.content.Context
import android.content.pm.PackageInfo
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json
import java.io.FileNotFoundException
@@ -25,7 +22,6 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.TimeUnit
import kotlin.reflect.typeOf
var zhCN = true
@@ -81,18 +77,6 @@ fun Context.showOperationResultToast(success: Boolean) {
const val APK_MIME = "application/vnd.android.package-archive"
inline fun <reified T> serializableNavTypePair() =
typeOf<T>() to object : NavType<T>(false) {
override fun get(bundle: Bundle, key: String): T? =
bundle.getString(key)?.let { parseValue(it) }
override fun put(bundle: Bundle, key: String, value: T) =
bundle.putString(key, serializeAsValue(value))
override fun parseValue(value: String): T =
Json.decodeFromString(value)
override fun serializeAsValue(value: T): String =
Json.encodeToString(value)
}
fun exportLogs(context: Context, uri: Uri) {
context.contentResolver.openOutputStream(uri)?.use { output ->
val proc = Runtime.getRuntime().exec("logcat -d")
@@ -103,10 +87,6 @@ fun exportLogs(context: Context, uri: Uri) {
}
}
fun <T> NavHostController.navigate(route: T, args: Bundle) {
navigate(graph.findNode(route)!!.id, args)
}
val HorizontalPadding = 16.dp
@OptIn(ExperimentalStdlibApi::class)

View File

@@ -391,19 +391,13 @@ fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
}
fun setDefaultAffiliationID() {
if(VERSION.SDK_INT < 26) return
val privilege = Privilege.status.value
if (VERSION.SDK_INT < 26) return
if(!SP.isDefaultAffiliationIdSet) {
try {
if(privilege.device || (!privilege.primary && privilege.profile)) {
val affiliationIDs = Privilege.DPM.getAffiliationIds(Privilege.DAR)
if(affiliationIDs.isEmpty()) {
Privilege.DPM.setAffiliationIds(Privilege.DAR, setOf("OwnDroid_default_affiliation_id"))
SP.isDefaultAffiliationIdSet = true
Log.d("DPM", "Default affiliation id set")
}
}
} catch(e: Exception) {
Privilege.DPM.setAffiliationIds(Privilege.DAR, setOf("OwnDroid_default_affiliation_id"))
SP.isDefaultAffiliationIdSet = true
Log.d("DPM", "Default affiliation id set")
} catch (e: Exception) {
e.printStackTrace()
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -164,7 +164,6 @@
<string name="nearby_notification_streaming">Политика потоковой передачи уведомлений Nearby</string>
<string name="enable_if_secure_enough">Только для одной управляемой учетной записи</string>
<string name="lock_task_mode">Режим закрепления задачи</string>
<string name="specify_activity">Указать Acitvity</string>
<string name="app_not_allowed">Приложение не разрешено</string>
<string name="disable_all">Отключить все</string>
<!--ltf: функция закрепления задачи-->
@@ -254,7 +253,6 @@
<string name="network_type">Тип сети</string>
<string name="mobile">Мобильный</string>
<string name="ethernet">Ethernet</string>
<string name="subscriber_id">ID подписчика</string>
<string name="all">Всё</string>
<string name="uninstalled">Удалённый</string>
<string name="tethering">Режим модема</string>
@@ -269,13 +267,9 @@
<string name="roaming">Роуминг</string>
<string name="metered">Измеренный</string>
<string name="private_dns">Частный DNS</string>
<string name="dns_provide_hostname">Укажите имя хоста</string>
<string name="host_not_serving_dns_tls">Хост не обслуживает DNS через TLS</string>
<string name="set_to_opportunistic">Установить в оппортунистический режим</string>
<string name="dns_hostname">Имя хоста DNS</string>
<string name="invalid_hostname">Неверное имя хоста</string>
<string name="security_exception">Исключение безопасности</string>
<string name="set_dns_host">Установить хост DNS</string>
<string name="recommended_global_proxy">Рекомендуемый глобальный прокси</string>
<string name="proxy_type_off">Без прокси</string>
<string name="proxy_type_pac">PAC-прокси</string>
@@ -296,27 +290,15 @@
<string name="included_uids">Включенные UID</string>
<string name="excluded_uids">Исключенные UID</string>
<string name="one_uid_per_line">Один UID на строку</string>
<!--TODO: following 4 strings-->
<!--TODO: following 2 strings-->
<string name="override_apn">Override APN</string>
<string name="apn_setting">APN setting</string>
<string name="apn_name">APN name</string>
<string name="entry_name">Entry name</string>
<string name="name">Имя</string>
<string name="profile_id">Идентификатор профиля</string>
<string name="auth_type">Тип аутентификации</string>
<string name="description">Описание</string>
<string name="mms_proxy">MMS-прокси</string>
<string name="address">Адрес</string>
<string name="port">Порт</string>
<string name="proxy">Прокси</string>
<string name="persistent">Постоянный</string>
<string name="protocol">Протокол</string>
<string name="roaming_protocol">Протокол роуминга</string>
<!--TODO: following 4 strings-->
<string name="carrier_id">Carrier ID</string>
<string name="mvno_type">MVNO type</string>
<string name="network_type_bitmask">Network type bitmask</string>
<string name="always_on">Always on</string>
<string name="update">Обновить</string>

View File

@@ -195,7 +195,6 @@
<string name="nearby_notification_streaming">Yakındaki Bildirim Akışı</string>
<string name="enable_if_secure_enough">Yalnızca aynı yönetilen hesap</string>
<string name="lock_task_mode">Görev Kilitleme Modu</string>
<string name="specify_activity">Etkinliği Belirt</string>
<string name="app_not_allowed">Uygulamaya izin verilmiyor</string>
<string name="disable_all">Hepsini Devre Dışı Bırak</string>
<!--ltf: lock task feature-->
@@ -283,7 +282,6 @@
<string name="network_type">Ağ Türü</string>
<string name="mobile">Mobil</string>
<string name="ethernet">Ethernet</string>
<string name="subscriber_id">Abone Kimliği</string>
<string name="all">Tümü</string>
<string name="uninstalled">Kaldırılmış</string>
<string name="tethering">Bağlantı Paylaşımı</string>
@@ -298,13 +296,9 @@
<string name="roaming">Dolaşım</string>
<string name="metered">Ölçülü</string>
<string name="private_dns">Özel DNS</string>
<string name="dns_provide_hostname">Ana bilgisayar adı sağla</string>
<string name="host_not_serving_dns_tls">Ana bilgisayar DNS TLS sunmuyor</string>
<string name="set_to_opportunistic">Fırsatçı olarak ayarla</string>
<string name="dns_hostname">DNS Ana Bilgisayar Adı</string>
<string name="invalid_hostname">Geçersiz ana bilgisayar adı</string>
<string name="security_exception">Güvenlik İstisnası</string>
<string name="set_dns_host">DNS Ana Bilgisayarı Ayarla</string>
<string name="recommended_global_proxy">Önerilen küresel vekil</string>
<string name="proxy_type_off">Vekil yok</string>
<string name="proxy_type_pac">PAC vekil</string>
@@ -327,22 +321,12 @@
<string name="one_uid_per_line">Satır başına bir UID</string>
<string name="override_apn">APN\'yi Geçersiz Kıl</string>
<string name="apn_setting">APN Ayarı</string>
<string name="apn_name">APN Adı</string>
<string name="entry_name">Giriş Adı</string>
<string name="name">Ad</string>
<string name="profile_id">Profil Kimliği</string>
<string name="auth_type">Kimlik Doğrulama Türü</string>
<string name="description">ıklama</string>
<string name="mms_proxy">MMS Vekil</string>
<string name="address">Adres</string>
<string name="port">Port</string>
<string name="proxy">Vekil</string>
<string name="persistent">Kalıcı</string>
<string name="protocol">Protokol</string>
<string name="roaming_protocol">Dolaşım Protokolü</string>
<string name="carrier_id">Operatör Kimliği</string>
<string name="mvno_type">MVNO Türü</string>
<string name="network_type_bitmask">Ağ Türü Bitmask</string>
<string name="always_on">Her Zaman Açık</string>
<string name="update">Güncelle</string>
@@ -688,8 +672,6 @@
<string name="info_wipe_data_in_managed_user">Bu kullanıcının tüm verileri silinecek, ancak kullanıcı kaldırılmayacak.</string>
<string name="info_lockdown_admin_configured_network">Kullanıcının yönetici tarafından yapılandırılan ağları değiştirip değiştiremeyeceğini kontrol eder.\nBu kilit etkinleştirildiğinde, kullanıcı hala diğer Wi-Fi ağlarını yapılandırabilir ve bağlanabilir veya bağlantı paylaşımı gibi diğer Wi-Fi özelliklerini kullanabilir.</string>
<string name="info_minimum_wifi_security_level">Wi-Fi ağları için gereken minimum güvenlik seviyesini belirtir. Cihaz, minimum güvenlik seviyesini karşılamayan ağlara bağlanmayabilir. Mevcut ağ minimum güvenlik seviyesini karşılamıyorsa bağlantısı kesilecektir.</string>
<string name="info_private_dns_mode_oppertunistic">Bu modda, DNS alt sistemi, açık metinle ad çözümlemesi yapmadan önce ağ tarafından sağlanan çözücüye TLS el sıkışması yapmaya çalışır.</string>
<string name="info_set_private_dns_host">Çözücünün geçerli olduğunu doğrulamak için bir bağlantı kontrolü gerçekleştirir.\nÖzel DNS çözücüsü ile birlikte bir VPN kullanıldığında, Özel DNS çözücüsüne hem VPN içinden hem de dışından erişilebilir olmalıdır. Aksi takdirde, sistem trafiği VPN üzerinden çözücüye gitmeyebilir ve cihaz ana bilgisayar adlarını çözümleme yeteneğini kaybedebilir.</string>
<string name="info_always_on_vpn">Geçerli kullanıcı için belirli bir uygulama aracılığıyla her zaman açık bir VPN bağlantısı yapılandırır. Bu bağlantı otomatik olarak verilir ve yeniden başlatma sonrası kalıcıdır.\nKilitlemeyi etkinleştir: VPN bağlı değilken ağ kullanımını engelle.</string>
<string name="info_recommended_global_proxy">Bu vekil yalnızca bir öneridir ve bazı uygulamalar bunu yok sayabilir.</string>
<string name="info_network_log">Ağ kayıtları, DNS aramalarını ve connect() kütüphane çağrı olaylarını içerir.\nBu işlev iş profilinde kullanıldığında yalnızca iş profilindeki ağ kayıtlarını alır.\nCihaz sahibi tarafından kullanılıyorsa, cihazda bağlı olmayan kullanıcı olmamalıdır.</string>

View File

@@ -175,7 +175,6 @@
<string name="nearby_notification_streaming">附近通知传输</string>
<string name="enable_if_secure_enough">在足够安全时启用</string>
<string name="lock_task_mode">锁定任务模式</string>
<string name="specify_activity">指定Activity</string>
<string name="app_not_allowed">应用未被允许</string>
<string name="disable_all">禁用全部</string>
<string name="ltf_sys_info">允许状态栏信息</string>
@@ -248,6 +247,7 @@
<string name="add_network_result_invalid_configuration">Invalid configuration</string>
<string name="min_wifi_security_level">最低Wi-Fi安全等级</string>
<string name="wifi_security_open">开放</string>
<string name="unchanged">不更改</string>
<string name="lockdown_admin_configured_network">锁定由管理员配置的网络</string>
<string name="wifi_ssid_policy">Wi-Fi SSID策略</string>
<string name="already_exist">已经存在</string>
@@ -263,13 +263,13 @@
<string name="network_type">网络类型</string>
<string name="mobile">移动</string>
<string name="ethernet">以太网</string>
<string name="subscriber_id">订阅者ID</string>
<string name="all">全部</string>
<string name="uninstalled">已卸载</string>
<string name="tethering">热点</string>
<string name="choose_an_app" tools:ignore="TypographyEllipsis">选择一个app...</string>
<string name="input">输入</string>
<string name="query">查询</string>
<string name="no_data">无数据</string>
<string name="transmitted">发送</string>
<string name="received">接收</string>
<string name="state">状态</string>
@@ -278,13 +278,10 @@
<string name="roaming">漫游</string>
<string name="metered">按量计费</string>
<string name="private_dns">私人DNS</string>
<string name="dns_provide_hostname">指定主机名</string>
<string name="host_not_serving_dns_tls">主机不支持</string>
<string name="set_to_opportunistic">设为自动</string>
<string name="automatic">自动</string>
<string name="dns_hostname">DNS主机名</string>
<string name="invalid_hostname">无效主机名</string>
<string name="security_exception">安全错误</string>
<string name="set_dns_host">设置DNS主机</string>
<string name="recommended_global_proxy">建议的全局代理</string>
<string name="proxy_type_off">无代理</string>
<string name="proxy_type_pac">PAC代理</string>
@@ -307,22 +304,12 @@
<string name="one_uid_per_line">每行一个UID</string>
<string name="override_apn">覆盖APN</string>
<string name="apn_setting">APN设置</string>
<string name="apn_name">APN名称</string>
<string name="entry_name">条目名称</string>
<string name="name">名称</string>
<string name="profile_id">资料ID</string>
<string name="auth_type">认证类型</string>
<string name="description">描述</string>
<string name="mms_proxy">MMS代理</string>
<string name="address">地址</string>
<string name="port">端口</string>
<string name="proxy">代理</string>
<string name="persistent">持久化</string>
<string name="protocol">协议</string>
<string name="roaming_protocol">漫游协议</string>
<string name="carrier_id">运营商ID</string>
<string name="mvno_type">MVNO类型</string>
<string name="network_type_bitmask">网络类型位掩码</string>
<string name="always_on">总是开启</string>
<string name="update">更新</string>
@@ -672,8 +659,6 @@
<string name="info_wipe_data_in_managed_user">此用户的所有数据将会被清除,但是用户不会被删除。</string>
<string name="info_lockdown_admin_configured_network">控制用户是否可以更改管理员配置的网络。启用此锁定后用户仍然可以配置和连接到其他Wi-Fi或使用其他Wi-Fi功能如网络共享</string>
<string name="info_minimum_wifi_security_level">指定Wi-Fi网络所需的最低安全等级。设备将无法连接到低于最低安全等级的网络。如果当前网络不满足要求则会断开连接。</string>
<string name="info_private_dns_mode_oppertunistic">在此模式下DNS子系统将在尝试以明文形式进行域名解析之前尝试与网络提供的DNS服务器进行TLS握手。</string>
<string name="info_set_private_dns_host">将对DNS服务器执行连接检查以确保其有效。\n如果将VPN与私人DNS结合使用则私人DNS必须可从VPN内部和外部访问。否则设备可能会失去解析域名的能力因为到DNS服务器的系统流量可能不会通过VPN。</string>
<string name="info_always_on_vpn">通过一个指定的app为当前用户设置一个保持打开的VPN连接。自动授权连接并在重启后保留。\n启用锁定如果VPN未连接则禁止使用网络。</string>
<string name="info_recommended_global_proxy">这个代理只是一个建议一些app有可能忽略它。</string>
<string name="info_network_log">网络日志包含DNS查询和connect()库调用记录</string>

View File

@@ -203,7 +203,6 @@
<string name="nearby_notification_streaming">Nearby notification streaming policy</string>
<string name="enable_if_secure_enough">Same managed account only</string>
<string name="lock_task_mode">Lock task mode</string>
<string name="specify_activity">Specify Activity</string>
<string name="app_not_allowed">App is not allowed</string>
<string name="disable_all">Disable all</string>
<!--ltf: lock task feature-->
@@ -277,6 +276,10 @@
<string name="add_network_result_invalid_configuration">Invalid configuration</string>
<string name="min_wifi_security_level">Minimum Wi-Fi security level</string>
<string name="wifi_security_open">Open</string>
<string name="wifi_security_psk" translatable="false">PSK</string>
<string name="wifi_mode_dhcp" translatable="false">DHCP</string>
<string name="http" translatable="false">HTTP</string>
<string name="unchanged">Unchanged</string>
<string name="lockdown_admin_configured_network">Lockdown admin configured network</string>
<string name="wifi_ssid_policy">Wi-Fi SSID policy</string>
<string name="already_exist">Already exist</string>
@@ -294,13 +297,13 @@
<string name="mobile">Mobile</string>
<string name="ethernet">Ethernet</string>
<string name="vpn" translatable="false">VPN</string>
<string name="subscriber_id">Subscriber ID</string>
<string name="all">All</string>
<string name="uninstalled">Uninstalled</string>
<string name="tethering">Tethering</string>
<string name="choose_an_app" tools:ignore="TypographyEllipsis">Choose an app...</string>
<string name="input">Input</string>
<string name="query">Query</string>
<string name="no_data">No data</string>
<string name="transmitted">Transmitted</string>
<string name="received">Received</string>
<string name="state">State</string>
@@ -309,13 +312,10 @@
<string name="roaming">Roaming</string>
<string name="metered">Metered</string>
<string name="private_dns">Private DNS</string>
<string name="dns_provide_hostname">Provide hostname</string>
<string name="host_not_serving_dns_tls">Host not serving</string>
<string name="set_to_opportunistic">Set to opportunistic</string>
<string name="automatic">Automatic</string>
<string name="dns_hostname">DNS hostname</string>
<string name="invalid_hostname">Invalid hostname</string>
<string name="security_exception">Security Exception</string>
<string name="set_dns_host">Set DNS host</string>
<string name="recommended_global_proxy">Recommended global proxy</string>
<string name="proxy_type_off">No proxy</string>
<string name="proxy_type_pac">PAC proxy</string>
@@ -338,22 +338,12 @@
<string name="one_uid_per_line">One UID per line</string>
<string name="override_apn">Override APN</string>
<string name="apn_setting">APN setting</string>
<string name="apn_name">APN name</string>
<string name="entry_name">Entry name</string>
<string name="name">Name</string>
<string name="profile_id">Profile ID</string>
<string name="auth_type">Auth type</string>
<string name="description">Description</string>
<string name="mms_proxy">MMS proxy</string>
<string name="address">Address</string>
<string name="port">Port</string>
<string name="proxy">Proxy</string>
<string name="persistent">Persistent</string>
<string name="protocol">Protocol</string>
<string name="roaming_protocol">Roaming protocol</string>
<string name="carrier_id">Carrier ID</string>
<string name="mvno_type">MVNO type</string>
<string name="network_type_bitmask">Network type bitmask</string>
<string name="always_on">Always on</string>
<string name="update">Update</string>
@@ -706,8 +696,6 @@
<string name="info_wipe_data_in_managed_user">All data of this user will be wiped, but that user won\'t be removed.</string>
<string name="info_lockdown_admin_configured_network">Control whether the user can change networks configured by the admin.\nWhen this lockdown is enabled, the user can still configure and connect to other Wi-Fi networks, or use other Wi-Fi capabilities such as tethering.</string>
<string name="info_minimum_wifi_security_level">Specify the minimum security level required for Wi-Fi networks. The device may not connect to networks that do not meet the minimum security level. If the current network does not meet the minimum security level set, it will be disconnected.</string>
<string name="info_private_dns_mode_oppertunistic">In this mode, the DNS subsystem will attempt a TLS handshake to the network-supplied resolver prior to attempting name resolution in cleartext.</string>
<string name="info_set_private_dns_host">It will perform a connectivity check to the resolver, to ensure it is valid.\nIn case a VPN is used in conjunction with Private DNS resolver, the Private DNS resolver must be reachable both from within and outside the VPN. Otherwise, the device may lose the ability to resolve hostnames as system traffic to the resolver may not go through the VPN.</string>
<string name="info_always_on_vpn">Configure an always-on VPN connection through a specific application for the current user. This connection is automatically granted and persisted after a reboot.\nEnable lockdown: Disallow networking when the VPN is not connected.</string>
<string name="info_recommended_global_proxy">This proxy is only a recommendation and it is possible that some apps will ignore it.</string>
<string name="info_network_log">Network logs contain DNS lookup and connect() library call events.\nUse this function in work profile will only retrieve network logs in work profile.\nThere shouldn\'t be unaffiliated user on this device if used by Device owner.</string>