{ "id": "159371", "key": "TIMOB-23165", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "17706", "name": "Release 5.4.0", "archived": false, "released": true, "releaseDate": "2016-08-11" } ], "resolution": { "id": "1", "description": "A fix for this issue is checked into the tree and tested.", "name": "Fixed" }, "resolutiondate": "2016-04-13T16:36:17.000+0000", "created": "2016-04-07T09:41:28.000+0000", "epic": { "id": 151897, "key": "TIMOB-19699", "name": "Android: Hyperloop for Android module", "summary": "Bring Hyperloop to Android", "color": { "key": "color_4" }, "done": false }, "priority": { "name": "Critical", "id": "1" }, "labels": [ "android", "hyperloop" ], "versions": [], "issuelinks": [], "assignee": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "updated": "2016-08-01T22:47:32.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "13715", "name": "Hyperloop", "description": "Hyperloop project" } ], "description": "Trying to implement BLE scanner the following error is returned:\r\n{code:java}\r\n[ERROR] : HyperloopProxy: (main) [828,2867] Exception thrown during invocation of method: public boolean android.bluetooth.BluetoothAdapter.startLeScan(android.bluetooth.BluetoothAdapter$LeScanCallback), args: [null]\r\n[ERROR] : HyperloopProxy: java.lang.NullPointerException: Expected to unbox a 'int' primitive type but was returned null\r\n[ERROR] : HyperloopProxy: at $Proxy0.hashCode(Unknown Source)\r\n[ERROR] : HyperloopProxy: at java.util.Collections.secondaryHash(Collections.java:3405)\r\n[ERROR] : HyperloopProxy: at java.util.HashMap.containsKey(HashMap.java:325)\r\n[ERROR] : HyperloopProxy: at android.bluetooth.BluetoothAdapter.startLeScan(BluetoothAdapter.java:2189)\r\n[ERROR] : HyperloopProxy: at android.bluetooth.BluetoothAdapter.startLeScan(BluetoothAdapter.java:2157)\r\n[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Native Method)\r\n[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Method.java:372)\r\n[ERROR] : HyperloopProxy: at hyperloop.BaseProxy.invokeMethod(BaseProxy.java:133)\r\n[ERROR] : HyperloopProxy: at hyperloop.BaseProxy.callNativeFunction(BaseProxy.java:115)\r\n[ERROR] : HyperloopProxy: at org.appcelerator.kroll.runtime.v8.V8Object.nativeFireEvent(Native Method)\r\n[ERROR] : HyperloopProxy: at org.appcelerator.kroll.runtime.v8.V8Object.fireEvent(V8Object.java:62)\r\n[ERROR] : HyperloopProxy: at org.appcelerator.kroll.KrollProxy.doFireEvent(KrollProxy.java:918)\r\n[ERROR] : HyperloopProxy: at org.appcelerator.kroll.KrollProxy.handleMessage(KrollProxy.java:1141)\r\n[ERROR] : HyperloopProxy: at org.appcelerator.titanium.proxy.TiViewProxy.handleMessage(TiViewProxy.java:356)\r\n[ERROR] : HyperloopProxy: at ti.modules.titanium.ui.widget.listview.ListViewProxy.handleMessage(ListViewProxy.java:312)\r\n[ERROR] : HyperloopProxy: at android.os.Handler.dispatchMessage(Handler.java:98)\r\n[ERROR] : HyperloopProxy: at android.os.Looper.loop(Looper.java:145)\r\n[ERROR] : HyperloopProxy: at android.app.ActivityThread.main(ActivityThread.java:5951)\r\n[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Native M\r\n[ERROR] : HyperloopProxy: at java.lang.reflect.Method.invoke(Method.java:372)\r\n[ERROR] : HyperloopProxy: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)\r\n[ERROR] : HyperloopProxy: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)\r\n\r\n{code}\r\n\r\n\r\nand Code to reproduce:\r\n\r\n\r\n{code:java}\r\nvar BluetoothManager = require('android.bluetooth.BluetoothManager'),\r\nBluetoothDevice = require('android.bluetooth.BluetoothDevice'),\r\nContext = require('android.content.Context')\r\n LeScanCallback = require('android.bluetooth.BluetoothAdapter.LeScanCallback'),\r\n Activity = require('android.app.Activity'),\r\n activity = new Activity(Ti.Android.currentActivity);\r\n\r\n\r\nvar bm = BluetoothManager.cast(activity.getSystemService(Context.BLUETOOTH_SERVICE));\r\n\tvar bluetoothAdapter = bm.getAdapter();\r\n\tconsole.log(bluetoothAdapter.isEnabled());\r\n\tvar scanCallback = new LeScanCallback({\r\n\t\tonLeScan : function(device, rssi, data) {\r\n\t\t\tconsole.log('startScan');\r\n​\r\n\t\t}\r\n\t});\r\n\tbluetoothAdapter.startLeScan(scanCallback);\r\n{code}\r\n\r\n", "attachment": [], "flagged": false, "summary": "Hyperloop: Android Anonymous interface implementations need to have default implementations of equals/hashCode/toString", "creator": { "name": "bgarcia", "key": "bgarcia", "displayName": "Brian García", "active": true, "timeZone": "America/Los_Angeles" }, "subtasks": [], "reporter": { "name": "bgarcia", "key": "bgarcia", "displayName": "Brian García", "active": true, "timeZone": "America/Los_Angeles" }, "environment": "SDK: 5.4.0.v20160405023842\r\nCLI:5.2.2 \r\nhyperloop module 1.1.2\r\nAndroid version: 4.3 and 5.0.1\r\n", "closedSprints": [ { "id": 618, "state": "closed", "name": "2016 Sprint 08 SDK", "startDate": "2016-04-09T00:30:18.262Z", "endDate": "2016-04-23T00:30:00.000Z", "completeDate": "2016-04-25T02:38:28.511Z", "originBoardId": 114 } ], "comment": { "comments": [ { "id": "382079", "author": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "body": "OK, so after a little thinking, I believe I know what is causing this. I couldn't test on an emulator because there's no bluetooth, but the stack trace jogged a few thoughts. My guess here its that the dynamic proxy class we're generating for the anonymous instance of the LeScanCallback is being asked for it's hashcode. Our dynamic function invoker looks for an override method in the JS code for any method, and if it doesn't exist, it returns a null return value. So in this case, it's looking for a hashcode method on the LeScanCallback, sees none and returns null - but hashcode has to return a primitive int; so Java chokes.\r\n\r\nSo the question here is: what the heck should be do about the base Object methods: hashCode(), equals(), toString() on anonymous implementations of interfaces if the user doesn't provide a JS implementation? \r\n\r\nThis may provide some clues: https://javaclippings.wordpress.com/2009/03/18/dynamic-proxies-equals-hashcode-tostring/", "updateAuthor": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "created": "2016-04-07T15:13:38.000+0000", "updated": "2016-04-07T15:14:09.000+0000" }, { "id": "382083", "author": { "name": "bgarcia", "key": "bgarcia", "displayName": "Brian García", "active": true, "timeZone": "America/Los_Angeles" }, "body": "I understand what you explained, but I'm currently overriding the onLeScan method, or at least I think so.\r\n\r\nthanks for explanation!", "updateAuthor": { "name": "bgarcia", "key": "bgarcia", "displayName": "Brian García", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-04-07T15:30:30.000+0000", "updated": "2016-04-07T15:30:30.000+0000" }, { "id": "382119", "author": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "body": "[~bgarcia] The note was more to myself than anyone else. But basically, you may be able to workaround this in the meantime by also overriding the hashCode method in your LeScanCallback impl:\r\n{code:javascript}\r\nvar scanCallback = new LeScanCallback({\r\n onLeScan : function(device, rssi, data) {\r\n console.log('startScan');\r\n },\r\n hashCode: function () {\r\n return 123; // any int should work\r\n }\r\n});\r\n{code}", "updateAuthor": { "name": "cwilliams", "key": "cwilliams", "displayName": "Christopher Williams", "active": true, "timeZone": "America/New_York" }, "created": "2016-04-07T19:17:21.000+0000", "updated": "2016-04-07T19:17:21.000+0000" }, { "id": "382121", "author": { "name": "bgarcia", "key": "bgarcia", "displayName": "Brian García", "active": true, "timeZone": "America/Los_Angeles" }, "body": "@Chris Williams thanks I'll try. Something out of the topic, how can I iterate over a java.Util.List?", "updateAuthor": { "name": "bgarcia", "key": "bgarcia", "displayName": "Brian García", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2016-04-07T19:29:46.000+0000", "updated": "2016-04-07T19:29:46.000+0000" }, { "id": "392158", "author": { "name": "wluu", "key": "wluu", "displayName": "Wilson Luu", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing ticket as fixed. Verified that if I use the above code, I do not get a null pointer exception.\r\n\r\nTested on:\r\n\r\nAppc CLI NPM: 4.2.7\r\nAppc CLI Core: 5.4.0-37\r\nArrow: 1.8.2\r\nSDK: 5.4.0.v20160801022303\r\nNode: v4.4.7\r\nOS: Mac OS X (10.11.6)\r\nDevice: Samsung Galaxy S4 (4.4.4)", "updateAuthor": { "name": "wluu", "key": "wluu", "displayName": "Wilson Luu", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2016-08-01T22:46:17.000+0000", "updated": "2016-08-01T22:46:17.000+0000" }, { "id": "392159", "author": { "name": "wluu", "key": "wluu", "displayName": "Wilson Luu", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Forgot to mention that I used Hyperloop 1.2.5 along with my above stack.", "updateAuthor": { "name": "wluu", "key": "wluu", "displayName": "Wilson Luu", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2016-08-01T22:47:32.000+0000", "updated": "2016-08-01T22:47:32.000+0000" } ], "maxResults": 7, "total": 7, "startAt": 0 } } }