android: add All() to state store implementation (#673)

Android has its own SharedPreferences-backed implementation of ipn.StateStore. Due to https://github.com/golang/go/issues/13445, we bundle the key list into a single primitive and unpack it in Go in our All() implementation.
This also adds a compile-time check to prevent drift the interface.

Updates tailscale/tailscale#15830

Signed-off-by: kari-ts <kari@tailscale.com>
pull/674/head
kari-ts 5 months ago committed by GitHub
parent f392619036
commit 460736a151
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -248,6 +248,16 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
return getEncryptedPrefs().getString(prefKey, null)
}
override fun getStateStoreKeysJSON(): String {
val prefix = "statestore-"
val keys = getEncryptedPrefs()
.getAll()
.keys
.filter { it.startsWith(prefix) }
.map { it.removePrefix(prefix) }
return org.json.JSONArray(keys).toString()
}
@Throws(IOException::class, GeneralSecurityException::class)
fun getEncryptedPrefs(): SharedPreferences {
val key = MasterKey.Builder(this).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()

@ -29,6 +29,10 @@ type AppContext interface {
// at the given key, or returns empty string if unset.
DecryptFromPref(key string) (string, error)
// GetStateStoreKeysJson retrieves all keys stored in the encrypted SharedPreferences,
// strips off the "statestore-" prefix, and returns them as a JSON array.
GetStateStoreKeysJSON() string
// GetOSVersion gets the Android version.
GetOSVersion() (string, error)

@ -5,6 +5,8 @@ package libtailscale
import (
"encoding/base64"
"encoding/json"
"iter"
"tailscale.com/ipn"
)
@ -23,6 +25,28 @@ func newStateStore(appCtx AppContext) *stateStore {
}
}
func (s *stateStore) All() iter.Seq2[ipn.StateKey, []byte] {
rawJSON := s.appCtx.GetStateStoreKeysJSON()
var keys []string
if err := json.Unmarshal([]byte(rawJSON), &keys); err != nil {
return func(yield func(ipn.StateKey, []byte) bool) {}
}
return func(yield func(ipn.StateKey, []byte) bool) {
for _, k := range keys {
blob, err := s.ReadState(ipn.StateKey(k))
if err != nil {
continue
}
if !yield(ipn.StateKey(k), blob) {
return
}
}
}
}
// compile-time assertion that store must implement ipn.StateStore to give immediate feedback on interface drift.
var _ ipn.StateStore = (*stateStore)(nil)
func prefKeyFor(id ipn.StateKey) string {
return "statestore-" + string(id)
}

Loading…
Cancel
Save