mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Add new restrictions that can be set by the device owner
This commit is contained in:
parent
6ba9614557
commit
abe6d47a96
10 changed files with 100 additions and 14 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -295,4 +295,7 @@ object ExperimentalFlags {
|
||||||
|
|
||||||
object ConsentFlags {
|
object ConsentFlags {
|
||||||
const val APP_LIST_SYNC = 1L
|
const val APP_LIST_SYNC = 1L
|
||||||
|
|
||||||
|
// this is used internally
|
||||||
|
const val BLOCK_USER_SWITCH_BY_DEFAULT = 2L
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -73,4 +73,5 @@ data class DeviceRelatedData (
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isExperimentalFlagSetSync(flags: Long) = (experimentalFlags and flags) == flags
|
fun isExperimentalFlagSetSync(flags: Long) = (experimentalFlags and flags) == flags
|
||||||
|
fun isConsentFlagSet(flags: Long) = (consentFlags and flags) == flags
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,6 +27,10 @@ import io.timelimit.android.integration.platform.PlatformFeature
|
||||||
object AndroidFeatures {
|
object AndroidFeatures {
|
||||||
private const val FEATURE_ADB = "adb"
|
private const val FEATURE_ADB = "adb"
|
||||||
private const val FEATURE_CONFIG_PRIVATE_DNS = "dns"
|
private const val FEATURE_CONFIG_PRIVATE_DNS = "dns"
|
||||||
|
const val FEATURE_ADD_USER = "add_user"
|
||||||
|
const val FEATURE_USER_SWITCH = "user_switch"
|
||||||
|
private const val FEATURE_VPN = "vpn"
|
||||||
|
private const val FEATURE_UNKNOWN_SOURCES = "unknown_sources"
|
||||||
|
|
||||||
fun applyBlockedFeatures(features: Set<String>, policyManager: DevicePolicyManager, admin: ComponentName): Boolean {
|
fun applyBlockedFeatures(features: Set<String>, policyManager: DevicePolicyManager, admin: ComponentName): Boolean {
|
||||||
fun apply(feature: String, restriction: String) {
|
fun apply(feature: String, restriction: String) {
|
||||||
|
@ -40,6 +44,18 @@ object AndroidFeatures {
|
||||||
apply(FEATURE_CONFIG_PRIVATE_DNS, UserManager.DISALLOW_CONFIG_PRIVATE_DNS)
|
apply(FEATURE_CONFIG_PRIVATE_DNS, UserManager.DISALLOW_CONFIG_PRIVATE_DNS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply(FEATURE_ADD_USER, UserManager.DISALLOW_ADD_USER)
|
||||||
|
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.P) {
|
||||||
|
apply(FEATURE_USER_SWITCH, UserManager.DISALLOW_USER_SWITCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(FEATURE_VPN, UserManager.DISALLOW_CONFIG_VPN)
|
||||||
|
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
||||||
|
apply(FEATURE_UNKNOWN_SOURCES, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +76,30 @@ object AndroidFeatures {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.add(PlatformFeature(
|
||||||
|
id = FEATURE_ADD_USER,
|
||||||
|
title = context.getString(R.string.dummy_app_feature_add_user)
|
||||||
|
))
|
||||||
|
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.P) {
|
||||||
|
result.add(PlatformFeature(
|
||||||
|
id = FEATURE_USER_SWITCH,
|
||||||
|
title = context.getString(R.string.dummy_app_feature_switch_user)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(PlatformFeature(
|
||||||
|
id = FEATURE_VPN,
|
||||||
|
title = context.getString(R.string.dummy_app_feature_vpn)
|
||||||
|
))
|
||||||
|
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
||||||
|
result.add(PlatformFeature(
|
||||||
|
id = FEATURE_UNKNOWN_SOURCES,
|
||||||
|
title = context.getString(R.string.dummy_app_feature_unknown_sources)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -507,7 +507,6 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
|
||||||
|
|
||||||
if (enableLockdown) {
|
if (enableLockdown) {
|
||||||
// disable problematic features
|
// disable problematic features
|
||||||
policyManager.addUserRestriction(deviceAdmin, UserManager.DISALLOW_ADD_USER)
|
|
||||||
policyManager.addUserRestriction(deviceAdmin, UserManager.DISALLOW_FACTORY_RESET)
|
policyManager.addUserRestriction(deviceAdmin, UserManager.DISALLOW_FACTORY_RESET)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
@ -557,7 +556,6 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
|
||||||
}
|
}
|
||||||
} else /* disable lockdown */ {
|
} else /* disable lockdown */ {
|
||||||
// enable problematic features
|
// enable problematic features
|
||||||
policyManager.clearUserRestriction(deviceAdmin, UserManager.DISALLOW_ADD_USER)
|
|
||||||
policyManager.clearUserRestriction(deviceAdmin, UserManager.DISALLOW_FACTORY_RESET)
|
policyManager.clearUserRestriction(deviceAdmin, UserManager.DISALLOW_FACTORY_RESET)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -65,6 +65,11 @@ class AppSetupLogic(private val appLogic: AppLogic) {
|
||||||
appLogic.database.deleteAllData()
|
appLogic.database.deleteAllData()
|
||||||
|
|
||||||
appLogic.database.config().setCustomServerUrlSync(customServerUrl)
|
appLogic.database.config().setCustomServerUrlSync(customServerUrl)
|
||||||
|
|
||||||
|
appLogic.database.config().setConsentFlagSync(
|
||||||
|
ConsentFlags.BLOCK_USER_SWITCH_BY_DEFAULT,
|
||||||
|
true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
run {
|
run {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,10 +19,12 @@ import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.data.invalidation.Observer
|
import io.timelimit.android.data.invalidation.Observer
|
||||||
import io.timelimit.android.data.invalidation.Table
|
import io.timelimit.android.data.invalidation.Table
|
||||||
import io.timelimit.android.data.model.CategoryApp
|
import io.timelimit.android.data.model.CategoryApp
|
||||||
|
import io.timelimit.android.data.model.ConsentFlags
|
||||||
import io.timelimit.android.data.model.ExperimentalFlags
|
import io.timelimit.android.data.model.ExperimentalFlags
|
||||||
import io.timelimit.android.data.model.UserType
|
import io.timelimit.android.data.model.UserType
|
||||||
import io.timelimit.android.data.model.derived.UserRelatedData
|
import io.timelimit.android.data.model.derived.UserRelatedData
|
||||||
import io.timelimit.android.integration.platform.ProtectionLevel
|
import io.timelimit.android.integration.platform.ProtectionLevel
|
||||||
|
import io.timelimit.android.integration.platform.android.AndroidFeatures
|
||||||
import io.timelimit.android.integration.platform.android.AndroidIntegrationApps
|
import io.timelimit.android.integration.platform.android.AndroidIntegrationApps
|
||||||
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
|
import io.timelimit.android.logic.blockingreason.CategoryHandlingCache
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
@ -107,12 +109,23 @@ class SuspendAppsLogic(private val appLogic: AppLogic): Observer {
|
||||||
val hasManagedFeatures = featureCategoryApps.isNotEmpty()
|
val hasManagedFeatures = featureCategoryApps.isNotEmpty()
|
||||||
val enableBlocking = isRestrictedUser && (enableBlockingAtSystemLevel || hasManagedFeatures)
|
val enableBlocking = isRestrictedUser && (enableBlockingAtSystemLevel || hasManagedFeatures)
|
||||||
|
|
||||||
|
val blockUserSwitchByDefault =
|
||||||
|
userAndDeviceRelatedData?.deviceRelatedData?.isConsentFlagSet(ConsentFlags.BLOCK_USER_SWITCH_BY_DEFAULT) == true
|
||||||
|
&& userAndDeviceRelatedData.userRelatedData?.user?.type == UserType.Child
|
||||||
|
|
||||||
|
val featureToAllowDefaults = mapOf(
|
||||||
|
AndroidFeatures.FEATURE_ADD_USER to false,
|
||||||
|
AndroidFeatures.FEATURE_USER_SWITCH to !blockUserSwitchByDefault
|
||||||
|
)
|
||||||
|
|
||||||
if (!enableBlocking) {
|
if (!enableBlocking) {
|
||||||
lastDefaultCategory = null
|
lastDefaultCategory = null
|
||||||
lastAllowedCategoryList = emptySet()
|
lastAllowedCategoryList = emptySet()
|
||||||
lastCategoryApps = emptyList()
|
lastCategoryApps = emptyList()
|
||||||
applySuspendedApps(emptyList())
|
applySuspendedApps(emptyList())
|
||||||
applyBlockedFeatures(emptySet())
|
applyBlockedFeatures(
|
||||||
|
featureToAllowDefaults.filter { !it.value }.map { it.key }.toSet()
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -191,9 +204,15 @@ class SuspendAppsLogic(private val appLogic: AppLogic): Observer {
|
||||||
val deviceSpecificFeatureIdentifiers = deviceSpecificFeatures.map { it.appSpecifierString }.toSet()
|
val deviceSpecificFeatureIdentifiers = deviceSpecificFeatures.map { it.appSpecifierString }.toSet()
|
||||||
val globalFeatures = featureCategoryApps.filter { !deviceSpecificFeatureIdentifiers.contains(it.appSpecifierString) }
|
val globalFeatures = featureCategoryApps.filter { !deviceSpecificFeatureIdentifiers.contains(it.appSpecifierString) }
|
||||||
val effectiveFeatures = deviceSpecificFeatures + globalFeatures
|
val effectiveFeatures = deviceSpecificFeatures + globalFeatures
|
||||||
val featuresToBlock = effectiveFeatures.filter { !categoryIdsToAllow.contains(it.categoryId) }
|
|
||||||
.map { it.appSpecifierString.substring(DummyApps.FEATURE_APP_PREFIX.length) }
|
val featuresToAllow = featureToAllowDefaults + effectiveFeatures.associate {
|
||||||
.toSet()
|
Pair(
|
||||||
|
it.appSpecifierString.substring(DummyApps.FEATURE_APP_PREFIX.length),
|
||||||
|
categoryIdsToAllow.contains(it.categoryId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val featuresToBlock = featuresToAllow.filter { !it.value }.map { it.key }.toSet()
|
||||||
|
|
||||||
applySuspendedApps(appsToBlock)
|
applySuspendedApps(appsToBlock)
|
||||||
applyBlockedFeatures(featuresToBlock)
|
applyBlockedFeatures(featuresToBlock)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -23,6 +23,7 @@ import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.coroutines.executeAndWait
|
import io.timelimit.android.coroutines.executeAndWait
|
||||||
import io.timelimit.android.data.backup.DatabaseBackup
|
import io.timelimit.android.data.backup.DatabaseBackup
|
||||||
import io.timelimit.android.data.devicename.DeviceName
|
import io.timelimit.android.data.devicename.DeviceName
|
||||||
|
import io.timelimit.android.data.model.ConsentFlags
|
||||||
import io.timelimit.android.logic.AppLogic
|
import io.timelimit.android.logic.AppLogic
|
||||||
import io.timelimit.android.sync.ApplyServerDataStatus
|
import io.timelimit.android.sync.ApplyServerDataStatus
|
||||||
import io.timelimit.android.sync.network.NewDeviceInfo
|
import io.timelimit.android.sync.network.NewDeviceInfo
|
||||||
|
@ -333,6 +334,11 @@ object SetupParentHandling {
|
||||||
database.config().setDeviceAuthTokenSync(result.deviceAuthToken)
|
database.config().setDeviceAuthTokenSync(result.deviceAuthToken)
|
||||||
database.config().setEnableBackgroundSync(state.backgroundSync)
|
database.config().setEnableBackgroundSync(state.backgroundSync)
|
||||||
|
|
||||||
|
database.config().setConsentFlagSync(
|
||||||
|
ConsentFlags.BLOCK_USER_SWITCH_BY_DEFAULT,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
ApplyServerDataStatus.applyServerDataStatusSync(result.serverDataStatus, logic.database, logic.platformIntegration)
|
ApplyServerDataStatus.applyServerDataStatusSync(result.serverDataStatus, logic.database, logic.platformIntegration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2023 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -24,6 +24,7 @@ import io.timelimit.android.coroutines.executeAndWait
|
||||||
import io.timelimit.android.coroutines.runAsync
|
import io.timelimit.android.coroutines.runAsync
|
||||||
import io.timelimit.android.data.backup.DatabaseBackup
|
import io.timelimit.android.data.backup.DatabaseBackup
|
||||||
import io.timelimit.android.data.devicename.DeviceName
|
import io.timelimit.android.data.devicename.DeviceName
|
||||||
|
import io.timelimit.android.data.model.ConsentFlags
|
||||||
import io.timelimit.android.livedata.castDown
|
import io.timelimit.android.livedata.castDown
|
||||||
import io.timelimit.android.logic.AppLogic
|
import io.timelimit.android.logic.AppLogic
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
@ -70,6 +71,11 @@ class SetupRemoteChildViewModel(application: Application): AndroidViewModel(appl
|
||||||
logic.database.config().setOwnDeviceIdSync(registerResponse.ownDeviceId)
|
logic.database.config().setOwnDeviceIdSync(registerResponse.ownDeviceId)
|
||||||
logic.database.config().setDeviceAuthTokenSync(registerResponse.deviceAuthToken)
|
logic.database.config().setDeviceAuthTokenSync(registerResponse.deviceAuthToken)
|
||||||
|
|
||||||
|
logic.database.config().setConsentFlagSync(
|
||||||
|
ConsentFlags.BLOCK_USER_SWITCH_BY_DEFAULT,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
ApplyServerDataStatus.applyServerDataStatusSync(clientStatusResponse, logic.database, logic.platformIntegration)
|
ApplyServerDataStatus.applyServerDataStatusSync(clientStatusResponse, logic.database, logic.platformIntegration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
TimeLimit Copyright <C> 2019 - 2024 Jonas Lochmann
|
TimeLimit Copyright <C> 2019 - 2025 Jonas Lochmann
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation version 3 of the License.
|
the Free Software Foundation version 3 of the License.
|
||||||
|
@ -1752,6 +1752,10 @@
|
||||||
<string name="dummy_app_unassigned_system_image_app">nicht zugeordnete Apps von der Systempartition</string>
|
<string name="dummy_app_unassigned_system_image_app">nicht zugeordnete Apps von der Systempartition</string>
|
||||||
<string name="dummy_app_feature_adb">Entwickleroptionen</string>
|
<string name="dummy_app_feature_adb">Entwickleroptionen</string>
|
||||||
<string name="dummy_app_feature_dns">DNS-Einstellungen</string>
|
<string name="dummy_app_feature_dns">DNS-Einstellungen</string>
|
||||||
|
<string name="dummy_app_feature_add_user">Systembenutzer erstellen</string>
|
||||||
|
<string name="dummy_app_feature_switch_user">Systembenutzer wechseln</string>
|
||||||
|
<string name="dummy_app_feature_vpn">VPN konfigurieren</string>
|
||||||
|
<string name="dummy_app_feature_unknown_sources">Apps aus unbekannten Quellen installieren</string>
|
||||||
<string name="dummy_app_activity_audio">Hintergrundmusikwiedergabe</string>
|
<string name="dummy_app_activity_audio">Hintergrundmusikwiedergabe</string>
|
||||||
|
|
||||||
<string name="notify_permission_title">Benachrichtigungen</string>
|
<string name="notify_permission_title">Benachrichtigungen</string>
|
||||||
|
|
|
@ -1804,6 +1804,10 @@
|
||||||
<string name="dummy_app_unassigned_system_image_app">not assigned Apps from the system image</string>
|
<string name="dummy_app_unassigned_system_image_app">not assigned Apps from the system image</string>
|
||||||
<string name="dummy_app_feature_adb">Developer Options</string>
|
<string name="dummy_app_feature_adb">Developer Options</string>
|
||||||
<string name="dummy_app_feature_dns">DNS Settings</string>
|
<string name="dummy_app_feature_dns">DNS Settings</string>
|
||||||
|
<string name="dummy_app_feature_add_user">Create System User</string>
|
||||||
|
<string name="dummy_app_feature_switch_user">Switch System User</string>
|
||||||
|
<string name="dummy_app_feature_vpn">Configure VPN</string>
|
||||||
|
<string name="dummy_app_feature_unknown_sources">Install Apps from unknown sources</string>
|
||||||
<string name="dummy_app_activity_audio">Background Audio Playback</string>
|
<string name="dummy_app_activity_audio">Background Audio Playback</string>
|
||||||
|
|
||||||
<string name="notify_permission_title">Notifications</string>
|
<string name="notify_permission_title">Notifications</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue