TBR Java: updated wakeup support.

MultiPart: 1/2
Change-Id: Iaa7b01becf102ca93d3bd369b5fcb9f72f99102d
diff --git a/impl/google/jni.go b/impl/google/jni.go
index 8c27dbc..afb29c5 100644
--- a/impl/google/jni.go
+++ b/impl/google/jni.go
@@ -38,7 +38,6 @@
 	if err := jservices.Init(env); err != nil {
 		return err
 	}
-
 	if err := jdiscovery.Init(env); err != nil {
 		return err
 	}
diff --git a/impl/google/namespace/jni.go b/impl/google/namespace/jni.go
index 965376a..d315f27 100644
--- a/impl/google/namespace/jni.go
+++ b/impl/google/namespace/jni.go
@@ -63,7 +63,7 @@
 	if err != nil {
 		return
 	}
-	opts, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	opts, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	if err != nil {
 		return
 	}
@@ -125,7 +125,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	return
 }
 
@@ -151,7 +151,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	return
 }
 
@@ -175,7 +175,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	if err != nil {
 		return
 	}
@@ -204,7 +204,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	if err != nil {
 		return
 	}
@@ -248,7 +248,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	if err != nil {
 		return
 	}
@@ -287,6 +287,27 @@
 	})
 }
 
+//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeSetCachingPolicy
+func Java_io_v_impl_google_namespace_NamespaceImpl_nativeSetCachingPolicy(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong, jDoCaching C.jboolean) C.jboolean {
+	n := *(*namespace.T)(jutil.GoRefValue(jutil.Ref(goRef)))
+	disable := naming.DisableCache(false)
+	if jDoCaching == C.JNI_FALSE {
+		disable = naming.DisableCache(true)
+	}
+	oldCtls := n.CacheCtl(disable)
+	var oldDisable naming.DisableCache
+	for _, ctl := range oldCtls {
+		switch value := ctl.(type) {
+		case naming.DisableCache:
+			oldDisable = value
+		}
+	}
+	if oldDisable {
+		return C.JNI_FALSE
+	}
+	return C.JNI_TRUE
+}
+
 //export Java_io_v_impl_google_namespace_NamespaceImpl_nativeFlushCacheEntry
 func Java_io_v_impl_google_namespace_NamespaceImpl_nativeFlushCacheEntry(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong, jContext C.jobject, jName C.jstring) C.jboolean {
 	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
@@ -319,6 +340,19 @@
 	}
 }
 
+//export Java_io_v_impl_google_namespace_NamespaceImpl_nativeGetRoots
+func Java_io_v_impl_google_namespace_NamespaceImpl_nativeGetRoots(jenv *C.JNIEnv, jNamespaceClass C.jclass, goRef C.jlong) C.jobject {
+	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+	n := *(*namespace.T)(jutil.GoRefValue(jutil.Ref(goRef)))
+	roots := n.Roots()
+	jRoots, err := jutil.JStringList(env, roots)
+	if err != nil {
+		jutil.JThrowV(env, err)
+		return nil
+	}
+	return C.jobject(unsafe.Pointer(jRoots))
+}
+
 func setPermissionsArgs(env jutil.Env, jContext, jPermissions C.jobject, jName, jVersion C.jstring, jOptions C.jobject) (context *context.T, permissions access.Permissions, name, version string, options []naming.NamespaceOpt, err error) {
 	context, _, err = jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext))))
 	if err != nil {
@@ -328,7 +362,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	name = jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jName))))
 	version = jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jVersion))))
 	return
@@ -354,7 +388,7 @@
 	if err != nil {
 		return
 	}
-	options, err = namespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
+	options, err = goNamespaceOptions(env, jutil.Object(uintptr(unsafe.Pointer(jOptions))))
 	if err != nil {
 		return
 	}
diff --git a/impl/google/namespace/util.go b/impl/google/namespace/util.go
index 99a0876..7845613 100644
--- a/impl/google/namespace/util.go
+++ b/impl/google/namespace/util.go
@@ -11,6 +11,7 @@
 	"v.io/v23/naming"
 	"v.io/v23/options"
 	"v.io/v23/security"
+
 	jutil "v.io/x/jni/util"
 )
 
@@ -32,35 +33,29 @@
 	return jNamespace, nil
 }
 
-func javaToGoOptions(env jutil.Env, key string, jValue jutil.Object) (interface{}, error) {
-	switch key {
-	case "io.v.v23.SKIP_SERVER_ENDPOINT_AUTHORIZATION":
-		value, err := jutil.CallBooleanMethod(env, jValue, "booleanValue", []jutil.Sign{})
-		if err != nil {
-			return nil, err
-		}
-		if value {
-			// TODO(ashankar): The Java APIs need to reflect the
-			// change in the Go APIs: any authorization policy can
-			// be providfed as an option?
-			return options.NameResolutionAuthorizer{security.AllowEveryone()}, nil
-		}
-	}
-	// Otherwise we don't know what this option is, ignore it.
-	return nil, jutil.SkipOption
-}
-
-func namespaceOptions(env jutil.Env, jOptions jutil.Object) ([]naming.NamespaceOpt, error) {
-	opts, err := jutil.GoOptions(env, jOptions, javaToGoOptions)
+func goNamespaceOptions(env jutil.Env, jOptions jutil.Object) ([]naming.NamespaceOpt, error) {
+	var opts []naming.NamespaceOpt
+	r, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.naming.REPLACE_MOUNT")
 	if err != nil {
 		return nil, err
 	}
-	var actualOpts []naming.NamespaceOpt
-	for _, opt := range opts {
-		switch opt := opt.(type) {
-		case naming.NamespaceOpt:
-			actualOpts = append(actualOpts, opt)
-		}
+	opts = append(opts, naming.ReplaceMount(r))
+	s, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.naming.SERVES_MOUNT_TABLE")
+	if err != nil {
+		return nil, err
 	}
-	return actualOpts, nil
+	opts = append(opts, naming.ServesMountTable(s))
+	l, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.naming.IS_LEAF")
+	if err != nil {
+		return nil, err
+	}
+	opts = append(opts, naming.IsLeaf(l))
+	e, err := jutil.GetBooleanOption(env, jOptions, "io.v.v23.SKIP_SERVER_ENDPOINT_AUTHORIZATION")
+	if err != nil {
+		return nil, err
+	}
+	if e {
+		opts = append(opts, options.NameResolutionAuthorizer{security.AllowEveryone()})
+	}
+	return opts, nil
 }
diff --git a/impl/google/rpc/invoker.go b/impl/google/rpc/invoker.go
index 17cbae7..a0b4606 100644
--- a/impl/google/rpc/invoker.go
+++ b/impl/google/rpc/invoker.go
@@ -66,9 +66,8 @@
 	}
 	env, freeFunc = jutil.GetEnv()
 	defer freeFunc()
-	jVomTagsLocal := jutil.NewLocalRef(env, jVomTags)
-	jutil.DeleteGlobalRef(env, jVomTags)
-	vomTags, err := jutil.GoByteArrayArray(env, jVomTagsLocal)
+	defer jutil.DeleteGlobalRef(env, jVomTags)
+	vomTags, err := jutil.GoByteArrayArray(env, jVomTags)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -110,9 +109,8 @@
 	}
 	env, freeFunc = jutil.GetEnv()
 	defer freeFunc()
-	jResultLocal := jutil.NewLocalRef(env, jResult)
-	jutil.DeleteGlobalRef(env, jResult)
-	vomResults, err := jutil.GoByteArrayArray(env, jResultLocal)
+	defer jutil.DeleteGlobalRef(env, jResult)
+	vomResults, err := jutil.GoByteArrayArray(env, jResult)
 	if err != nil {
 		return nil, err
 	}
@@ -140,6 +138,7 @@
 	}
 	env, freeFunc = jutil.GetEnv()
 	defer freeFunc()
+	defer jutil.DeleteGlobalRef(env, jInterfaces)
 	interfacesArr, err := jutil.GoObjectArray(env, jInterfaces)
 	if err != nil {
 		return nil, err
@@ -168,6 +167,7 @@
 	}
 	env, freeFunc = jutil.GetEnv()
 	defer freeFunc()
+	defer jutil.DeleteGlobalRef(env, jMethod)
 	var result signature.Method
 	err = jutil.GoVomCopy(env, jMethod, jMethodClass, &result)
 	if err != nil {
diff --git a/impl/google/rpc/jni.go b/impl/google/rpc/jni.go
index 868ed63..a27f405 100644
--- a/impl/google/rpc/jni.go
+++ b/impl/google/rpc/jni.go
@@ -220,6 +220,30 @@
 	return C.jobject(unsafe.Pointer(jStatus))
 }
 
+//export Java_io_v_impl_google_rpc_ServerImpl_nativeAllPublished
+func Java_io_v_impl_google_rpc_ServerImpl_nativeAllPublished(jenv *C.JNIEnv, jServer C.jobject, goRef C.jlong, jContext C.jobject, jCallbackObj C.jobject) {
+	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+	server := *(*rpc.Server)(jutil.GoRefValue(jutil.Ref(goRef)))
+	jCallback := jutil.Object(uintptr(unsafe.Pointer(jCallbackObj)))
+	jutil.DoAsyncCall(env, jCallback, func() (jutil.Object, error) {
+		for {
+			status := server.Status()
+			done := true
+			for _, pub := range status.PublisherStatus {
+				if pub.LastState != pub.DesiredState {
+					done = false
+					break
+				}
+			}
+			if done {
+				break
+			}
+			<-status.Dirty
+		}
+		return jutil.NullObject, nil
+	})
+}
+
 //export Java_io_v_impl_google_rpc_ServerImpl_nativeFinalize
 func Java_io_v_impl_google_rpc_ServerImpl_nativeFinalize(jenv *C.JNIEnv, jServer C.jobject, goRef C.jlong) {
 	jutil.GoDecRef(jutil.Ref(goRef))
diff --git a/impl/google/rt/jni.go b/impl/google/rt/jni.go
index f6450fc..d6732f4 100644
--- a/impl/google/rt/jni.go
+++ b/impl/google/rt/jni.go
@@ -204,8 +204,8 @@
 	return C.jobject(unsafe.Pointer(jPrincipal))
 }
 
-//export Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNamespace
-func Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNamespace(jenv *C.JNIEnv, jRuntime C.jclass, jContext C.jobject, jRoots C.jobjectArray) C.jobject {
+//export Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewNamespace
+func Java_io_v_impl_google_rt_VRuntimeImpl_nativeWithNewNamespace(jenv *C.JNIEnv, jRuntime C.jclass, jContext C.jobject, jRoots C.jobjectArray) C.jobject {
 	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
 	ctx, cancel, err := jcontext.GoContext(env, jutil.Object(uintptr(unsafe.Pointer(jContext))))
 	if err != nil {
diff --git a/jni_android.go b/jni_android.go
index c7273c0..15ac732 100644
--- a/jni_android.go
+++ b/jni_android.go
@@ -7,23 +7,48 @@
 package jni
 
 import (
+	"fmt"
 	"unsafe"
 
+	"v.io/v23/context"
+	"v.io/v23/namespace"
+	"v.io/v23/naming"
+
 	"v.io/x/lib/vlog"
-	_ "v.io/x/ref/runtime/factories/android"
+	"v.io/x/ref/runtime/factories/android"
 
 	jdplugins "v.io/x/jni/impl/google/discovery/plugins"
 	jutil "v.io/x/jni/util"
+	jcontext "v.io/x/jni/v23/context"
 )
 
 // #include "jni.h"
 import "C"
 
+var (
+	// Global reference for io.v.android.v23.V class.
+	jVClass jutil.Class
+)
+
+func Init(env jutil.Env) error {
+	var err error
+	jVClass, err = jutil.JFindClass(env, "io/v/android/v23/V")
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
 //export Java_io_v_android_v23_V_nativeInitGlobalAndroid
-func Java_io_v_android_v23_V_nativeInitGlobalAndroid(jenv *C.JNIEnv, jVClass C.jclass, jOptions C.jobject) {
+func Java_io_v_android_v23_V_nativeInitGlobalAndroid(jenv *C.JNIEnv, _ C.jclass, jOptions C.jobject) {
 	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
 	jOpts := jutil.Object(uintptr(unsafe.Pointer(jOptions)))
 
+	if err := Init(env); err != nil {
+		jutil.JThrowV(env, err)
+		return
+	}
+
 	// Setup logging.
 	_, _, level, vmodule, err := loggingOpts(env, jOpts)
 	if err != nil {
@@ -39,4 +64,29 @@
 		jutil.JThrowV(env, err)
 		return
 	}
+
+	// Setup namespace.
+	android.SetNamespaceFactory(func(ctx *context.T, ns namespace.T, _ ...string) (namespace.T, error) {
+		env, freeFunc := jutil.GetEnv()
+		defer freeFunc()
+		jContext, err := jcontext.JavaContext(env, ctx, nil)
+		if err != nil {
+			return nil, err
+		}
+		contextSign := jutil.ClassSign("io.v.v23.context.VContext")
+		wakeupMountRoot, err := jutil.CallStaticStringMethod(env, jVClass, "getWakeupMountRoot", []jutil.Sign{contextSign}, jContext)
+		if err != nil {
+			return nil, err
+		}
+		if wakeupMountRoot == "" {
+			return ns, nil
+		}
+		if !naming.Rooted(wakeupMountRoot) {
+			return nil, fmt.Errorf("wakeup mount root %s must be ... rooted.", wakeupMountRoot)
+		}
+		return &wakeupNamespace{
+			wakeupMountRoot: wakeupMountRoot,
+			ns:              ns,
+		}, nil
+	})
 }
diff --git a/util/opts.go b/util/opts.go
index 1566fc0..adb1775 100644
--- a/util/opts.go
+++ b/util/opts.go
@@ -7,6 +7,7 @@
 package util
 
 import (
+	"errors"
 	"fmt"
 )
 
@@ -30,6 +31,16 @@
 	return CallObjectMethod(env, jOpts, "get", []Sign{StringSign}, ObjectSign, key)
 }
 
+// SetOption associates the given value with the provided key in the provided options.
+func SetOption(env Env, jOpts Object, key string, jValue Object) error {
+	if jOpts.IsNull() {
+		return errors.New("Couldn't set option on a null options object.")
+	}
+	optionsSign := ClassSign("io.v.v23.Options")
+	_, err := CallObjectMethod(env, jOpts, "set", []Sign{StringSign, ObjectSign}, optionsSign, key, jValue)
+	return err
+}
+
 // GetIntOption returns the integer option with the given key.  It returns 0 if the option
 // doesn't exist.
 func GetIntOption(env Env, jOpts Object, key string) (int, error) {
@@ -46,6 +57,16 @@
 	return CallIntMethod(env, jVal, "intValue", nil)
 }
 
+// SetIntOption associates the given integer value with the provided key
+// in the provided options.
+func SetIntOption(env Env, jOpts Object, key string, value int) error {
+	jValue, err := NewObject(env, jIntegerClass, []Sign{IntSign}, value)
+	if err != nil {
+		return err
+	}
+	return SetOption(env, jOpts, key, jValue)
+}
+
 // GetBooleanOption returns the boolean option with the given key.  It returns 'false' if the option
 // doesn't exist.
 func GetBooleanOption(env Env, jOpts Object, key string) (bool, error) {
@@ -62,6 +83,16 @@
 	return CallBooleanMethod(env, jVal, "booleanValue", nil)
 }
 
+// SetBooleanOption associates the given boolean value with the provided key
+// in the provided options.
+func SetBooleanOption(env Env, jOpts Object, key string, value bool) error {
+	jValue, err := NewObject(env, jBooleanClass, []Sign{BoolSign}, value)
+	if err != nil {
+		return err
+	}
+	return SetOption(env, jOpts, key, jValue)
+}
+
 // StringOption returns the string option with the given key.  It returns an empty string if the
 // option doesn't exist.
 func GetStringOption(env Env, jOpts Object, key string) (string, error) {
@@ -77,3 +108,10 @@
 	}
 	return GoString(env, jVal), nil
 }
+
+// SetStringOption associates the given string value with the provided key
+// in the provided options.
+func SetStringOption(env Env, jOpts Object, key string, value string) error {
+	jValue := JString(env, value)
+	return SetOption(env, jOpts, key, jValue)
+}
diff --git a/util/util.go b/util/util.go
index 71b51ec..70ae2f4 100644
--- a/util/util.go
+++ b/util/util.go
@@ -398,6 +398,16 @@
 	return ret, nil
 }
 
+// JStringList converts the provided slice of Go strings into a Java
+// List<String>.
+func JStringList(env Env, strs []string) (Object, error) {
+	strArr := make([]Object, len(strs))
+	for i, str := range strs {
+		strArr[i] = JString(env, str)
+	}
+	return JObjectList(env, strArr, jStringClass)
+}
+
 // JStringArray converts the provided slice of Go strings into a Java array of
 // strings.
 func JStringArray(env Env, strs []string) (Object, error) {
diff --git a/wakeup_android.go b/wakeup_android.go
new file mode 100644
index 0000000..6e7f9af
--- /dev/null
+++ b/wakeup_android.go
@@ -0,0 +1,131 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build android
+
+package jni
+
+import (
+	"fmt"
+	"time"
+
+	"v.io/v23/context"
+	"v.io/v23/namespace"
+	"v.io/v23/naming"
+	"v.io/v23/security/access"
+)
+
+// wakeupNamespace is a namespace used by the persistent Android services
+// to mount themselves in a way that they can be woken up whenever a
+// client resolves their names.
+//
+// This namespace consists of two sub-parts:
+//    1) The original namespace (OrigNS),
+//    2) The mount root used for waking up the service (WakeMTRoot)
+//
+// Let's say the service wishes to mount itself under a relative name
+// 'a/b'. (Absolute names are disallowed.)  The service first mounts its
+// endpoints under 'WakeMTRoot/server/a/b' and then mounts name
+// 'WakeMTRoot/client/a/b' under 'OrigNS/a/b'.  When the client resolves
+// 'OrigNS/a/b', the name resolution will trigger resolution of
+// 'WakeMTRoot/client/a/b', which in turn triggers the process of waking
+// up the server.
+//
+// Note that we prefix mounted names by 'client' and 'server' above.
+// This is needed in order to distinguish the cases of a server mounting itself
+// into the mounttable (i.e., no wakeup needed) from the case of a client
+// resolving a name (i.e., wakeup needed).
+type wakeupNamespace struct {
+	wakeupMountRoot string
+	ns              namespace.T
+}
+
+func (w *wakeupNamespace) Mount(ctx *context.T, name, server string, ttl time.Duration, opts ...naming.NamespaceOpt) error {
+	if naming.Rooted(name) {
+		return fmt.Errorf("Mount(%q): rooted names not allowed in wakeup namespace", name)
+	}
+	mountName := naming.Join(w.wakeupMountRoot, "server", name)
+	if err := w.ns.Mount(ctx, mountName, server, ttl, opts...); err != nil {
+		return err
+	}
+	// We set an infinite TTL at the original mount location as it allows us to wake up a
+	// server even after its mount entries have expired.  (It's fine if the wakeup MT entries
+	// do expire.)  Since entries are mounted forever, we explicitly have to clean up previous
+	// entries.
+	w.ns.Delete(ctx, name, false)
+	wakeupName := naming.Join(w.wakeupMountRoot, "client", name)
+	var zeroTTL time.Duration
+	return w.ns.Mount(ctx, name, wakeupName, zeroTTL, append(opts, naming.ServesMountTable(true))...)
+}
+
+func (w *wakeupNamespace) Unmount(ctx *context.T, name, server string, opts ...naming.NamespaceOpt) error {
+	if naming.Rooted(name) {
+		return fmt.Errorf("Unmount(%q): rooted names not allowed in wakeup namespace", name)
+	}
+	mountName := naming.Join(w.wakeupMountRoot, "server", name)
+	return w.ns.Unmount(ctx, mountName, server, opts...)
+	// We never unmount at the original location as it allows us to wake up a sleeping server.
+}
+
+func (w *wakeupNamespace) Delete(ctx *context.T, name string, deleteSubtree bool, opts ...naming.NamespaceOpt) error {
+	if naming.Rooted(name) {
+		return fmt.Errorf("Delete(%q): rooted names not allowed in wakeup namespace", name)
+	}
+	mountName := naming.Join(w.wakeupMountRoot, "server", name)
+	if err := w.ns.Delete(ctx, mountName, deleteSubtree, opts...); err != nil {
+		return err
+	}
+	return w.ns.Delete(ctx, name, deleteSubtree, opts...)
+}
+
+func (w *wakeupNamespace) Resolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (entry *naming.MountEntry, err error) {
+	return w.ns.Resolve(ctx, name, opts...)
+}
+
+func (w *wakeupNamespace) ShallowResolve(ctx *context.T, name string, opts ...naming.NamespaceOpt) (entry *naming.MountEntry, err error) {
+	return w.ns.ShallowResolve(ctx, name, opts...)
+}
+
+func (w *wakeupNamespace) ResolveToMountTable(ctx *context.T, name string, opts ...naming.NamespaceOpt) (entry *naming.MountEntry, err error) {
+	return w.ns.ResolveToMountTable(ctx, name, opts...)
+}
+
+func (w *wakeupNamespace) FlushCacheEntry(ctx *context.T, name string) bool {
+	if naming.Rooted(name) {
+		return false
+	}
+	mountName := naming.Join(w.wakeupMountRoot, "server", name)
+	return w.ns.FlushCacheEntry(ctx, mountName) || w.ns.FlushCacheEntry(ctx, name)
+}
+
+func (w *wakeupNamespace) CacheCtl(ctls ...naming.CacheCtl) []naming.CacheCtl {
+	return w.ns.CacheCtl(ctls...)
+}
+
+func (w *wakeupNamespace) Glob(ctx *context.T, pattern string, opts ...naming.NamespaceOpt) (<-chan naming.GlobReply, error) {
+	return w.ns.Glob(ctx, pattern, opts...)
+}
+
+func (w *wakeupNamespace) SetRoots(roots ...string) error {
+	return w.ns.SetRoots(roots...)
+}
+
+func (w *wakeupNamespace) Roots() []string {
+	return w.ns.Roots()
+}
+
+func (w *wakeupNamespace) SetPermissions(ctx *context.T, name string, perms access.Permissions, version string, opts ...naming.NamespaceOpt) error {
+	if naming.Rooted(name) {
+		return fmt.Errorf("SetPermissions(%q): rooted names not allowed in wakeup namespace", name)
+	}
+	mountName := naming.Join(w.wakeupMountRoot, "server", name)
+	if err := w.ns.SetPermissions(ctx, mountName, perms, version, opts...); err != nil {
+		return err
+	}
+	return w.ns.SetPermissions(ctx, name, perms, version, opts...)
+}
+
+func (w *wakeupNamespace) GetPermissions(ctx *context.T, name string, opts ...naming.NamespaceOpt) (perms access.Permissions, version string, err error) {
+	return w.ns.GetPermissions(ctx, name, opts...)
+}