I am running a sensor and location service, the data is passed to the TraceManager file which where it is dealt with and passed to the TraceCWrapper to be mapped to a shared C library .so , It seems the sensor and location data is fine and received in TraceManager, it then is passed into TraceCWrapper, however the app crashes after a few seconds, and the only error line i get is:
A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x8 in tid 29938 (AsyncTask #1), pid 29870 (pp.traceandroid)
public class TraceManager extends AppCompatActivity {
private String TAG = "TraceManager";
private int phoneAngle = 0;
private double initialStepCalibrationOffset;
private int initialPointingAngleDeg = 0;
private int initialAlignmentMode = 0;
private int startingFloorID = 0;
private LatLng startingLatLong;
private double startingAccuracy = 1.0;
private Context context;
private boolean isMagConsistentAtInit = false;
private boolean isMagValid = true;
private Timer callBackTimer;
private String[] contentsStatic;
private String[] contentsDynamic;
private boolean isRunning = false;
private TraceCWrapper traceCWrapper = new TraceCWrapper();
Handler callbackHandler = new Handler();
Runnable callbackRunnable;
//internal use only
private boolean _traceCDontActuallyUse;
// The interval, in seconds, for providing trace updates.
public ObservableDouble updateCallbackInterval = new ObservableDouble(0){
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
if(isRunning){
stopCallbackTimer();
startCallbackTimer();
}
super.addOnPropertyChangedCallback(callback);
}
};
private double updateCallBackIntervalValue = updateCallbackInterval.get();
/// A Boolean value
public ObservableBoolean allowsBackgroundExecution = new ObservableBoolean(false){
@Override
public void addOnPropertyChangedCallback(@NonNull OnPropertyChangedCallback callback) {
if(isRunning){
stopUpdatingTrace();
startUpdatingTrace();
}
super.addOnPropertyChangedCallback(callback);
}
};
private boolean allowsBackgroundExecutionValue = allowsBackgroundExecution.get();
public TraceManager(Context context){
this.context=context;
}
public TraceManager(){
}
public void initialiseTrace(String[] mapFloors,
String[] initialDynamicMaps,
int phoneRelativeToBodyDegree, //this comes from onboarding?
double updateCallBackIntervalValue,
boolean allowsBackgroundExecutionValue,
double initialStepCalibrationOffset, //standard
String[] iBeaconUUIDs,
int startingFloorID,
LatLng startingLatLong, //this is form the starting node
double startingAccuracy, //
boolean _traceCDontActuallyUse,
int phoneOrientation,
int phoneOrientationUse,
boolean magntometerValid
){
this.contentsStatic = mapFloors;
this.contentsDynamic = initialDynamicMaps;
this.phoneAngle = phoneRelativeToBodyDegree;
this.initialStepCalibrationOffset = initialStepCalibrationOffset;
this.updateCallbackInterval = updateCallbackInterval;
this.allowsBackgroundExecution = allowsBackgroundExecution;
this.isMagValid = magntometerValid;
if(!(iBeaconUUIDs.length <=0)){
LocationProvider.arrayOfUUIDsToDetect = iBeaconUUIDs;
}else{
Log.i(TAG, "TraceManager.init: ignoring ibeaconUIDs, because it is empty. Default used");
};
this.startingFloorID = startingFloorID;
this.startingLatLong = startingLatLong;
this.startingAccuracy = startingAccuracy;
this.initialPointingAngleDeg = phoneOrientation;
this.initialAlignmentMode = phoneOrientationUse;
//internal use only
this._traceCDontActuallyUse = _traceCDontActuallyUse;
}
//Functions
/// Broadcast Receiver to get readings from MotionProvider/service
public void startUpdatingSensors(){
//Start sensor service
Intent startService = new Intent(TraceManager.this, SensorService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(startService);
} else {
startService(startService);
}
}
/// Starts the generation of trace updates.
public void startUpdatingTrace(){
//Start Sensors
//startUpdatingSensors();
//register for sensorBroadcast
BroadcastReceiver sensorReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "imu Received");
TCIMUEvent tcimuEvent = (TCIMUEvent) intent.getSerializableExtra("imu");
traceCWrapper.provideDeviceMotion(tcimuEvent, 1, 90, RotationMode.PortraitYUp);
}
};
LocalBroadcastManager.getInstance(context).registerReceiver(
sensorReceiver, new IntentFilter("imuCreated")
);
//register for locationBroadcast
//register for sensorBroadcast
BroadcastReceiver locationReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "location Received");
TCLocationEvent tcLocationEvent = (TCLocationEvent) intent.getSerializableExtra("locationCreated");
Log.d(TAG, "Inlocation reciever");
traceCWrapper.provideLocation(tcLocationEvent);
}
};
LocalBroadcastManager.getInstance(context).registerReceiver(
locationReceiver, new IntentFilter("locationCreated")
);
Log.d(TAG, "inside updating trace");
//Start CallbackTimer
startCallbackTimer();
}
private void CallbackUpdate() {
/* callbackRunnable = new Runnable(){
@Override
public void run() {
Log.d(TAG, "calling callback");
traceCWrapper.getLatestTraceResult();
callbackHandler.postDelayed(this, 1000);
}
};*/
}
private void startCallbackTimer(){
Log.d(TAG, "I get in here callback");
callbackRunnable = new Runnable(){
@Override
public void run() {
Log.d(TAG, "calling callback");
traceCWrapper.getLatestTraceResult();
callbackHandler.postDelayed(this, 1000);
}
};
callbackHandler.postDelayed(callbackRunnable, 1000);
}
private void stopCallbackTimer(){
callbackHandler.removeCallbacks(callbackRunnable);
}
//Calls TraceCWrapper upadate maps and passes the dynamic maps
/* public void updateMaps(String[] dynamicMaps){
traceCWrapper.updateMaps(dynamicMaps dynamicmaps){
}
}*/
public void stopUpdatingTrace(){
boolean stopSensors = true;
if(stopSensors){
stopUpdatingSensors();
}
//Callback Timer
stopCallbackTimer();
//State
isRunning = false;
//Trace terminate
if (_traceCDontActuallyUse == false){
traceCWrapper.terminate();
}
}
private void stopUpdatingSensors() {
//todo
//stop the event bus
//stop the service
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void provideManualLocation(TraceManualLocation manualLocation){
if(isRunning){
}else{
Log.e(TAG, "Calling provideManualLocation, but is running is set to false");
}
if(!_traceCDontActuallyUse){
traceCWrapper.provideManualLocation(manualLocation);
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void provideManualHeadingCorrection(TraceManualHeading traceManualHeading){
if(isRunning){
}else{
Log.e(TAG, "Calling provideHeadingCorrection, but is running is set to false");
}
if (!_traceCDontActuallyUse){
traceCWrapper.provideManualHeading(traceManualHeading);
}
}
public void updateParameter(TraceCVarParameter traceCVarParameter, double value){
if(isRunning){
}else{
Log.e(TAG, "Calling updateparameter, but is running is set to false");
}
//todo
//callback async
}
//Private [START]
boolean isInitialised = false;
public boolean isInitialised() {
if(!isInitialised){
}else{
//todo
//send to didfinishinitialisation? confirm isMagConsistentAtInit is true
}
return isInitialised;
}
private boolean isMagConsistantAtInit = false;
private Timer callbackTimer;
/* public traceCallBack(int seconds){
callBackTimer = new Timer();
callBackTimer.schedule(new callUpdate(), seconds*1000);
}*/
class callUpdate extends TimerTask{
@Override
public void run() {
//traceCWrapper.getLatestTraceResult();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_trace_manager);
}
}
I do not have enough room to add the TraceCWrapper file however the library is loaded as:
static CLibrary lib = Native.loadLibrary("com.waymap.app.traceandroid", CLibrary.class);
And as the main example the method, traceCWrapper.provideDeviceMotion() is received in TraceCWrapper as:
//Provide Device Motion
public static boolean provideDeviceMotion(TCIMUEvent mTCIMUEvent, int Status, double userHeadingDeg, float rotationMode){
DeviceMotion dM = new DeviceMotion();
dM.setTcimuEvent(mTCIMUEvent);
dM.setStatus(Status);
dM.setUserHeadingDeg(userHeadingDeg);
dM.setRotationMode(rotationMode);
if(isRunning) {
new sendToTraceHandleImuEvent().execute(dM);
isInitalized = true;
return isInitalized;
}else{
Log.i(TAG, "IMU update ignored as not running");
isInitalized = false;
return isInitalized;
}
}
public static class sendToTraceHandleImuEvent extends AsyncTask<DeviceMotion,Void,Void>{
@Override
protected Void doInBackground(DeviceMotion... devicemotions) {
/*public class Arg extends Structure {
public devicemotions[] var1 = new byte[9];
public devicemotions[] var2 = new byte[5];
}*/
Log.d(TAG, "InTraceCwrapper Again, provideIMU");
lib.TraceHandleImuEvent(devicemotions[0].getTcimuEvent(), devicemotions[0].getStatus(), devicemotions[0].getUserHeadingDeg(), devicemotions[0].getRotationMode());
return null;
}
}
You will have to excuse the large amount of Logging and excess code as i have been wrestling with this for a while.
When passing my TCIMUEvent i am using the structure annotation as below:
@Structure.FieldOrder({ "time", "accel", "accelValid", "mag", "magValid", "gyro", "gyroValid", "pressure", "pressureValid", "temperature", "temperatureValid"})
public class TCIMUEvent extends Structure implements Serializable {
public double time;
public float[] accel = new float[3];
public boolean accelValid;
public float[] mag = new float[3];
public boolean magValid;
public float[] gyro = new float[3];
public boolean gyroValid;
public float pressure;
public boolean pressureValid;
public float temperature;
public boolean temperatureValid;
public TCIMUEvent(double time, float[] accel, boolean accelValid, float[] mag, boolean magValid, float[] gyro, boolean gyroValid, float pressure, boolean pressureValid, float temperature, boolean temperatureValid) {
this.time = time;
this.accel = accel;
this.accelValid = accelValid;
this.mag = mag;
this.magValid = magValid;
this.gyro = gyro;
this.gyroValid = gyroValid;
this.pressure = pressure;
this.pressureValid = pressureValid;
this.temperature = temperature;
this.temperatureValid = temperatureValid;
}
}
The Java C Mappings that are required:
My Java Library to map:
void TracehandleLocationEvent(TCLocationEvent tcLocationEvent);
void TracehandleManualLocationEvent(TCManualLocationEvent tcManualLocationEvent);
void TracehandleManualHeadingEvent(TCManualHeadingEvent tcManualHeadingEvent);
void TracehandleManualInitialLocation(TCLocationEvent initialLocationEvent);
void TraceHandleImuEvent(TCIMUEvent tcimuEvent, int Status, double userHeadingDeg, float rotationMode);
void TraceGetResult(Double uptime, Pointer traceResult_out);
-------- These map retrospectively to C:---------
void TraceHandleLocationEvent (const Trace_locationSample_t *locationSample)
void TraceHandleManualLocationEvent(const Trace_manualLocationSample_t
*manualLocationSample)
void TraceHandleManualHeadingEvent(const Trace_manualHeadingSample_t
*manualHeadingSample)
void TraceHandleLocationEvent (const Trace_locationSample_t *locationSample)
void TraceHandleImuEvent(Trace_imuDataSample_t *imuDataSample, int *status,
double *userHeadingDeg, StrapdownStreaming_RotationMode *currentRotateMode)
void TraceGetResult(double time, Trace_Result_t *TraceResult)
The new Mappings look like this, the structures for the objects are all the same format as above in the original question:
void TracehandleLocationEvent(TCLocationEvent tcLocationEvent);
void TracehandleManualLocationEvent(TCManualLocationEvent tcManualLocationEvent);
void TracehandleManualHeadingEvent(TCManualHeadingEvent tcManualHeadingEvent);
void TracehandleManualInitialLocation(TCLocationEvent initialLocationEvent);
void TraceGetResult(DoubleByReference uptime, TCResult traceResult_out);
void TraceHandleImuEvent(TCIMUEvent tcimuEvent, IntByReference status, DoubleByReference heading, FloatByReference rotationMode);
The error being thrown now in relation to the empty constructors in my Structure objects:
java.lang.Error: Structure.getFieldOrder() on class com.dataTypes.TCLocationEvent returns names ([altitude, coordinate, horizontalAccuracy, timestamp, verticalAccuracy]) which do not match declared field names ([])
at com.sun.jna.Structure.getFields(Structure.java:1089)
at com.sun.jna.Structure.deriveLayout(Structure.java:1232)
at com.sun.jna.Structure.calculateSize(Structure.java:1159)
at com.sun.jna.Structure.calculateSize(Structure.java:1111)
at com.sun.jna.Structure.allocateMemory(Structure.java:414)
at com.sun.jna.Structure.<init>(Structure.java:205)
at com.sun.jna.Structure.<init>(Structure.java:193)
at com.sun.jna.Structure.<init>(Structure.java:180)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.dataTypes.TCLocationEvent.<init>(TCLocationEvent.java:30)
at com.locationGetter.LocationService.<clinit>(LocationService.java:39)
SIGSEGV errors with JNA mappings are frequently caused by accessing native memory you don't own. Problems vary, but the first place to look is your structure type mappings and method/function argument mappings.
As one specific example (there may be more), your code includes this mapping:
void TraceHandleImuEvent(TCIMUEvent tcimuEvent, int Status,
double userHeadingDeg, float rotationMode);
However, the native mapping does not expect an int
, double
, and float
here. It expects pointers:
void TraceHandleImuEvent(Trace_imuDataSample_t *imuDataSample, int *status,
double *userHeadingDeg, StrapdownStreaming_RotationMode *currentRotateMode)
(Structures like TCIMUEvent
are automatically mapped to their pointers when passed as arguments, so that one's okay.)
What is happening is that you are passing an int
for status (e.g., 8) but the native code is thinking "There's an integer stored at memory location 0x8
." You don't own that memory, and thus the error.
IntByReference
would be the correct type mapping here, and for many of those function arguments.