From 9f8bb9fb2412896ee4c67edd007d1cb8713edeff Mon Sep 17 00:00:00 2001 From: Shankari Date: Fri, 12 Apr 2024 08:28:00 -0700 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=97=83=EF=B8=8F=20Add=20keys=20for=20?= =?UTF-8?q?the=20new=20BLE-related=20transitions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And make sure that they can be sent from the UI --- res/android/statemachine.xml | 2 ++ src/android/DataCollectionPlugin.java | 2 ++ src/ios/BEMDataCollection.m | 2 ++ src/ios/Location/TripDiaryStateMachine.h | 2 ++ 4 files changed, 8 insertions(+) diff --git a/res/android/statemachine.xml b/res/android/statemachine.xml index ff626b17..43cb3b11 100644 --- a/res/android/statemachine.xml +++ b/res/android/statemachine.xml @@ -11,6 +11,8 @@ local.transition.stopped_moving local.transition.stop_tracking local.transition.start_tracking + local.transition.ble_beacon_found + local.transition.ble_beacon_lost local.transition.tracking_error diff --git a/src/android/DataCollectionPlugin.java b/src/android/DataCollectionPlugin.java index 8387a08b..1cc6b7ba 100644 --- a/src/android/DataCollectionPlugin.java +++ b/src/android/DataCollectionPlugin.java @@ -214,6 +214,8 @@ private static Map getTransitionMap(Context ctxt) { retVal.put("STOPPED_MOVING", ctxt.getString(R.string.transition_stopped_moving)); retVal.put("STOP_TRACKING", ctxt.getString(R.string.transition_stop_tracking)); retVal.put("START_TRACKING", ctxt.getString(R.string.transition_start_tracking)); + retVal.put("BLE_BEACON_FOUND", ctxt.getString(R.string.ble_beacon_found)); + retVal.put("BLE_BEACON_LOST", ctxt.getString(R.string.ble_beacon_lost)); return retVal; } diff --git a/src/ios/BEMDataCollection.m b/src/ios/BEMDataCollection.m index ba656172..954553b5 100644 --- a/src/ios/BEMDataCollection.m +++ b/src/ios/BEMDataCollection.m @@ -367,6 +367,8 @@ - (NSDictionary*) getTransitionMap { retVal[@"RECEIVED_SILENT_PUSH"] = CFCTransitionRecievedSilentPush; retVal[@"VISIT_STARTED"] = CFCTransitionVisitStarted; retVal[@"VISIT_ENDED"] = CFCTransitionVisitEnded; + retVal[@"BLE_BEACON_FOUND"] = CFCTransitionBeaconFound; + retVal[@"BLE_BEACON_LOST"] = CFCTransitionBeaconLost; return retVal; } diff --git a/src/ios/Location/TripDiaryStateMachine.h b/src/ios/Location/TripDiaryStateMachine.h index 2dd99de1..c97740f7 100644 --- a/src/ios/Location/TripDiaryStateMachine.h +++ b/src/ios/Location/TripDiaryStateMachine.h @@ -37,6 +37,8 @@ #define CFCTransitionStartTracking @"T_START_TRACKING" #define CFCTransitionVisitStarted @"T_VISIT_STARTED" #define CFCTransitionVisitEnded @"T_VISIT_ENDED" +#define CFCTransitionBeaconFound @"T_BLE_BEACON_FOUND" +#define CFCTransitionBeaconLost @"T_BLE_BEACON_LOST" #define CFCTransitionNOP @"T_NOP" /* From 42203029e5a62e366750e5ed28c0edffbc285c32 Mon Sep 17 00:00:00 2001 From: Shankari Date: Sat, 13 Apr 2024 10:04:43 -0700 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=97=83=EF=B8=8F=20Plumb=20through=20a?= =?UTF-8?q?n=20initial=20version=20of=20the=20native=20classes=20for=20the?= =?UTF-8?q?=20Bluetooth=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The data format is very similar to the beacon object in iOS. We consciously patterned the object to be similar to `CLBeacon` since the alt-beacon library for android is an effort to be compatible with iOS to the extent possible. Our only changes are to change the fields in iOS to be more easily serializable https://github.com/e-mission/e-mission-docs/issues/1062#issuecomment-2053117434 https://github.com/e-mission/e-mission-docs/issues/1062#issuecomment-2053192499 Testing done: After adding a hack to read/write messages on every ble_found transition, https://github.com/e-mission/e-mission-phone/pull/1144#issuecomment-2053705589 was able to verify that both read and write worked properly Android: ``` sqlite> select * from userCache where type == "sensor-data"; 1713026512.887||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026512887E9} ``` ``` 04-13 09:45:21.365 18332 18332 I TripDiaryStateMachineRcvr: TripDiaryStateMachineReciever onReceive(android.app.ReceiverRestrictedContext@9e3bd75, Intent { act=local.transition.ble_beacon_found flg=0x10 pkg=edu.berkeley.eecs.emission cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineReceiver }) called 04-13 09:45:21.385 18332 18332 I TripDiaryStateMachineService: Service created. Initializing one-time variables! 04-13 09:45:21.396 18332 18332 D TripDiaryStateMachineService: service started with flags = 0 startId = 1 action = local.transition.ble_beacon_found 04-13 09:45:21.404 18332 18332 D TripDiaryStateMachineService: after reading from the prefs, the current state is local.state.waiting_for_trip_start 04-13 09:45:21.424 18332 18332 D BuiltinUserCache: Added value for key statemachine/transition at time 1.713026721405E9 04-13 09:45:21.432 18332 18332 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.ble_beacon_found) called 04-13 09:45:21.448 18332 18332 D BuiltinUserCache: Added value for key background/battery at time 1.713026721433E9 04-13 09:45:21.464 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721449E9 04-13 09:45:21.479 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721465E9 04-13 09:45:21.493 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.71302672148E9 04-13 09:45:21.520 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721493E9 04-13 09:45:21.532 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.71302672152E9 04-13 09:45:21.546 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721533E9 04-13 09:45:21.548 18332 18332 I System.out: [BLE native] Found 5 entries 04-13 09:45:21.548 18332 18332 I System.out: [BLE native] First entry is edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE@afc8f12 last entry is edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE@b0c96e3 04-13 09:45:21.548 18332 18332 I System.out: [BLE native] while handling transition local.transition.ble_beacon_found ``` ``` sqlite> select * from userCache where type == "sensor-data"; 1713026512.887||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026512887E9} 1713026710.433||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026710432E9} 1713026712.809||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026712808E9} 1713026721.433||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026721433E9} 1713026721.449||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"REGION_ENTER","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"0e27c613-ff78-486c-b523-950776777d16"} 1713026721.465||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"d8635e19-4133-493c-b2fd-c04fdd920c6a"} 1713026721.48||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"5efe9e8b-ce30-4417-80e3-79ce9ff53ca9"} 1713026721.493||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"d9283862-2926-4a0c-b377-0e80718e6525"} 1713026721.52||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"cef2f3df-30c5-49d5-8707-2f234530ef18"} 1713026721.533||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"326876e5-9869-407d-976c-2de438bec330"} 1713026800.123||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026800121E9} 1713026800.788||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026800788E9} ``` iOS ``` $ sqlite3 /Users/kshankar/Library/Developer/CoreSimulator/Devices/42B6F8A1-A925-4CAD-A48A-4835D69595ED/data/Containers/Data/Application/13D8F42C-921C-4754-9420-89C4350CE63F/Library/LocalDatabase/userCacheDB SQLite version 3.39.5 2022-10-14 20:58:05 Enter ".help" for usage hints. sqlite> select * from userCache where type == "sensor-data"; sqlite> ``` ``` 2024-04-13 09:34:56.566596-0700 emission[17352:9891606] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-13 09:34:56.566796-0700 emission[17352:9891606] DEBUG: In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-13 09:34:56.568786-0700 emission[17352:9891606] data has 92 bytes, str has size 92 2024-04-13 09:34:56.570995-0700 emission[17352:9891606] data has 69 bytes, str has size 69 2024-04-13 09:35:12.781616-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:12.784409-0700 emission[17352:9891606] data has 176 bytes, str has size 176 2024-04-13 09:35:12.786659-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:12.789160-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:12.791407-0700 emission[17352:9891606] data has 176 bytes, str has size 176 2024-04-13 09:35:12.793945-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:17.900501-0700 emission[17352:9891606] [BLE native] Found 5 entries 2024-04-13 09:35:17.900666-0700 emission[17352:9891606] [BLE native] First entry is , last entry is 2024-04-13 09:35:17.900773-0700 emission[17352:9891606] [BLE native] while handling transition T_BLE_BEACON_FOUND 2024-04-13 09:35:20.434450-0700 emission[17352:9891606] Got unexpected transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP, ignoring ``` ``` sqlite> select * from userCache where type == "sensor-data"; 1713026112.78185||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"REGION_ENTER","ts":1713026111.2200561,"uuid":"35355A74-E587-4F09-B114-D6718E925DC0","proximity":"ProximityNear","accuracy":100} 1713026112.78457||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.784344,"uuid":"3448C47C-BE3B-40B8-A11F-4BD3CC1C2F63","proximity":"ProximityNear","accuracy":100} 1713026112.78698||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.7865958,"uuid":"A2778875-CDF8-4AA8-A2E1-0639C9B4B159","proximity":"ProximityNear","accuracy":100} 1713026112.78947||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.7891002,"uuid":"EE5129DA-6D64-4B25-BA68-3FB3D0FE6BD6","proximity":"ProximityNear","accuracy":100} 1713026112.79173||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.791352,"uuid":"5B6A86F3-BE4D-498E-A3FD-0CAB6039914E","proximity":"ProximityNear","accuracy":100} 1713026112.7941||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.7938981,"uuid":"71E73F8E-4138-4346-8E5F-D05E045A6D69","proximity":"ProximityNear","accuracy":100} ``` --- plugin.xml | 7 +- res/android/statemachine.xml | 4 +- src/android/DataCollectionPlugin.java | 4 +- .../TripDiaryStateMachineReceiver.java | 2 + src/android/wrapper/BluetoothBLE.java | 94 +++++++++++++++++++ src/ios/Wrapper/BluetoothBLE.h | 28 ++++++ src/ios/Wrapper/BluetoothBLE.m | 50 ++++++++++ 7 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 src/android/wrapper/BluetoothBLE.java create mode 100644 src/ios/Wrapper/BluetoothBLE.h create mode 100644 src/ios/Wrapper/BluetoothBLE.m diff --git a/plugin.xml b/plugin.xml index 2781c938..6b25cf4d 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,7 +1,7 @@ + version="1.8.4"> DataCollection Background data collection FTW! This is the part that I really @@ -80,6 +80,8 @@ + + @@ -169,6 +171,7 @@ + @@ -247,6 +250,7 @@ + @@ -269,6 +273,7 @@ + diff --git a/res/android/statemachine.xml b/res/android/statemachine.xml index 43cb3b11..eeb5e9ef 100644 --- a/res/android/statemachine.xml +++ b/res/android/statemachine.xml @@ -11,8 +11,8 @@ local.transition.stopped_moving local.transition.stop_tracking local.transition.start_tracking - local.transition.ble_beacon_found - local.transition.ble_beacon_lost + local.transition.ble_beacon_found + local.transition.ble_beacon_lost local.transition.tracking_error diff --git a/src/android/DataCollectionPlugin.java b/src/android/DataCollectionPlugin.java index 1cc6b7ba..1d2d0f75 100644 --- a/src/android/DataCollectionPlugin.java +++ b/src/android/DataCollectionPlugin.java @@ -214,8 +214,8 @@ private static Map getTransitionMap(Context ctxt) { retVal.put("STOPPED_MOVING", ctxt.getString(R.string.transition_stopped_moving)); retVal.put("STOP_TRACKING", ctxt.getString(R.string.transition_stop_tracking)); retVal.put("START_TRACKING", ctxt.getString(R.string.transition_start_tracking)); - retVal.put("BLE_BEACON_FOUND", ctxt.getString(R.string.ble_beacon_found)); - retVal.put("BLE_BEACON_LOST", ctxt.getString(R.string.ble_beacon_lost)); + retVal.put("BLE_BEACON_FOUND", ctxt.getString(R.string.transition_ble_beacon_found)); + retVal.put("BLE_BEACON_LOST", ctxt.getString(R.string.transition_ble_beacon_lost)); return retVal; } diff --git a/src/android/location/TripDiaryStateMachineReceiver.java b/src/android/location/TripDiaryStateMachineReceiver.java index 444ed6dc..7b6ec0dc 100644 --- a/src/android/location/TripDiaryStateMachineReceiver.java +++ b/src/android/location/TripDiaryStateMachineReceiver.java @@ -85,6 +85,8 @@ public void onReceive(Context context, Intent intent) { context.getString(R.string.transition_stopped_moving), context.getString(R.string.transition_stop_tracking), context.getString(R.string.transition_start_tracking), + context.getString(R.string.transition_ble_beacon_found), + context.getString(R.string.transition_ble_beacon_lost), context.getString(R.string.transition_tracking_error) })); diff --git a/src/android/wrapper/BluetoothBLE.java b/src/android/wrapper/BluetoothBLE.java new file mode 100644 index 00000000..f8366f59 --- /dev/null +++ b/src/android/wrapper/BluetoothBLE.java @@ -0,0 +1,94 @@ +package edu.berkeley.eecs.emission.cordova.tracker.wrapper; + +import java.util.UUID; + +/** + * Created by shankari on 3/30/24. + */ +public class BluetoothBLE { + public String getEventType() { + return eventType; + } + + /* Not sure if we need to return the UUID, major and minor + since we will only get callbacks for the registered UUID + and we don't need to check the major or minor in native code + only that it is **our** beacon. Let's hold off on them for now. + **/ + + /* We do need to use proximity in the FSM to avoid spurious exits (e.g. a + * personal car parked next to a fleet car may still see a region enter, + * but we don't want to track the trip because the proximity is "far". + */ + public String getProximity() { + return proximity; + } + + public double getTs() { + return ts; + } + + // Similarly, we store the accuracy and the rssi for the record, but the + // underlying API already converts it to a proximity value, so we use that + // instead + + private String eventType; + private String uuid; + private int major; + private int minor; + private String proximity; + private double accuracy; + private int rssi; + + private double ts; + // Should we put newState in here as well? + // If so, we will need to change the location of the save + + private BluetoothBLE() {} + + public static BluetoothBLE initRegionEnter(String uuid, double ts) { + BluetoothBLE enterEvent = new BluetoothBLE(); + enterEvent.eventType = "REGION_ENTER"; + enterEvent.uuid = uuid; + enterEvent.ts = ts; + return enterEvent; + } + + public static BluetoothBLE initRegionExit(String uuid, double ts) { + BluetoothBLE exitEvent = new BluetoothBLE(); + exitEvent.eventType = "REGION_EXIT"; + exitEvent.uuid = uuid; + exitEvent.ts = ts; + return exitEvent; + } + + public static BluetoothBLE initRangeUpdate(String uuid, double ts, + int major, int minor, + String proximity, double accuracy, int rssi) { + BluetoothBLE rangeEvent = new BluetoothBLE(); + rangeEvent.eventType = "RANGE_UPDATE"; + rangeEvent.uuid = uuid; + rangeEvent.ts = ts; + + rangeEvent.major = major; + rangeEvent.minor = minor; + rangeEvent.proximity = proximity; + rangeEvent.accuracy = accuracy; + rangeEvent.rssi = rssi; + return rangeEvent; + } + + public static BluetoothBLE initFakeWithEventType(String eventType) { + BluetoothBLE fakeEvent = new BluetoothBLE(); + fakeEvent.uuid = UUID.randomUUID().toString(); + fakeEvent.major = 4538; + fakeEvent.minor = 1256; + fakeEvent.proximity = "ProximityNear"; + fakeEvent.accuracy = 100; + fakeEvent.rssi = 10; + + fakeEvent.eventType = eventType; + fakeEvent.ts = System.currentTimeMillis() / 1000; // time is in seconds for us + return fakeEvent; + } +} diff --git a/src/ios/Wrapper/BluetoothBLE.h b/src/ios/Wrapper/BluetoothBLE.h new file mode 100644 index 00000000..97e96691 --- /dev/null +++ b/src/ios/Wrapper/BluetoothBLE.h @@ -0,0 +1,28 @@ +// +// Transition.h +// CFC_Tracker +// +// Created by Kalyanaraman Shankari on 10/27/15. +// Copyright © 2015 Kalyanaraman Shankari. All rights reserved. +// + +#import +#import + +@interface BluetoothBLE : NSObject + +- (instancetype)initWithCLBeacon:(CLBeacon *)beacon andEventType:(NSString *) eventType; +- (instancetype)initFakeWithEventType:(NSString *)eventType; + +// fields from CLBeacon, modified to be easy to serialize and restore +@property NSString* uuid; +@property NSInteger major; +@property NSInteger minor; +@property NSString* proximity; +@property CLLocationAccuracy accuracy; +@property NSInteger rssi; +@property NSString* eventType; +@property double ts; + ++ (NSString*) proximityToString:(CLProximity) proximityObj; +@end diff --git a/src/ios/Wrapper/BluetoothBLE.m b/src/ios/Wrapper/BluetoothBLE.m new file mode 100644 index 00000000..4f625b05 --- /dev/null +++ b/src/ios/Wrapper/BluetoothBLE.m @@ -0,0 +1,50 @@ +// +// Transition.m +// CFC_Tracker +// +// Created by Kalyanaraman Shankari on 10/27/15. +// Copyright © 2015 Kalyanaraman Shankari. All rights reserved. +// + +#import "BluetoothBLE.h" +#import "DataUtils.h" + +@implementation BluetoothBLE + +-(id) initWithCLBeacon:(CLBeacon*) beacon andEventType:(NSString*) eventType { + self = [super init]; + self.uuid = beacon.UUID; + self.major = beacon.major.integerValue; + self.minor = beacon.minor.integerValue; + self.proximity = [BluetoothBLE proximityToString:beacon.proximity]; + self.accuracy = beacon.accuracy; + self.rssi = beacon.rssi; + + self.eventType = eventType; + self.ts = [DataUtils dateToTs:beacon.timestamp]; + return self; +} + +-(id) initFakeWithEventType:(NSString*) eventType { + self = [super init]; + self.uuid = [NSUUID UUID].UUIDString; + self.major = 4538; + self.minor = 1256; + self.proximity = [BluetoothBLE proximityToString:CLProximityNear]; + self.accuracy = 100; + self.rssi = 10; + + self.eventType = eventType; + self.ts = [DataUtils dateToTs:[NSDate now]]; + return self; +} + ++ (NSString*) proximityToString:(CLProximity) proximityObj { + if (proximityObj == CLProximityImmediate) { return @"ProximityImmediate"; }; + if (proximityObj == CLProximityNear) { return @"ProximityNear"; }; + if (proximityObj == CLProximityFar) { return @"ProximityFar"; }; + return @"ProximityUnknown"; +} + + +@end From 084667874a829624251f4b964505aaeee9f236b2 Mon Sep 17 00:00:00 2001 From: Shankari Date: Sat, 13 Apr 2024 13:53:20 -0700 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A4=A1=20Implement=20the=20testing=20?= =?UTF-8?q?hack=20as=20a=20mock=20method=20in=20the=20data=20collection=20?= =?UTF-8?q?plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We created a testing hack to test the previous read/write native changes. https://github.com/e-mission/e-mission-phone/pull/1144#issuecomment-2053705589 We now move that hack to the plugin interface and make it accessible externally. That makes it easier to call from the UI for better mocking. Concretely, we can modify https://github.com/e-mission/e-mission-phone/pull/1144/commits/7cb388227e52369725ab4c20c5692d433dbc6417 to call the interface, which will write sensor data using native code. This allows us to simulate the actual working of the system (writing native code) through the UI Summary of changes: - Create a new `mockBLEObjects` method in the interface - Implement it on both android and iOS - Change the fake object implementaton in the BluetoothBLE wrapper to take more arguments to that they can be passed in from the UI Testing done: https://github.com/e-mission/e-mission-phone/pull/1144/commits/463c2a5b642087e3901c995f8a57d4287d802067 --- src/android/DataCollectionPlugin.java | 30 ++++++++++++++++ src/android/wrapper/BluetoothBLE.java | 23 +++++++------ src/ios/BEMDataCollection.h | 1 + src/ios/BEMDataCollection.m | 49 +++++++++++++++++++++++++++ src/ios/Wrapper/BluetoothBLE.h | 2 +- src/ios/Wrapper/BluetoothBLE.m | 20 ++++++----- www/datacollection.js | 9 +++++ 7 files changed, 115 insertions(+), 19 deletions(-) diff --git a/src/android/DataCollectionPlugin.java b/src/android/DataCollectionPlugin.java index 1d2d0f75..c0b45d20 100644 --- a/src/android/DataCollectionPlugin.java +++ b/src/android/DataCollectionPlugin.java @@ -36,9 +36,11 @@ import edu.berkeley.eecs.emission.cordova.tracker.wrapper.ConsentConfig; import edu.berkeley.eecs.emission.cordova.tracker.wrapper.LocationTrackingConfig; import edu.berkeley.eecs.emission.cordova.tracker.wrapper.StatsEvent; +import edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE; import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlForegroundDelegate; import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log; import edu.berkeley.eecs.emission.cordova.usercache.BuiltinUserCache; +import edu.berkeley.eecs.emission.cordova.usercache.UserCacheFactory; public class DataCollectionPlugin extends CordovaPlugin { public static final String TAG = "DataCollectionPlugin"; @@ -193,6 +195,34 @@ public void run() { } }); return true; + } else if (action.equals("mockBLEObjects")) { + // we want to run this in a background thread because it might sometimes wait to get + // the current location + final String eventType = data.getString(0); + final String uuid = data.getString(1); + final int major = data.getInt(2); + final int minor = data.getInt(3); + final int nObjects = data.getInt(4); + cordova.getThreadPool().execute(new Runnable() { + @Override + public void run() { + Context ctxt = cordova.getActivity(); + for (int i = 0; i < nObjects; i++) { + BluetoothBLE currWrapper = BluetoothBLE.initFake(eventType, uuid, major, minor); + UserCacheFactory.getUserCache(ctxt).putSensorData(R.string.key_usercache_bluetooth_ble, + currWrapper); + } + BluetoothBLE[] justAddedEntries = UserCacheFactory.getUserCache(ctxt).getLastSensorData( + R.string.key_usercache_bluetooth_ble, nObjects, BluetoothBLE.class); + for(BluetoothBLE currEntry : justAddedEntries) { + if (!currEntry.getEventType().equals(eventType)) { + callbackContext.error(currEntry.getEventType()+ " found in last "+nObjects+" objects, expected all "+eventType); + } + callbackContext.success(eventType); + } + } + }); + return true; } else if (action.equals("handleSilentPush")) { throw new UnsupportedOperationException("silent push handling not supported for android"); } else if (action.equals("getAccuracyOptions")) { diff --git a/src/android/wrapper/BluetoothBLE.java b/src/android/wrapper/BluetoothBLE.java index f8366f59..c45391f7 100644 --- a/src/android/wrapper/BluetoothBLE.java +++ b/src/android/wrapper/BluetoothBLE.java @@ -1,7 +1,5 @@ package edu.berkeley.eecs.emission.cordova.tracker.wrapper; -import java.util.UUID; - /** * Created by shankari on 3/30/24. */ @@ -78,17 +76,22 @@ public static BluetoothBLE initRangeUpdate(String uuid, double ts, return rangeEvent; } - public static BluetoothBLE initFakeWithEventType(String eventType) { + public static BluetoothBLE initFake(String eventType, String uuid, int major, int minor) { BluetoothBLE fakeEvent = new BluetoothBLE(); - fakeEvent.uuid = UUID.randomUUID().toString(); - fakeEvent.major = 4538; - fakeEvent.minor = 1256; - fakeEvent.proximity = "ProximityNear"; - fakeEvent.accuracy = 100; - fakeEvent.rssi = 10; - + fakeEvent.uuid = uuid; fakeEvent.eventType = eventType; fakeEvent.ts = System.currentTimeMillis() / 1000; // time is in seconds for us + + // we assume that we don't have major and minor entries for the + // "monitor" responses + if (eventType == "RANGE_UPDATE") { + fakeEvent.major = major; + fakeEvent.minor = minor; + fakeEvent.proximity = "ProximityNear"; + fakeEvent.accuracy = (int)(Math.random() * 100); + fakeEvent.rssi = (int)(Math.random() * 10); + } + return fakeEvent; } } diff --git a/src/ios/BEMDataCollection.h b/src/ios/BEMDataCollection.h index eb1a4e4e..92e621cb 100644 --- a/src/ios/BEMDataCollection.h +++ b/src/ios/BEMDataCollection.h @@ -20,6 +20,7 @@ - (void) setConfig:(CDVInvokedUrlCommand*)command; - (void) getState:(CDVInvokedUrlCommand*)command; - (void) forceTransition:(CDVInvokedUrlCommand *)command; +- (void) mockBLEObjects:(CDVInvokedUrlCommand *)command; - (void) handleSilentPush:(CDVInvokedUrlCommand *)command; - (void)getAccuracyOptions:(CDVInvokedUrlCommand *)command; diff --git a/src/ios/BEMDataCollection.m b/src/ios/BEMDataCollection.m index 954553b5..4b486704 100644 --- a/src/ios/BEMDataCollection.m +++ b/src/ios/BEMDataCollection.m @@ -1,6 +1,7 @@ #import "BEMDataCollection.h" #import "LocalNotificationManager.h" #import "Wrapper/LocationTrackingConfig.h" +#import "Wrapper/BluetoothBLE.h" #import "BEMAppDelegate.h" #import "ConfigManager.h" #import "DataUtils.h" @@ -301,6 +302,54 @@ - (void)forceTransition:(CDVInvokedUrlCommand *)command messageAsString:msg]; [self.commandDelegate sendPluginResult:result callbackId:callbackId]; } + @catch (NSException *exception) { + NSString* msg = [NSString stringWithFormat: @"While getting settings, error %@", exception]; + CDVPluginResult* result = [CDVPluginResult + resultWithStatus:CDVCommandStatus_ERROR + messageAsString:msg]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } +} + +- (void)mockBLEObjects:(CDVInvokedUrlCommand *)command +{ + NSString* callbackId = [command callbackId]; + + @try { + NSString* eventType = [[command arguments] objectAtIndex:0]; + NSString* uuid = [[command arguments] objectAtIndex:1]; + int major = [[[command arguments] objectAtIndex:2] intValue]; + int minor = [[[command arguments] objectAtIndex:3] intValue]; + int nObjects = [[[command arguments] objectAtIndex:4] intValue]; + for (int i = 0; i < nObjects; i++) { + BluetoothBLE* currWrapper = [[BluetoothBLE new] initFake:@"RANGE_UPDATE" anduuid: uuid andmajor: major andminor: minor]; + [[BuiltinUserCache database] putSensorData:@"key.usercache.bluetooth_ble" value:currWrapper]; + } + NSArray* justAddedEntries = [[BuiltinUserCache database] getLastSensorData:@"key.usercache.bluetooth_ble" + nEntries:nObjects + wrapperClass:BluetoothBLE.class]; + for (int i = 0; i < justAddedEntries.count; i++) { + BluetoothBLE* currEntry = [justAddedEntries objectAtIndex:i]; + if (![currEntry.eventType isEqualToString:eventType]) { + NSString* msg = [NSString stringWithFormat: @"%@ found in last %d objects, expected all %@", currEntry.eventType, nObjects, eventType]; + CDVPluginResult* result = [CDVPluginResult + resultWithStatus:CDVCommandStatus_ERROR + messageAsString:msg]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + } + CDVPluginResult* result = [CDVPluginResult + resultWithStatus:CDVCommandStatus_OK + messageAsString:eventType]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } + @catch (NSException *exception) { + NSString* msg = [NSString stringWithFormat: @"While getting settings, error %@", exception]; + CDVPluginResult* result = [CDVPluginResult + resultWithStatus:CDVCommandStatus_ERROR + messageAsString:msg]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } } - (void)handleSilentPush:(CDVInvokedUrlCommand *)command diff --git a/src/ios/Wrapper/BluetoothBLE.h b/src/ios/Wrapper/BluetoothBLE.h index 97e96691..e4b1d8b6 100644 --- a/src/ios/Wrapper/BluetoothBLE.h +++ b/src/ios/Wrapper/BluetoothBLE.h @@ -12,7 +12,7 @@ @interface BluetoothBLE : NSObject - (instancetype)initWithCLBeacon:(CLBeacon *)beacon andEventType:(NSString *) eventType; -- (instancetype)initFakeWithEventType:(NSString *)eventType; +- (instancetype)initFake:(NSString *)eventType anduuid:(NSString*) uuid andmajor:(int) major andminor:(int) minor; // fields from CLBeacon, modified to be easy to serialize and restore @property NSString* uuid; diff --git a/src/ios/Wrapper/BluetoothBLE.m b/src/ios/Wrapper/BluetoothBLE.m index 4f625b05..2de61cf5 100644 --- a/src/ios/Wrapper/BluetoothBLE.m +++ b/src/ios/Wrapper/BluetoothBLE.m @@ -8,6 +8,7 @@ #import "BluetoothBLE.h" #import "DataUtils.h" +#import @implementation BluetoothBLE @@ -25,17 +26,20 @@ -(id) initWithCLBeacon:(CLBeacon*) beacon andEventType:(NSString*) eventType { return self; } --(id) initFakeWithEventType:(NSString*) eventType { +-(id) initFake:(NSString*) eventType anduuid:(NSString*) uuid andmajor:(int) major andminor:(int)minor { self = [super init]; - self.uuid = [NSUUID UUID].UUIDString; - self.major = 4538; - self.minor = 1256; - self.proximity = [BluetoothBLE proximityToString:CLProximityNear]; - self.accuracy = 100; - self.rssi = 10; - + self.uuid = uuid; self.eventType = eventType; self.ts = [DataUtils dateToTs:[NSDate now]]; + + if ([eventType isEqualToString:@"RANGE_UPDATE"]) { + self.major = major; + self.minor = minor; + self.proximity = [BluetoothBLE proximityToString:CLProximityNear]; + self.accuracy = arc4random_uniform(100); + self.rssi = arc4random_uniform(10); + } + return self; } diff --git a/www/datacollection.js b/www/datacollection.js index 96ff7240..a7ef0fa4 100644 --- a/www/datacollection.js +++ b/www/datacollection.js @@ -127,6 +127,15 @@ var DataCollection = { exec(resolve, reject, "DataCollection", "forceTransition", [generalTransitionName]); }); }, + mockBLEObjects: function (eventType, uuid, major, minor, nObjects) { + // major and minor are optional, so we check if they are defined and + // put in a default value if they don't this allows us to have a nice + // external interface without running into null pointers internally + return new Promise(function(resolve, reject) { + exec(resolve, reject, "DataCollection", "mockBLEObjects", [eventType, uuid, + major? major : -1, minor? minor: -1, nObjects]); + }); + }, handleSilentPush: function() { return new Promise(function(resolve, reject) { exec(resolve, reject, "DataCollection", "handleSilentPush",