2015-03-27 21:53:09 +00:00
|
|
|
// 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.
|
|
|
|
|
2015-08-12 17:16:13 +00:00
|
|
|
// +build android
|
|
|
|
|
2015-03-27 21:53:09 +00:00
|
|
|
package sensor
|
|
|
|
|
2015-03-30 18:53:15 +00:00
|
|
|
/*
|
|
|
|
#cgo LDFLAGS: -landroid
|
2015-03-27 21:53:09 +00:00
|
|
|
|
2015-03-30 18:53:15 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <android/sensor.h>
|
|
|
|
|
2015-08-12 17:16:13 +00:00
|
|
|
#include "android.h"
|
2015-03-30 18:53:15 +00:00
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"runtime"
|
2015-08-28 02:55:09 +00:00
|
|
|
"sync"
|
2015-03-30 18:53:15 +00:00
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
2015-08-28 02:55:09 +00:00
|
|
|
var (
|
|
|
|
collectingMu sync.Mutex // guards collecting
|
|
|
|
|
|
|
|
// collecting is true if sensor event collecting background
|
|
|
|
// job has already started.
|
|
|
|
collecting bool
|
|
|
|
)
|
|
|
|
|
2015-03-30 18:53:15 +00:00
|
|
|
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.
|
2015-03-27 21:53:09 +00:00
|
|
|
type manager struct {
|
2015-08-12 21:51:42 +00:00
|
|
|
m *C.GoAndroid_SensorManager
|
2015-04-07 19:56:44 +00:00
|
|
|
inout chan inOut
|
2015-03-30 18:53:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2015-04-03 18:44:00 +00:00
|
|
|
switch s := v.in.(type) {
|
2015-03-30 18:53:15 +00:00
|
|
|
case initSignal:
|
2015-08-19 10:29:28 +00:00
|
|
|
id := atomic.AddInt64(&nextLooperID, 1)
|
2015-08-12 21:51:42 +00:00
|
|
|
var mgr C.GoAndroid_SensorManager
|
|
|
|
C.GoAndroid_createManager(C.int(id), &mgr)
|
2015-03-30 18:53:15 +00:00
|
|
|
m.m = &mgr
|
|
|
|
case enableSignal:
|
2015-08-19 10:29:28 +00:00
|
|
|
usecsDelay := s.delay.Nanoseconds() / 1000
|
2015-08-12 21:51:42 +00:00
|
|
|
code := int(C.GoAndroid_enableSensor(m.m.queue, typeToInt(s.t), C.int32_t(usecsDelay)))
|
2015-03-30 18:53:15 +00:00
|
|
|
if code != 0 {
|
2015-04-03 18:44:00 +00:00
|
|
|
*s.err = fmt.Errorf("sensor: no default %v sensor on the device", s.t)
|
2015-03-30 18:53:15 +00:00
|
|
|
}
|
|
|
|
case disableSignal:
|
2015-08-12 21:51:42 +00:00
|
|
|
C.GoAndroid_disableSensor(m.m.queue, typeToInt(s.t))
|
2015-03-30 18:53:15 +00:00
|
|
|
case readSignal:
|
|
|
|
n, err := readEvents(m, s.dst)
|
2015-04-03 18:44:00 +00:00
|
|
|
*s.n = n
|
|
|
|
*s.err = err
|
2015-03-30 18:53:15 +00:00
|
|
|
case closeSignal:
|
2015-08-12 21:51:42 +00:00
|
|
|
C.GoAndroid_destroyManager(m.m)
|
2015-03-30 18:53:15 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-28 02:55:09 +00:00
|
|
|
func (m *manager) enable(s Sender, t Type, delay time.Duration) error {
|
|
|
|
m.startCollecting(s)
|
|
|
|
|
2015-03-30 18:53:15 +00:00
|
|
|
var err error
|
|
|
|
done := make(chan struct{})
|
|
|
|
m.inout <- inOut{
|
|
|
|
in: enableSignal{t: t, delay: delay, err: &err},
|
|
|
|
out: done,
|
|
|
|
}
|
|
|
|
<-done
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-08-28 02:55:09 +00:00
|
|
|
func (m *manager) startCollecting(s Sender) {
|
|
|
|
collectingMu.Lock()
|
|
|
|
defer collectingMu.Unlock()
|
|
|
|
|
|
|
|
if collecting {
|
|
|
|
// already collecting.
|
|
|
|
return
|
2015-03-30 18:53:15 +00:00
|
|
|
}
|
2015-08-28 02:55:09 +00:00
|
|
|
collecting = true
|
|
|
|
|
|
|
|
// TODO(jbd): Disable the goroutine if all sensors are disabled?
|
|
|
|
// Read will block until there are new events, a goroutine will be
|
|
|
|
// parked forever until a sensor is enabled. There must be no
|
|
|
|
// performance cost other than allocating blocking an OS thread
|
|
|
|
// forever to keep the goroutine running.
|
|
|
|
go func() {
|
|
|
|
ev := make([]Event, 8)
|
|
|
|
var n int
|
|
|
|
var err error // TODO(jbd): How to handle the errors? error channel?
|
|
|
|
for {
|
|
|
|
// TODO(jbd): readSignal is not required anymore. Use the proxying
|
|
|
|
// goroutine to continously poll the queue and send the events to s.
|
|
|
|
done := make(chan struct{})
|
|
|
|
m.inout <- inOut{
|
|
|
|
in: readSignal{dst: ev, n: &n, err: &err},
|
|
|
|
out: done,
|
|
|
|
}
|
|
|
|
<-done
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
s.Send(ev[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2015-03-27 21:53:09 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 02:55:09 +00:00
|
|
|
func (m *manager) disable(t Type) error {
|
2015-03-30 18:53:15 +00:00
|
|
|
done := make(chan struct{})
|
|
|
|
m.inout <- inOut{
|
2015-08-28 02:55:09 +00:00
|
|
|
in: disableSignal{t: t},
|
2015-03-30 18:53:15 +00:00
|
|
|
out: done,
|
|
|
|
}
|
|
|
|
<-done
|
2015-08-28 02:55:09 +00:00
|
|
|
return nil
|
2015-03-30 18:53:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2015-04-07 19:56:44 +00:00
|
|
|
|
2015-08-12 21:51:42 +00:00
|
|
|
n = int(C.GoAndroid_readQueue(
|
2015-04-07 19:56:44 +00:00
|
|
|
m.m.looperId, m.m.queue,
|
2015-03-30 18:53:15 +00:00
|
|
|
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
|
2015-03-27 21:53:09 +00:00
|
|
|
}
|
|
|
|
|
2015-08-28 02:55:09 +00:00
|
|
|
// TODO(jbd): Remove close?
|
2015-03-30 18:53:15 +00:00
|
|
|
func (m *manager) close() error {
|
|
|
|
done := make(chan struct{})
|
|
|
|
m.inout <- inOut{
|
|
|
|
in: closeSignal{},
|
|
|
|
out: done,
|
|
|
|
}
|
|
|
|
<-done
|
|
|
|
return nil
|
2015-03-27 21:53:09 +00:00
|
|
|
}
|
|
|
|
|
2015-03-30 18:53:15 +00:00
|
|
|
var intToType = map[int]Type{
|
|
|
|
C.ASENSOR_TYPE_ACCELEROMETER: Accelerometer,
|
|
|
|
C.ASENSOR_TYPE_GYROSCOPE: Gyroscope,
|
|
|
|
C.ASENSOR_TYPE_MAGNETIC_FIELD: Magnetometer,
|
2015-03-27 21:53:09 +00:00
|
|
|
}
|
|
|
|
|
2015-03-30 18:53:15 +00:00
|
|
|
func typeToInt(t Type) C.int {
|
|
|
|
for k, v := range intToType {
|
|
|
|
if v == t {
|
|
|
|
return C.int(k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return C.int(-1)
|
2015-03-27 21:53:09 +00:00
|
|
|
}
|