ui: add view to debug MDM settings and add the syspolicy handlers (#199)
* mdm: add Android syspolicy handler (#195) Updates tailscale/corp#18202 Adds a syspolicy handler for Android in cmd/tailscale. This allows the Go code to use the syspolicy package to read values set by a system administrator using the Android RestrictionsManager. Out of the box, this adds supports for a number of MDM policies that are fully integrated on the Go side, such as `ExitNodeID` (forced exit node functionality). Signed-off-by: Andrea Gottardo <andrea@gottardo.me> Signed-off-by: Jonathan Nobels <jonathan@tailscale.com> * ui: add view to debug MDM settings Adds a view to see the currently set MDM settings, we're going to need this to debug actual MDM integrations more effectively. Signed-off-by: Andrea Gottardo <andrea@gottardo.me> Signed-off-by: Jonathan Nobels <jonathan@tailscale.com> --------- Signed-off-by: Andrea Gottardo <andrea@gottardo.me> Signed-off-by: Jonathan Nobels <jonathan@tailscale.com> Co-authored-by: Andrea Gottardo <andrea@gottardo.me>pull/205/head
parent
1f457399b8
commit
f275656c25
@ -0,0 +1,119 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package com.tailscale.ipn.ui.view
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import com.tailscale.ipn.R
|
||||||
|
import com.tailscale.ipn.mdm.AlwaysNeverUserDecidesSetting
|
||||||
|
import com.tailscale.ipn.mdm.BooleanSetting
|
||||||
|
import com.tailscale.ipn.mdm.MDMSettings
|
||||||
|
import com.tailscale.ipn.mdm.ShowHideSetting
|
||||||
|
import com.tailscale.ipn.mdm.StringArraySetting
|
||||||
|
import com.tailscale.ipn.mdm.StringSetting
|
||||||
|
import com.tailscale.ipn.ui.util.defaultPaddingModifier
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun MDMSettingsDebugView(mdmSettings: MDMSettings) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
title = {
|
||||||
|
Text(stringResource(R.string.current_mdm_settings))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { innerPadding ->
|
||||||
|
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
||||||
|
items(enumValues<BooleanSetting>()) { booleanSetting ->
|
||||||
|
MDMSettingView(
|
||||||
|
title = booleanSetting.localizedTitle,
|
||||||
|
caption = booleanSetting.key,
|
||||||
|
valueDescription = mdmSettings.get(booleanSetting).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
items(enumValues<StringSetting>()) { stringSetting ->
|
||||||
|
MDMSettingView(
|
||||||
|
title = stringSetting.localizedTitle,
|
||||||
|
caption = stringSetting.key,
|
||||||
|
valueDescription = mdmSettings.get(stringSetting).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
items(enumValues<ShowHideSetting>()) { showHideSetting ->
|
||||||
|
MDMSettingView(
|
||||||
|
title = showHideSetting.localizedTitle,
|
||||||
|
caption = showHideSetting.key,
|
||||||
|
valueDescription = mdmSettings.get(showHideSetting).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
items(enumValues<AlwaysNeverUserDecidesSetting>()) { anuSetting ->
|
||||||
|
MDMSettingView(
|
||||||
|
title = anuSetting.localizedTitle,
|
||||||
|
caption = anuSetting.key,
|
||||||
|
valueDescription = mdmSettings.get(anuSetting).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
items(enumValues<StringArraySetting>()) { stringArraySetting ->
|
||||||
|
MDMSettingView(
|
||||||
|
title = stringArraySetting.localizedTitle,
|
||||||
|
caption = stringArraySetting.key,
|
||||||
|
valueDescription = mdmSettings.get(stringArraySetting).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MDMSettingView(title: String, caption: String, valueDescription: String) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
modifier = defaultPaddingModifier()
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Text(title, maxLines = 3)
|
||||||
|
Text(
|
||||||
|
caption,
|
||||||
|
fontSize = MaterialTheme.typography.labelSmall.fontSize,
|
||||||
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
fontFamily = FontFamily.Monospace
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
valueDescription,
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
maxLines = 1,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/tailscale/tailscale-android/cmd/jni"
|
||||||
|
"tailscale.com/util/syspolicy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// androidHandler is a syspolicy handler for the Android version of the Tailscale client,
|
||||||
|
// which lets the main networking code read values set via the Android RestrictionsManager.
|
||||||
|
type androidHandler struct {
|
||||||
|
a *App
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h androidHandler) ReadString(key string) (string, error) {
|
||||||
|
if key == "" {
|
||||||
|
return "", syspolicy.ErrNoSuchKey
|
||||||
|
}
|
||||||
|
retVal := ""
|
||||||
|
err := jni.Do(h.a.jvm, func(env *jni.Env) error {
|
||||||
|
cls := jni.GetObjectClass(env, h.a.appCtx)
|
||||||
|
m := jni.GetMethodID(env, cls, "getSyspolicyStringValue", "(Ljava/lang/String;)Ljava/lang/String;")
|
||||||
|
strObj, err := jni.CallObjectMethod(env, h.a.appCtx, m, jni.Value(jni.JavaString(env, key)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
retVal = jni.GoString(env, jni.String(strObj))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("syspolicy: failed to get string value via JNI: %v", err)
|
||||||
|
}
|
||||||
|
return retVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h androidHandler) ReadBoolean(key string) (bool, error) {
|
||||||
|
if key == "" {
|
||||||
|
return false, syspolicy.ErrNoSuchKey
|
||||||
|
}
|
||||||
|
retVal := false
|
||||||
|
err := jni.Do(h.a.jvm, func(env *jni.Env) error {
|
||||||
|
cls := jni.GetObjectClass(env, h.a.appCtx)
|
||||||
|
m := jni.GetMethodID(env, cls, "getSyspolicyBooleanValue", "(Ljava/lang/String;)Z")
|
||||||
|
b, err := jni.CallBooleanMethod(env, h.a.appCtx, m, jni.Value(jni.JavaString(env, key)))
|
||||||
|
retVal = b
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("syspolicy: failed to get bool value via JNI: %v", err)
|
||||||
|
}
|
||||||
|
return retVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h androidHandler) ReadUInt64(key string) (uint64, error) {
|
||||||
|
if key == "" {
|
||||||
|
return 0, syspolicy.ErrNoSuchKey
|
||||||
|
}
|
||||||
|
// TODO(angott): drop ReadUInt64 everywhere. We are not using it.
|
||||||
|
log.Fatalf("ReadUInt64 is not implemented on Android")
|
||||||
|
return 0, nil
|
||||||
|
}
|
Loading…
Reference in New Issue