Skip to content

Commit

Permalink
Integrated the config check to determine if in fleet mode, added in B…
Browse files Browse the repository at this point in the history
…luetoothMonitoringService for future constant monitoring, and added in some permission checks
  • Loading branch information
louisg1337 committed Apr 12, 2024
1 parent f83ff3c commit 41d06f5
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 15 deletions.
6 changes: 6 additions & 0 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@
android:enabled="true"
android:exported="false">
</service>
<service
android:name="edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothMonitoringService"
android:enabled="true"
android:exported="false">
</service>
</config-file>

<framework src="com.google.code.gson:gson:2.10.1"/>
Expand Down Expand Up @@ -182,6 +187,7 @@
<source-file src="src/android/wrapper/Timer.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/wrapper"/>
<source-file src="src/android/wrapper/StatsEvent.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/wrapper"/>
<source-file src="src/android/bluetooth/BluetoothService.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/bluetooth"/>
<source-file src="src/android/bluetooth/BluetoothMonitoringService.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/bluetooth"/>
<resource-file src="res/android/statemachine.xml" target="res/values/statemachine.xml" />
<resource-file src="res/android/values/dc_strings.xml" target="res/values/dc_strings.xml"/>
</platform>
Expand Down
86 changes: 86 additions & 0 deletions src/android/bluetooth/BluetoothMonitoringService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package edu.berkeley.eecs.emission.cordova.tracker.bluetooth;

// Android imports
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

// Altbeacon imports
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.MonitorNotifier;
import org.altbeacon.beacon.Beacon;

// Other plugin imports
import edu.berkeley.eecs.emission.R;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlChecks;


public class BluetoothMonitoringService extends Service {
private static String TAG = "BluetoothMonitoringService";
private BeaconManager beaconManager;
private String uuid = "bf3df3b1-6e46-35fa-86e5-927c95dd096c";

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this, TAG, "onStartCommand called!!!!");

// Instantiate variable
beaconManager = BeaconManager.getInstanceForApplication(this);
// This line will ensure that we always get a first callback if beacon is in region when we start monitoring
// https://github.com/AltBeacon/android-beacon-library/issues/708#issuecomment-399513853
beaconManager.setRegionStatePersistenceEnabled(false);

// Start monitoring for BLE beacons
startMonitoring();

// Start sticky
return 1;
}

@Override
public IBinder onBind(Intent intent) {
return null;
}

private void startMonitoring() {
Log.d(this, TAG, "Start monitoring has been called!");

// Code to start monitoring for BLE beacons using AltBeacon library
beaconManager.addMonitorNotifier(new MonitorNotifier() {
@Override
public void didEnterRegion(Region region) {
Log.d(BluetoothMonitoringService.this, TAG, "I just saw a beacon for the first time!");
}

@Override
public void didExitRegion(Region region) {
Log.d(BluetoothMonitoringService.this, TAG, "I no longer see an beacon");
}

@Override
public void didDetermineStateForRegion(int state, Region region) {
Log.d(BluetoothMonitoringService.this, TAG, "I have just switched from seeing/not seeing beacons: "+state);
}
});

// Define our region and start monitoring
Region region = new Region(uuid, null, null, null);
beaconManager.startMonitoring(region);
Log.d(this, TAG, "Starting to monitor for our region: " + region.toString());
}

@Override
public void onDestroy() {
stopMonitoring();
super.onDestroy();
}

private void stopMonitoring() {
Log.d(this, TAG, "Stopping monitoring for the beacon.");
beaconManager.stopMonitoring(new Region(uuid, null, null, null));
beaconManager.removeAllMonitorNotifiers();
}
}
28 changes: 18 additions & 10 deletions src/android/bluetooth/BluetoothService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
import android.content.Intent;
import android.os.IBinder;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.Set;
import java.util.HashSet;

// Altbeacon imports
import org.altbeacon.beacon.BeaconManager;
import org.altbeacon.beacon.MonitorNotifier;
import org.altbeacon.beacon.Region;
import org.altbeacon.beacon.RangeNotifier;
import org.altbeacon.beacon.Beacon;
Expand All @@ -20,23 +18,33 @@
import edu.berkeley.eecs.emission.R;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.verification.SensorControlChecks;


public class BluetoothService extends Service {
private static String TAG = "BluetoothService";
private BeaconManager beaconManager;
private Set<Beacon> scanned = new HashSet<>();
private String uuid = "426c7565-4368-6172-6d42-6561636f6e73";
private Set<Beacon> scanned;
private String uuid = "bf3df3b1-6e46-35fa-86e5-927c95dd096c";

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this, TAG, "onStartCommand called!!!!");

// Define the beacon manager
// Instantiate variables
beaconManager = BeaconManager.getInstanceForApplication(this);
scanned = new HashSet<>();

// Check to see if we even have permission to scan at all
boolean bluetoothPermissions = SensorControlChecks.checkBluetoothScanningPermissions(this);

if (!bluetoothPermissions) {
// If we aren't allowed, just say we found no beacons
Log.d(this, TAG, "Wanted to scan, but no bluetooth permissions!");
this.sendBroadcast(new ExplicitIntent(this, R.string.transition_beacon_not_found));
return 1;
}

// Add in check to see if we can scan at all, otherwise we will be stuck here

// Start scanning for BLE beacons
startBeaconScan();

Expand Down Expand Up @@ -74,17 +82,17 @@ private void startBeaconScan() {
Log.d(this, TAG, "startBeaconScan called!!!!");

// Keep track of how many times we have scanned

beaconManager.addRangeNotifier(new RangeNotifier() {
int numScans = 0;

@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.d(BluetoothService.this, TAG, "Range notifier called...");

if (beacons.size() > 0) {
Log.d(BluetoothService.this, TAG, "The first beacon I see is about "+beacons.iterator().next().getDistance()+" meters away.");
Log.d(BluetoothService.this, TAG, "Found beacons " + beacons.toString());

for (Beacon beacon : beacons) {
Log.d(BluetoothService.this, TAG, beacon.toString());
// Even though we are scanning for beacons in a certain region, beacons with different UUID's still come up.
// Until we figure out why that is the case, put this check in place so we only save the ones with the right UUID.
if (beacon.getId1().toString().equals(uuid)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.StatsEvent;
import edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothService;
import edu.berkeley.eecs.emission.cordova.tracker.bluetooth.BluetoothMonitoringService;
import edu.berkeley.eecs.emission.cordova.usercache.BuiltinUserCache;

import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
Expand Down Expand Up @@ -85,6 +86,10 @@ public int onStartCommand(Intent intent, int flags, int startId) {
Intent bluetoothService = new Intent(this, BluetoothService.class);
this.startService(bluetoothService);
return START_STICKY;
} else if (intent.getAction() != null && intent.getAction().equals("foreground_start_bluetooth_monitoring")) {
Intent bluetoothService = new Intent(this, BluetoothMonitoringService.class);
this.startService(bluetoothService);
return START_STICKY;
}

String message = humanizeState(this, TripDiaryStateMachineService.getState(this));
Expand Down
40 changes: 35 additions & 5 deletions src/android/location/TripDiaryStateMachineService.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class TripDiaryStateMachineService extends Service {
private String mTransition = null;
private SharedPreferences mPrefs = null;
private ForegroundServiceComm mComm = null;
private JSONObject config;

public TripDiaryStateMachineService() {
super();
Expand All @@ -61,6 +62,12 @@ public void onCreate() {
/*
* Need to initialize once per create.
*/
try {
JSONObject c = (JSONObject) UserCacheFactory.getUserCache(this).getDocument("config/app_ui_config", false);
config = c;
} catch (JSONException e) {
Log.d(this, TAG, "Error reading config! " + e);
}
}

@Override
Expand Down Expand Up @@ -202,6 +209,21 @@ private void handleStart(final Context ctxt, String actionString) {
// create the significant motion callback first since it is
// synchronous and the geofence is not
createGeofenceInThread(ctxt, actionString);

// // Comment this out for now as this interferes with our beacon ranging function
// // Check to see if we are in fleet mode, if we are then start monitoring for beacons
// try {
// if (config != null && config.has("tracking") && config.getJSONObject("tracking").getBoolean("bluetooth_only")){
// // Send intent to foreground service to start monitoring service
// Log.d(this, TAG, "Starting the bluetooth monitoring!");
// Intent foregroundBluetoothMonitoring = new Intent(ctxt, TripDiaryStateMachineForegroundService.class);
// foregroundBluetoothMonitoring.setAction("foreground_start_bluetooth_monitoring");
// ctxt.startService(foregroundBluetoothMonitoring);
// }
// } catch (JSONException e) {
// Log.d(this, TAG, "Error trying to read config to monitor for beacons!");
// }

return;
// we will wait for async geofence creation to complete
}
Expand Down Expand Up @@ -243,11 +265,19 @@ public void handleTripStart(Context ctxt, final String actionString) {
// If we are just normally exiting the geofence AND fleet mode, then transition to checking for beacons.
if (actionString.equals(ctxt.getString(R.string.transition_beacon_found))){
Log.d(this, TAG, "In handleTripStart, beacon has been found so continue.");
} else if (true){ // Change the true to fleet mode config check
Log.d(this, TAG, "Detected exited_geofence, but are in fleet mode so redirect to check for beacons!");
// Transition to checking for beacon state
ctxt.sendBroadcast(new ExplicitIntent(ctxt, R.string.transition_checking_for_beacon));
return;
} else {
// If we are in fleet mode, redirect to scan for beacons
try {
if (config != null && config.has("tracking") && config.getJSONObject("tracking").getBoolean("bluetooth_only")){
Log.d(this, TAG, "Detected exited_geofence, but are in fleet mode so redirect to check for beacons!");

// Transition to checking for beacon state
ctxt.sendBroadcast(new ExplicitIntent(ctxt, R.string.transition_checking_for_beacon));
return;
}
} catch (JSONException e) {
Log.d(this, TAG, "Error trying to read config to check for beacons.");
}
}

// Delete geofence
Expand Down
11 changes: 11 additions & 0 deletions src/android/verification/SensorControlChecks.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.location.Location;
import android.os.Build;
import android.os.PowerManager;
import android.content.pm.PackageManager;

import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationManagerCompat;
Expand Down Expand Up @@ -130,4 +131,14 @@ public static boolean checkIgnoreBatteryOptimizations(final Context ctxt) {
PowerManager pm = (PowerManager)ctxt.getSystemService(Context.POWER_SERVICE);
return pm.isIgnoringBatteryOptimizations(ctxt.getPackageName());
}

public static boolean checkBluetoothScanningPermissions(final Context ctxt) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.S){
// If its an older build version than API 31, we don't have to worry about scanning permissions
return true;
} else {
int permissionState = ctxt.checkSelfPermission("android.permission.BLUETOOTH_SCAN");
return permissionState == PackageManager.PERMISSION_GRANTED;
}
}
}
5 changes: 5 additions & 0 deletions src/android/verification/SensorControlForegroundDelegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ public void checkAndPromptLocationPermissions(CallbackContext cordovaCallback) {
}
}

/**
* Check to see if the user has the ability to scan for bluetooth devices, if not prompt them asking for it.
*
* @param cordovaCallback
*/
public void checkAndPromptBluetoothScanPermissions(CallbackContext cordovaCallback) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.S){
Log.d(cordova.getActivity(), TAG, "Older build version than API 31, return success!");
Expand Down

0 comments on commit 41d06f5

Please sign in to comment.