Skip to content

Commit 460736a

Browse files
authored
android: add All() to state store implementation (#673)
Android has its own SharedPreferences-backed implementation of ipn.StateStore. Due to golang/go#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 <[email protected]>
1 parent f392619 commit 460736a

File tree

3 files changed

+38
-0
lines changed

3 files changed

+38
-0
lines changed

android/src/main/java/com/tailscale/ipn/App.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,16 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
248248
return getEncryptedPrefs().getString(prefKey, null)
249249
}
250250

251+
override fun getStateStoreKeysJSON(): String {
252+
val prefix = "statestore-"
253+
val keys = getEncryptedPrefs()
254+
.getAll()
255+
.keys
256+
.filter { it.startsWith(prefix) }
257+
.map { it.removePrefix(prefix) }
258+
return org.json.JSONArray(keys).toString()
259+
}
260+
251261
@Throws(IOException::class, GeneralSecurityException::class)
252262
fun getEncryptedPrefs(): SharedPreferences {
253263
val key = MasterKey.Builder(this).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()

libtailscale/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type AppContext interface {
2929
// at the given key, or returns empty string if unset.
3030
DecryptFromPref(key string) (string, error)
3131

32+
// GetStateStoreKeysJson retrieves all keys stored in the encrypted SharedPreferences,
33+
// strips off the "statestore-" prefix, and returns them as a JSON array.
34+
GetStateStoreKeysJSON() string
35+
3236
// GetOSVersion gets the Android version.
3337
GetOSVersion() (string, error)
3438

libtailscale/store.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ package libtailscale
55

66
import (
77
"encoding/base64"
8+
"encoding/json"
9+
"iter"
810

911
"tailscale.com/ipn"
1012
)
@@ -23,6 +25,28 @@ func newStateStore(appCtx AppContext) *stateStore {
2325
}
2426
}
2527

28+
func (s *stateStore) All() iter.Seq2[ipn.StateKey, []byte] {
29+
rawJSON := s.appCtx.GetStateStoreKeysJSON()
30+
var keys []string
31+
if err := json.Unmarshal([]byte(rawJSON), &keys); err != nil {
32+
return func(yield func(ipn.StateKey, []byte) bool) {}
33+
}
34+
return func(yield func(ipn.StateKey, []byte) bool) {
35+
for _, k := range keys {
36+
blob, err := s.ReadState(ipn.StateKey(k))
37+
if err != nil {
38+
continue
39+
}
40+
if !yield(ipn.StateKey(k), blob) {
41+
return
42+
}
43+
}
44+
}
45+
}
46+
47+
// compile-time assertion that store must implement ipn.StateStore to give immediate feedback on interface drift.
48+
var _ ipn.StateStore = (*stateStore)(nil)
49+
2650
func prefKeyFor(id ipn.StateKey) string {
2751
return "statestore-" + string(id)
2852
}

0 commit comments

Comments
 (0)