1- // Exposing NDK sensors to Java/Kotlin via gomobile bind .
1+ // gomobile bind sensor bridge: round-trip NDK handles through int64 .
22//
3- // gomobile bind generates Java/Kotlin bindings for exported Go functions.
4- // It supports signed integers, floats, strings, booleans, []byte, errors,
5- // interfaces, and structs — but NOT unsafe.Pointer or uintptr.
3+ // Demonstrates the pattern for passing NDK sensor handles across the
4+ // Go/Java boundary via gomobile bind. gomobile bind does not support
5+ // unsafe.Pointer or uintptr, so native handles are transported as int64
6+ // (Java long).
67//
7- // To pass NDK handles across the Go/Java boundary, use int64 (Java long)
8- // as the transport type and convert via uintptr inside Go:
8+ // This example obtains a real ASensorManager, converts it to int64
9+ // (simulating the gomobile boundary), converts back, and uses the
10+ // restored handle to query sensors — proving the round-trip is lossless.
911//
10- // func NewSensorBridge(managerHandle int64) *SensorBridge {
11- // mgr := sensor.NewManagerFromUintPtr(uintptr(managerHandle))
12- // return &SensorBridge{mgr: mgr}
13- // }
14- //
15- // The Java side obtains native handles as long values (e.g. from JNI)
16- // and passes them to Go:
17- //
18- // // Java
19- // long sensorManagerPtr = nativeGetSensorManager();
20- // SensorBridge bridge = Sensorbridge.newSensorBridge(sensorManagerPtr);
21- // String name = bridge.defaultAccelerometerName();
22- //
23- // # Go library package (bindable via gomobile bind)
24- //
25- // The pattern for a gomobile-bindable Go package:
12+ // A gomobile-bindable Go package would expose these operations as
13+ // exported methods on a struct:
2614//
27- // // Package sensorbridge exposes NDK sensor access to Java/Kotlin.
28- // // Build with: gomobile bind -target=android ./sensorbridge
29- // package sensorbbridge
15+ // package sensorbridge
3016//
31- // import "github.com/AndroidGoLab/ndk/ sensor"
17+ // type Bridge struct{ mgr * sensor.Manager }
3218//
33- // // SensorBridge wraps a sensor.Manager for cross-language use.
34- // // Exported struct fields and methods with supported types are
35- // // automatically exposed to Java/Kotlin by gomobile bind.
36- // type SensorBridge struct {
37- // mgr *sensor.Manager // unexported: invisible to Java
19+ // func NewBridge(h int64) *Bridge {
20+ // return &Bridge{mgr: sensor.NewManagerFromUintPtr(uintptr(h))}
3821// }
3922//
40- // // NewSensorBridge creates a bridge from a raw ASensorManager handle.
41- // // The handle is passed as int64 because gomobile bind does not
42- // // support unsafe.Pointer or uintptr.
43- // func NewSensorBridge(managerHandle int64) *SensorBridge {
44- // mgr := sensor.NewManagerFromUintPtr(uintptr(managerHandle))
45- // return &SensorBridge{mgr: mgr}
23+ // func (b *Bridge) AccelerometerName() string {
24+ // return b.mgr.DefaultSensor(sensor.Accelerometer).Name()
4625// }
4726//
48- // // DefaultAccelerometerName returns the name of the default accelerometer.
49- // func (b *SensorBridge) DefaultAccelerometerName() string {
50- // s := b.mgr.DefaultSensor(sensor.Accelerometer)
51- // return s.Name()
52- // }
53- //
54- // // Handle returns the underlying ASensorManager handle for passing
55- // // back to Java/JNI code.
56- // func (b *SensorBridge) Handle() int64 {
27+ // func (b *Bridge) Handle() int64 {
5728// return int64(b.mgr.UintPtr())
5829// }
5930//
60- // # Java usage
61- //
62- // import sensorbbridge.SensorBridge;
63- //
64- // // Obtain the native ASensorManager* from JNI or the NDK.
65- // long ptr = nativeGetSensorManager();
31+ // Java side:
6632//
67- // // Create the Go bridge, passing the handle as a long.
68- // SensorBridge bridge = Sensorbbridge.newSensorBridge(ptr);
69- //
70- // // Call Go methods — gomobile generates Java proxy methods.
71- // String name = bridge.defaultAccelerometerName();
72- // Log.d("Sensor", "Accelerometer: " + name);
73- //
74- // // Retrieve the handle back (e.g. to pass to another native call).
75- // long handle = bridge.handle();
76- //
77- // # Key rules
78- //
79- // - Use int64 (Java long) to transport native pointers across the boundary.
80- // - Convert int64 <-> uintptr inside Go; never expose uintptr to gomobile.
81- // - Keep NDK handle fields unexported — gomobile cannot serialize them.
82- // - Exported methods must use only gomobile-supported types.
83- // - Close/release NDK resources from Go, not from Java.
33+ // long ptr = nativeGetSensorManager(); // from JNI
34+ // Bridge b = Sensorbridge.newBridge(ptr);
35+ // String name = b.accelerometerName();
8436//
8537// This program must run on an Android device.
8638package main
8739
8840import (
89- "fmt "
41+ "log "
9042
9143 "github.com/AndroidGoLab/ndk/sensor"
9244)
9345
9446func main () {
95- fmt .Println ("=== gomobile bind: sensor bridge ===" )
96- fmt .Println ()
47+ log .Println ("=== gomobile sensor bridge ===" )
9748
98- // Demonstrate the int64 <-> uintptr <-> NDK handle conversion chain .
99- //
100- // In a real app, the int64 comes from Java (JNI native handle).
101- // Here we show the Go-side conversion pattern.
49+ // Step 1: Obtain a real sensor manager from the NDK .
50+ mgr := sensor . ASensorManager_getInstanceForPackage ( "com.example.gomobile" )
51+ original := mgr . UintPtr ()
52+ log . Printf ( "sensor manager obtained: 0x%x" , original )
10253
103- fmt .Println ("Pattern: Java long -> Go int64 -> uintptr -> NDK handle" )
104- fmt .Println ()
54+ // Step 2: Simulate the gomobile boundary — convert to int64.
55+ // In a real app, this int64 crosses the Go/Java boundary.
56+ javaHandle := int64 (original )
57+ log .Printf ("converted to int64 (Java long): %d" , javaHandle )
10558
106- // Step 1: Receive a handle from Java as int64.
107- fmt .Println ("Step 1: Java passes native handle as long (int64)" )
108- fmt .Println (" Java: long ptr = nativeGetSensorManager();" )
109- fmt .Println (" Java: SensorBridge bridge = Sensorbbridge.newSensorBridge(ptr);" )
110- fmt .Println ()
59+ // Step 3: On the "Java side", pass the int64 back to Go.
60+ // Reconstruct the sensor.Manager from int64.
61+ restored := sensor .NewManagerFromUintPtr (uintptr (javaHandle ))
62+ log .Printf ("restored from int64: 0x%x" , restored .UintPtr ())
11163
112- // Step 2: Convert int64 -> uintptr -> NDK type in Go.
113- fmt .Println ("Step 2: Go converts int64 -> uintptr -> sensor.Manager" )
114- fmt .Println (" Go: mgr := sensor.NewManagerFromUintPtr(uintptr(handle))" )
115- fmt .Println ()
116-
117- // Step 3: Use the NDK type normally.
118- fmt .Println ("Step 3: Use NDK types normally in Go" )
119- fmt .Println (" Go: s := mgr.DefaultSensor(sensor.Accelerometer)" )
120- fmt .Println (" Go: name := s.Name()" )
121- fmt .Println ()
122-
123- // Step 4: Return results to Java using supported types.
124- fmt .Println ("Step 4: Return results as gomobile-supported types (string, int64, error)" )
125- fmt .Println (" Go: return s.Name() // string -> Java String" )
126- fmt .Println (" Go: return int64(mgr.UintPtr()) // handle -> Java long" )
127- fmt .Println ()
64+ // Step 4: Verify the round-trip — pointers must match.
65+ if restored .UintPtr () != original {
66+ log .Fatalf ("FAIL: pointer mismatch: original=0x%x restored=0x%x" , original , restored .UintPtr ())
67+ }
68+ log .Println ("round-trip OK: pointers match" )
12869
129- // Show the available sensor type constants that a bridge might expose .
130- types := []struct {
131- name string
132- value sensor.Type
70+ // Step 5: Use the restored handle to query real sensors .
71+ sensorTypes := []struct {
72+ name string
73+ typeCode sensor.Type
13374 }{
13475 {"Accelerometer" , sensor .Accelerometer },
13576 {"Gyroscope" , sensor .Gyroscope },
@@ -138,25 +79,14 @@ func main() {
13879 {"Proximity" , sensor .Proximity },
13980 }
14081
141- fmt .Println ("Sensor type constants (use int32 for gomobile):" )
142- for _ , t := range types {
143- fmt .Printf (" %-25s = %d\n " , t .name , int32 (t .value ))
82+ for _ , st := range sensorTypes {
83+ s := restored .DefaultSensor (st .typeCode )
84+ if s .UintPtr () == 0 {
85+ log .Printf (" %-15s not available" , st .name )
86+ continue
87+ }
88+ log .Printf (" %-15s name=%q vendor=%q" , st .name , s .Name (), s .Vendor ())
14489 }
145- fmt .Println ()
146-
147- // gomobile bind type mapping summary.
148- fmt .Println ("gomobile bind type mapping:" )
149- fmt .Println (" Go int64 <-> Java long (native handle transport)" )
150- fmt .Println (" Go string <-> Java String" )
151- fmt .Println (" Go int32 <-> Java int (sensor type constants)" )
152- fmt .Println (" Go float32 <-> Java float (sensor values)" )
153- fmt .Println (" Go float64 <-> Java double" )
154- fmt .Println (" Go bool <-> Java boolean" )
155- fmt .Println (" Go []byte <-> Java byte[]" )
156- fmt .Println (" Go error <-> Java Exception" )
157- fmt .Println (" Go struct <-> Java class (exported methods only)" )
158- fmt .Println (" Go interface <-> Java interface" )
159- fmt .Println ()
16090
161- fmt .Println ("sensor-bridge example complete " )
91+ log .Println ("sensor-bridge done " )
16292}
0 commit comments