Android Java Fuser crash when creating Data Route
I have been experimenting with the Fuser in Java recently. I can't get it to work because it keeps crashing upon creation of the Data Route every single time I run it, even though I followed the example from the documentation.
Board: MMR model number 5, hardware revision 0.3, firmware revision 1.5.0
Host: Samsung Galaxy S9, Android 10 (One UI 2.1), kernel 4.9.118-18847185 Aug 19 2020
SDK: Java SDK 3.7.1. I also tried 3.8.0 b00, with the same result.
Note that I posted a similar message last week in the Android group that so far got no replies.I think this group is more appropriate.
Code, based on the FreeFall example:
public void onServiceConnected(ComponentName name, IBinder service) {
BtleService.LocalBinder serviceBinder = (BtleService.LocalBinder) service;
String mwMacAddress = "XX:XX:XX:XX:XX:XX"; ///< Removed MAC address for privacy reasons
BluetoothManager btManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
BluetoothDevice btDevice = btManager.getAdapter().getRemoteDevice(mwMacAddress);
mwBoard = serviceBinder.getMetaWearBoard(btDevice);
mwBoard.connectAsync().onSuccessTask(task -> {
Log.d(LOG_TAG, "connectAsync");
final Accelerometer accelerometer = mwBoard.getModule(Accelerometer.class);
accelerometer.configure()
.odr(100f) // 100 Hz sample rate
.range(4f) // +- 4g range
.commit();
accelCapture.set(accelerometer);
final AsyncDataProducer acceleration;
if (accelerometer instanceof AccelerometerBosch) {
acceleration = ((AccelerometerBosch) accelerometer).acceleration();
} else {
acceleration = ((AccelerometerMma8452q) accelerometer).acceleration();
}
accelerationCapture.set(acceleration);
final GyroBmi160 gyro = mwBoard.getModule(GyroBmi160.class);
gyro.configure()
.odr(GyroBmi160.OutputDataRate.ODR_100_HZ) // 100 Hz
.range(GyroBmi160.Range.FSR_2000) // 2000 deg/s max
.commit();
gyroCapture.set(gyro);
final AsyncDataProducer angularvelocity = gyro.angularVelocity();
angvelCapture.set(angularvelocity);
return angvelCapture.get().addRouteAsync(source -> source.buffer().name("gyro-buffer"))
.onSuccessTask(ignored -> angvelCapture.get().addRouteAsync(source ->
source.fuse("gyro-buffer").stream((data, env) -> {
Data[] values = data.value(Data[].class);
Acceleration accdata = values[0].value(Acceleration.class);
Log.d(LOG_TAG, "acc = " + accdata.toString());
AngularVelocity gyrodata = values[1].value(AngularVelocity.class);
Log.d(LOG_TAG, "gyr = " + gyrodata.toString());
})))
.continueWith((Continuation<Route, Void>) continuetask -> {
if (continuetask.isFaulted()) {
Log.e(LOG_TAG, mwBoard.isConnected() ? "Error setting up route" : "Error connecting", continuetask.getError());
} else {
Log.d(LOG_TAG, "Connected");
debug = mwBoard.getModule(Debug.class);
}
return null;
});
});
}
Stack trace:
2020-10-11 14:18:13.678 29267-29425/com.mbientlab.metawear.acquisition E/acquisition: Error setting up route java.lang.NullPointerException: Attempt to read from field 'com.mbientlab.metawear.impl.DataTypeBase com.mbientlab.metawear.impl.DataProcessorImpl$Processor.state' on a null object reference at com.mbientlab.metawear.impl.DataTypeBase.dataProcessorTransform(DataTypeBase.java:351) at com.mbientlab.metawear.impl.FloatVectorData.dataProcessorTransform(FloatVectorData.java:57) at com.mbientlab.metawear.impl.RouteComponentImpl.fuse(RouteComponentImpl.java:877) at com.mbientlab.metawear.acquisition.MainActivity.lambda$null$4(MainActivity.java:153) at com.mbientlab.metawear.acquisition.-$$Lambda$MainActivity$Ny3LVMOUTY5OPcoJHdUmJCqU4-s.configure(Unknown Source:0) at com.mbientlab.metawear.impl.JseMetaWearBoard.createRoute(JseMetaWearBoard.java:1404) at com.mbientlab.metawear.impl.JseMetaWearBoard.access$1100(JseMetaWearBoard.java:93) at com.mbientlab.metawear.impl.JseMetaWearBoard$1.queueRouteBuilder(JseMetaWearBoard.java:312) at com.mbientlab.metawear.impl.GyroBmi160Impl$2.addRouteAsync(GyroBmi160Impl.java:255) at com.mbientlab.metawear.acquisition.MainActivity.lambda$null$5$MainActivity(MainActivity.java:152) at com.mbientlab.metawear.acquisition.-$$Lambda$MainActivity$MtqEXyHIMj-gS7aooSVhtz_phuQ.then(Unknown Source:2) at bolts.Task$15.run(Task.java:917) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:919)
I have extensively tried to debug this code. The crash occurs in line 351 of DataTypeBase.java:
346 case DataProcessorConfig.Fuser.ID: {
347 byte fusedLength = attributes.length();
348 DataProcessorConfig.Fuser casted = (DataProcessorConfig.Fuser) config;
349
350 for(byte id: casted.filterIds) {
351 fusedLength+= dpModule.activeProcessors.get(id).state.attributes.length(); // <- point of crash: id==0
352 }
353
354 return new Pair<>(new ArrayData(this, DATA_PROCESSOR, DataProcessorImpl.NOTIFY, new DataAttributes(new byte[] {fusedLength}, (byte) 1, (byte) 0, false)), null);
The problem here is that 'filterIds[]' in the DataProcessorConfig.Fuser.config structure has its member(s) set to 0 in the constructor that is used when providing buffer names. So id == 0 in the get(id), returning a null pointer and causing the crash on the state property.
It seems to me that Fuser.syncFilterIds() should have been called somewehere prior to this line, since that is where the filterIds[] array is filled to link buffer names to activeProcessors using ids. I have manually set the id to the right value, and the code continued to run.
I can't see a way in which my code could influence this behaviour, so I don't know how to solve this. Any help would be highly appreciated.