ui: port syspolicy handler code to new app

port over https://github.com/tailscale/tailscale-android/pull/199 from cmd/tailscale and legacy_android to libtailscale and android/

Updates tailscale/corp#18202

Signed-off-by: kari-ts <kari@tailscale.com>
pull/308/head
kari-ts 2 months ago
parent 38f57b4737
commit 6668265ea5

@ -15,6 +15,7 @@ import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.RestrictionsManager
import android.content.SharedPreferences
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
@ -37,6 +38,10 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.tailscale.ipn.mdm.AlwaysNeverUserDecidesMDMSetting
import com.tailscale.ipn.mdm.BooleanMDMSetting
import com.tailscale.ipn.mdm.ShowHideMDMSetting
import com.tailscale.ipn.mdm.StringMDMSetting
import com.tailscale.ipn.ui.localapi.Client
import com.tailscale.ipn.ui.localapi.Request
import com.tailscale.ipn.ui.model.Ipn
@ -451,4 +456,32 @@ class App : Application(), libtailscale.AppContext {
return downloads
}
@Throws(IOException::class, GeneralSecurityException::class)
override fun getSyspolicyBooleanValue(key: String): Boolean {
val manager = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
return BooleanMDMSetting(key, key).getFrom(manager.applicationRestrictions, this)
}
@Throws(IOException::class, GeneralSecurityException::class)
override fun getSyspolicyStringValue(key: String): String {
val manager = getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
val alwaysNeverSetting = AlwaysNeverUserDecidesMDMSetting(key, key, false)
val showHideSetting = ShowHideMDMSetting(key, key, false)
val stringSetting = StringMDMSetting(key, key, false)
return when {
alwaysNeverSetting.keyExists(key) ->
alwaysNeverSetting.getFrom(manager.applicationRestrictions, this).value
showHideSetting.keyExists(key) ->
showHideSetting.getFrom(manager.applicationRestrictions, this).value
stringSetting.keyExists(key) ->
stringSetting.getFrom(manager.applicationRestrictions, this) ?: ""
else -> {
Log.d("MDM", "$key is not defined on Android. Returning empty.")
""
}
}
}
}

@ -4,14 +4,32 @@
package com.tailscale.ipn.mdm
import android.os.Bundle
import android.util.Log
import com.tailscale.ipn.App
import com.tailscale.ipn.ui.util.set
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
abstract class MDMSetting<T>(defaultValue: T, val key: String, val localizedTitle: String) {
// Don't keep track of the key if shouldRegisterKey is false - shouldRegisterKey should be false when creating an MDMSetting to check whether a key has been registered with that setting.
abstract class MDMSetting<T>(defaultValue: T, val key: String, val localizedTitle: String, private val shouldRegisterKey: Boolean = true) {
private val keys = mutableSetOf<String>()
init {
if (shouldRegisterKey) { registerKey(key)
}
}
val flow: StateFlow<T> = MutableStateFlow<T>(defaultValue)
private fun registerKey(key: String) {
if (!keyExists(key)) {
keys.add(key)
}
}
fun keyExists(key: String): Boolean {
return keys.contains(key)
}
fun setFrom(bundle: Bundle?, app: App) {
val v = getFrom(bundle, app)
flow.set(v)
@ -26,8 +44,8 @@ class BooleanMDMSetting(key: String, localizedTitle: String) :
bundle?.getBoolean(key) ?: app.getEncryptedPrefs().getBoolean(key, false)
}
class StringMDMSetting(key: String, localizedTitle: String) :
MDMSetting<String?>(null, key, localizedTitle) {
class StringMDMSetting(key: String, localizedTitle: String, shouldRegisterKey: Boolean = true) :
MDMSetting<String?>(null, key, localizedTitle, shouldRegisterKey) {
override fun getFrom(bundle: Bundle?, app: App) =
bundle?.getString(key) ?: app.getEncryptedPrefs().getString(key, null)
}
@ -39,8 +57,8 @@ class StringArrayListMDMSetting(key: String, localizedTitle: String) :
?: app.getEncryptedPrefs().getStringSet(key, HashSet<String>())?.toList()
}
class AlwaysNeverUserDecidesMDMSetting(key: String, localizedTitle: String) :
MDMSetting<AlwaysNeverUserDecides>(AlwaysNeverUserDecides.UserDecides, key, localizedTitle) {
class AlwaysNeverUserDecidesMDMSetting(key: String, localizedTitle: String, shouldRegisterKey: Boolean = true) :
MDMSetting<AlwaysNeverUserDecides>(AlwaysNeverUserDecides.UserDecides, key, localizedTitle, shouldRegisterKey) {
override fun getFrom(bundle: Bundle?, app: App): AlwaysNeverUserDecides {
val storedString =
bundle?.getString(key)
@ -60,8 +78,8 @@ class AlwaysNeverUserDecidesMDMSetting(key: String, localizedTitle: String) :
}
}
class ShowHideMDMSetting(key: String, localizedTitle: String) :
MDMSetting<ShowHide>(ShowHide.Show, key, localizedTitle) {
class ShowHideMDMSetting(key: String, localizedTitle: String, shouldRegisterKey: Boolean = true) :
MDMSetting<ShowHide>(ShowHide.Show, key, localizedTitle, shouldRegisterKey) {
override fun getFrom(bundle: Bundle?, app: App): ShowHide {
val storedString =
bundle?.getString(key)

@ -45,6 +45,12 @@ type AppContext interface {
// GetPlatformDNSConfig gets a string representation of the current DNS
// configuration.
GetPlatformDNSConfig() string
// GetSyspolicyStringValue returns the current string value for the given system policy.
GetSyspolicyStringValue(key string) (string, error)
// GetSyspolicyBooleanValue returns whether the given system policy is enabled.
GetSyspolicyBooleanValue(key string) (bool, error)
}
// IPNService corresponds to our IPNService in Java.

@ -0,0 +1,47 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package libtailscale
import (
"log"
"tailscale.com/util/syspolicy"
)
// syspolicyHandler 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 syspolicyHandler struct {
a *App
}
func (h syspolicyHandler) ReadString(key string) (string, error) {
if key == "" {
return "", syspolicy.ErrNoSuchKey
}
retVal, err := h.a.appCtx.GetSyspolicyStringValue(key)
if err != nil {
log.Printf("syspolicy: failed to get string value via gomobile: %v", err)
}
return retVal, err
}
func (h syspolicyHandler) ReadBoolean(key string) (bool, error) {
if key == "" {
return false, syspolicy.ErrNoSuchKey
}
retVal, err := h.a.appCtx.GetSyspolicyBooleanValue(key)
if err != nil {
log.Printf("syspolicy: failed to get bool value via gomobile: %v", err)
}
return retVal, err
}
func (h syspolicyHandler) 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
}

@ -18,6 +18,7 @@ import (
"tailscale.com/types/logger"
"tailscale.com/types/logid"
"tailscale.com/util/clientmetric"
"tailscale.com/util/syspolicy"
)
const defaultMTU = 1280 // minimalMTU from wgengine/userspace.go
@ -38,6 +39,7 @@ func newApp(dataDir, directFileRoot string, appCtx AppContext) Application {
a.store = newStateStore(a.appCtx)
interfaces.RegisterInterfaceGetter(a.getInterfaces)
syspolicy.RegisterHandler(syspolicyHandler{a: a})
go func() {
defer func() {
if p := recover(); p != nil {

Loading…
Cancel
Save