diff --git a/android/src/main/java/com/tailscale/ipn/App.kt b/android/src/main/java/com/tailscale/ipn/App.kt
index 1329019..3326de3 100644
--- a/android/src/main/java/com/tailscale/ipn/App.kt
+++ b/android/src/main/java/com/tailscale/ipn/App.kt
@@ -146,6 +146,10 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
}
private fun initializeApp() {
+ // Read MDM settings as early as possible, before starting the go backend.
+ val rm = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
+ MDMSettings.update(this, rm, true)
+
// Check if a directory URI has already been stored.
val storedUri = getStoredDirectoryUri()
if (storedUri != null && storedUri.toString().startsWith("content://")) {
@@ -158,8 +162,6 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
NetworkChangeCallback.monitorDnsChanges(connectivityManager, dns)
initViewModels()
applicationScope.launch {
- val rm = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
- MDMSettings.update(get(), rm)
Notifier.state.collect { _ ->
combine(Notifier.state, MDMSettings.forceEnabled.flow, Notifier.prefs, Notifier.netmap) {
state,
@@ -545,6 +547,13 @@ open class UninitializedApp : Application() {
}
fun getIsClientLoggingEnabled(): Boolean {
+
+ // Force client logging to be enabled, when the device is managed by MDM
+ // Later this could become a dedicated MDMSetting / restriction.
+ if (MDMSettings.isMDMConfigured) {
+ return true
+ }
+
return getUnencryptedPrefs().getBoolean(IS_CLIENT_LOGGING_ENABLED_KEY, true)
}
diff --git a/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt b/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt
index 34b341f..5aa5b04 100644
--- a/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt
+++ b/android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt
@@ -18,6 +18,11 @@ object MDMSettings {
// to the backend.
class NoSuchKeyException : Exception("no such key")
+ // We default this to true, so that stricter behavior is used during initialization,
+ // prior to receiving MDM restrictions.
+ var isMDMConfigured = true
+ private set
+
val forceEnabled = BooleanMDMSetting("ForceEnabled", "Force Enabled Connection Toggle")
// Handled on the backed
@@ -117,10 +122,15 @@ object MDMSettings {
val allSettingsByKey by lazy { allSettings.associateBy { it.key } }
- fun update(app: App, restrictionsManager: RestrictionsManager?) {
+ fun update(app: App, restrictionsManager: RestrictionsManager?, skipNotify: Boolean = false) {
val bundle = restrictionsManager?.applicationRestrictions
val preferences = lazy { app.getEncryptedPrefs() }
allSettings.forEach { it.setFrom(bundle, preferences) }
- app.notifyPolicyChanged()
+
+ isMDMConfigured = bundle?.isEmpty == true
+
+ if (!skipNotify) {
+ app.notifyPolicyChanged()
+ }
}
}
diff --git a/android/src/main/java/com/tailscale/ipn/mdm/MDMSettingsChangedReceiver.kt b/android/src/main/java/com/tailscale/ipn/mdm/MDMSettingsChangedReceiver.kt
index d54129d..7647308 100644
--- a/android/src/main/java/com/tailscale/ipn/mdm/MDMSettingsChangedReceiver.kt
+++ b/android/src/main/java/com/tailscale/ipn/mdm/MDMSettingsChangedReceiver.kt
@@ -16,7 +16,16 @@ class MDMSettingsChangedReceiver : BroadcastReceiver() {
TSLog.d("syspolicy", "MDM settings changed")
val restrictionsManager =
context?.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
+
+ val previouslyIsMDMEnabled = MDMSettings.isMDMConfigured
+
MDMSettings.update(App.get(), restrictionsManager)
+
+ if (MDMSettings.isMDMConfigured && !previouslyIsMDMEnabled) {
+ // async MDM settings updated from disabled -> enabled. restart to ensure
+ // correctly applied (particularly forcing client logs on).
+ // TODO: actually restart
+ }
}
}
}
diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt
index 75ccc4d..6e86e87 100644
--- a/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt
+++ b/android/src/main/java/com/tailscale/ipn/ui/view/SettingsView.kt
@@ -111,8 +111,13 @@ fun SettingsView(
Lists.ItemDivider()
Setting.Switch(
R.string.client_remote_logging_enabled,
- subtitle = stringResource(R.string.client_remote_logging_enabled_subtitle),
+ subtitle =
+ stringResource(
+ if (MDMSettings.isMDMConfigured)
+ R.string.client_remote_logging_enabled_subtitle_mdm
+ else R.string.client_remote_logging_enabled_subtitle),
isOn = isClientRemoteLoggingEnabled,
+ enabled = !MDMSettings.isMDMConfigured,
onToggle = { viewModel.toggleIsClientRemoteLoggingEnabled() })
if (!AndroidTVUtil.isAndroidTV()) {
diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml
index c681ade..4f94eef 100644
--- a/android/src/main/res/values/strings.xml
+++ b/android/src/main/res/values/strings.xml
@@ -348,6 +348,7 @@
Subnet routing
Remote client logging
Whether debug logs are uploaded to Tailscale support. When disabled no support or network flow logs.\nChanges require restarting the app to take effect.
+ Client logging is always enabled for devices under remote management.
Specifies a device name to be used instead of the automatic default.
Hostname
Failed to save