Search code examples
androidbluetoothairbluetooth-lowenergyane

Context dispatchStatusEventAsync throwing IllegalArgumentException


I am creating an ANE that interfaces with an Android SDK for proprietary Bluetooth LE devices. The SDK has been tested on a pure-Android project and is working fine. I am using FREContext.dispatchStatusEventAsync to return information about each beacon that is detected. However, in watching the logs returning from LogCat while the AIR app is running, I'm seeing an IllegalArgumentException being thrown each time a beacon is found. Interestingly, I am able to use dispatchStatusEventAsync anywhere else and it seems to be working fine.

Here is all of the pertinent code for the ANE. (I've masked the name of the SDK by doing a find/replace but the code compiles so please do not consider it a possible issue if there is something crazy with the naming of a class or function).

The "DUMMY" statements just indicate that they are for debug.

BecsterExtension.java

public class BecsterExtension implements FREExtension 
{
    public static final String TAG = "BecsterExtension";

    @Override
    public FREContext createContext(String arg0) 
    {
        return new BecsterContext();
    }

    @Override
    public void dispose() 
    {
        // TODO Auto-generated method stub

    }

    @Override
    public void initialize() 
    {
        // TODO Auto-generated method stub

    }

}

BecContext.java

public class BecContext extends FREContext {

@Override
public void dispose() 
{
    // TODO Auto-generated method stub

}

@Override
public Map<String, FREFunction> getFunctions() 
{
    Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
    functionMap.put("startScanning", new StartScanFunction());
    return functionMap;
}

}

StartScanFunction.java

private final static String TAG = StartScanFunction.class.getSimpleName();

byte[] advPacket;

public BecsterBeacon becBecMgr; 

@Override
public FREObject call(FREContext context, FREObject[] args) 
{

    //****-----BECSTER SDK START-----****//

    Log.i(TAG, "**Initializing Becster SDK**");

    //this call works
    context.dispatchStatusEventAsync("BECSTER_DUMMY", "Becster SDK has started initalizing");

    if (!context.getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) 
    {
        Log.e(TAG, "Bluetooth LE not supported or disabled");
        return null;
    }

    // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
    // BluetoothAdapter through BluetoothManager.
    final BluetoothManager bluetoothManager =
            (BluetoothManager) context.getActivity().getSystemService(Context.BLUETOOTH_SERVICE);

    Log.i(TAG, "Starting Becster Beacon Manager");

    becBecMgr = new BecsterBeacon(bluetoothManager, new BecNotifyHandler(context));

    Log.i(TAG, "**Becster SDK Initialized**");

    //this call works
    context.dispatchStatusEventAsync("BECSTER_DUMMY", "Becster SDK has finished initalizing");

    BecBecMgr.startLEScan();

    return null;
}

BecNotifyHandler.java (This implements BecEvent's function becsterEventNotify which is called from inside

public static final String TAG = "BecNotifyHandler"; 

private FREContext context;

public BecNotifyHandler(FREContext context)
{
    context.dispatchStatusEventAsync("BECSTER_DUMMY", "BecNotifyHandler has been initialized");
    this.context = context;
}

@Override
public void becsterEventNotify(BecPkt event) 
{
    try
    {
        Log.i(TAG, "Notifying of Becster event. Context: " + context);

        //this call ALWAYS throws the IllegalArgumentException
        context.dispatchStatusEventAsync("BECSTER_NOTIFY", "Becster notify");
        Log.i(TAG, "Notified");
    }
    catch (IllegalArgumentException e)
    {
        e.printStackTrace();
    }

}

LogCat:

07-17 10:50:33.949: I/BecNotifyHandler(17467): Notifying of Becster event. Context: com.company.sdk.BecsterContext@41e2df30
07-17 10:50:33.949: W/System.err(17467): java.lang.IllegalArgumentException
07-17 10:50:33.949: W/System.err(17467):    at com.adobe.fre.FREContext.dispatchStatusEventAsync(Native Method)
07-17 10:50:33.949: W/System.err(17467):    at com.company.sdk.BecNotifyHandler.becsterEventNotify(BecNotifyHandler.java:27)
07-17 10:50:33.949: W/System.err(17467):    at com.becster.becsterSDK.BecsterBeacon$1$1.run(BecsterBeacon.java:280)
07-17 10:50:33.949: W/System.err(17467):    at android.os.Handler.handleCallback(Handler.java:733)
07-17 10:50:33.949: W/System.err(17467):    at android.os.Handler.dispatchMessage(Handler.java:95)
07-17 10:50:33.949: W/System.err(17467):    at android.os.Looper.loop(Looper.java:136)
07-17 10:50:33.949: W/System.err(17467):    at android.app.ActivityThread.main(ActivityThread.java:5141)
07-17 10:50:33.949: W/System.err(17467):    at java.lang.reflect.Method.invokeNative(Native Method)
07-17 10:50:33.957: W/System.err(17467):    at java.lang.reflect.Method.invoke(Method.java:515)
07-17 10:50:33.957: W/System.err(17467):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:795)
07-17 10:50:33.957: W/System.err(17467):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:611)
07-17 10:50:33.957: W/System.err(17467):    at dalvik.system.NativeStart.main(Native Method)

Any insight greatly appreciated!


Solution

  • context.dispatchStatusEventAsync continued to yield some very weird results. When I would invoke becsterEventNotify once, I would receive the correct message. Invoking becsterEventNotify twice back to back yielded no message dispatched. Thus, I am still unsure what is causing the problem. I suspect there may be an issue with threading as I used Thread.sleep(1000) at one point to see if putting some time in between messages would help, but that caused no messages at all to be sent.

    In case anyone is curious, I did come up with a workaround. Instead of using dispatchStatusEventAsync, I created a static Vector of Strings to hold the data from the beacons (id, battery life, and rssi).

    public static Vector<String> packets = new Vector<String>();
    

    Then added this gem as a function:

    public class GetNextPacketFunction implements FREFunction 
    {
    private static final String TAG = GetNextPacketFunction.class.getSimpleName();
    
    @Override
    public FREObject call(FREContext context, FREObject[] args) 
    {
        Vector<String> packets = BecsterExtension.packets;
    
        if (packets != null && packets.size() > 0)
        {
            String nextPacket = packets.remove(packets.size() - 1);
            Log.i(TAG, "Found packet: " + nextPacket);
            try 
            {
                return FREObject.newObject(nextPacket);
            } 
            catch (FREWrongThreadException e) 
            {
                Log.e(TAG, "WRONG THREAD!");
                e.printStackTrace();
            }
        }
    
        return null;
    }
    
    }
    

    Then I implemented the notifier like so to save the packets as they came in:

    @Override
    public void becsterEventNotify(BecPkt event) 
    {
    
        String newPacket = btIDStr + ";" + event.getBattVal() + ";" + event.getRssiVal();
        BecsterExtension.packets.add(newPacket);
    
        Log.i(TAG, "Number of packets in queue: " + BecsterExtension.packets.size());
    
    }
    

    Then on the AS side I use a Timer to poll for new packets every second (haven't determined if this is the optimum interval):

                var packetTimer:Timer = new Timer(1000);
                packetTimer.addEventListener(TimerEvent.TIMER, function (e:TimerEvent):void
                {
                    var newPacket:String = getNextPacket();
    
                    if (newPacket != null)
                    {
                        var data:Array = newPacket.split(";");
    
                        var becEvent:BecEvent = new BecEvent(BecEvent.NOTIFY);
                        becEvent.id = data[0];
                        becEvent.battLife = data[1];
                        becEvent.rssi = data[2];
                        dispatchEvent(becEvent);
                    }
                });
    
                packetTimer.start();
    

    This approach is currently working very well and I kinda like it better because I get to manage the queuing on the AS side.

    Thanks all!