// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package sensor /* #cgo LDFLAGS: -landroid #include #include #include "sensors_android.h" */ import "C" import ( "fmt" "runtime" "sync/atomic" "time" "unsafe" ) var nextLooperID int64 // each underlying ALooper should have a unique ID. // initSignal initializes an underlying looper and event queue. type initSignal struct{} // closeSignal destroys the underlying looper and event queue. type closeSignal struct{} // readSignal reads up to len(dst) events and mutates n with // the number of returned events. If error occurs during the read, // it mutates err. type readSignal struct { dst []Event n *int err *error } // enableSignal enables the sensors events on the underlying // event queue for the specified sensor type with the specified // latency criterion. type enableSignal struct { t Type delay time.Duration err *error } // disableSignal disables the events on the underlying event queue // from the sensor specified. type disableSignal struct { t Type } type inOut struct { in interface{} out chan struct{} } // manager is the Android-specific implementation of Manager. type manager struct { m *C.android_SensorManager inout chan inOut } // initialize inits the manager and creates a goroutine to proxy the CGO calls. // All actions related to an ALooper needs to be performed from the same // OS thread. The goroutine proxy locks itself to an OS thread and handles the // CGO traffic on the same thread. func (m *manager) initialize() { m.inout = make(chan inOut) go func() { runtime.LockOSThread() for { v := <-m.inout switch s := (v.in).(type) { case initSignal: id := atomic.AddInt64(&nextLooperID, int64(1)) var mgr C.android_SensorManager C.android_createManager(C.int(id), &mgr) m.m = &mgr case enableSignal: usecsDelay := s.delay.Nanoseconds() * 1000 code := int(C.android_enableSensor(m.m.queue, typeToInt(s.t), C.int32_t(usecsDelay))) if code != 0 { *(s.err) = fmt.Errorf("sensor: no default %v sensor on the device", s.t) } case disableSignal: C.android_disableSensor(m.m.queue, typeToInt(s.t)) case readSignal: n, err := readEvents(m, s.dst) *(s.n) = n *(s.err) = err case closeSignal: C.android_destroyManager(m.m) close(v.out) return // we don't need this goroutine anymore } close(v.out) } }() if m.m == nil { done := make(chan struct{}) m.inout <- inOut{ in: initSignal{}, out: done, } <-done } } func (m *manager) enable(t Type, delay time.Duration) error { var err error done := make(chan struct{}) m.inout <- inOut{ in: enableSignal{t: t, delay: delay, err: &err}, out: done, } <-done return err } func (m *manager) disable(t Type) error { done := make(chan struct{}) m.inout <- inOut{ in: disableSignal{t: t}, out: done, } <-done return nil } func (m *manager) read(e []Event) (n int, err error) { done := make(chan struct{}) m.inout <- inOut{ in: readSignal{dst: e, n: &n, err: &err}, out: done, } <-done return } func readEvents(m *manager, e []Event) (n int, err error) { num := len(e) types := make([]C.int32_t, num) timestamps := make([]C.int64_t, num) vectors := make([]C.float, 3*num) // TODO(jbd): add timeout. n = int(C.android_readQueue( m.m.looperId, m.m.queue, C.int(num), (*C.int32_t)(unsafe.Pointer(&types[0])), (*C.int64_t)(unsafe.Pointer(×tamps[0])), (*C.float)(unsafe.Pointer(&vectors[0]))), ) for i := 0; i < n; i++ { e[i] = Event{ Sensor: intToType[int(types[i])], Timestamp: int64(timestamps[i]), Data: []float64{ float64(vectors[i*3]), float64(vectors[i*3+1]), float64(vectors[i*3+2]), }, } } return } func (m *manager) close() error { done := make(chan struct{}) m.inout <- inOut{ in: closeSignal{}, out: done, } <-done return nil } var intToType = map[int]Type{ C.ASENSOR_TYPE_ACCELEROMETER: Accelerometer, C.ASENSOR_TYPE_GYROSCOPE: Gyroscope, C.ASENSOR_TYPE_MAGNETIC_FIELD: Magnetometer, } func typeToInt(t Type) C.int { for k, v := range intToType { if v == t { return C.int(k) } } return C.int(-1) }