1
1
package github .umer0586 .sensorserver ;
2
2
3
+ import android .Manifest ;
4
+ import android .annotation .SuppressLint ;
3
5
import android .content .Context ;
6
+ import android .content .pm .PackageManager ;
4
7
import android .hardware .Sensor ;
5
8
import android .hardware .SensorEvent ;
6
9
import android .hardware .SensorEventListener ;
7
10
import android .hardware .SensorManager ;
11
+ import android .location .Location ;
12
+ import android .location .LocationListener ;
13
+ import android .location .LocationManager ;
8
14
import android .net .Uri ;
15
+ import android .os .Build ;
9
16
import android .os .Handler ;
10
17
import android .os .HandlerThread ;
11
18
import android .util .Log ;
12
19
20
+ import androidx .annotation .NonNull ;
21
+
13
22
import org .java_websocket .WebSocket ;
14
23
import org .java_websocket .handshake .ClientHandshake ;
15
24
import org .java_websocket .server .WebSocketServer ;
25
34
import github .umer0586 .util .SensorUtil ;
26
35
27
36
28
- public class SensorWebSocketServer extends WebSocketServer implements SensorEventListener {
37
+ public class SensorWebSocketServer extends WebSocketServer implements SensorEventListener , LocationListener {
29
38
30
39
private static final String TAG = SensorWebSocketServer .class .getName ();
31
40
41
+
42
+ private Context context ;
43
+
32
44
private int samplingRate = 200000 ;//default value normal rate
33
45
private static final String CONNECTION_PATH_SINGLE_SENSOR = "/sensor/connect" ;
34
46
private static final String CONNECTION_PATH_MULTIPLE_SENSORS = "/sensors/connect" ;
47
+ private static final String CONNECTION_PATH_LOCATION = "/location" ;
48
+
35
49
private static final HashMap <String , Object > response = new HashMap <>();
36
50
37
51
private HandlerThread handlerThread ;
@@ -56,17 +70,22 @@ public class SensorWebSocketServer extends WebSocketServer implements SensorEven
56
70
//websocket close codes ranging 4000 - 4999 are for application's custom messages
57
71
public static final int CLOSE_CODE_SENSOR_NOT_FOUND = 4001 ;
58
72
public static final int CLOSE_CODE_UNSUPPORTED_REQUEST = 4002 ;
59
- public static final int CLOSE_CODE_TYPE_PARAMETER_MISSING = 4003 ;
73
+ public static final int CLOSE_CODE_PARAMETER_MISSING = 4003 ;
60
74
public static final int CLOSE_CODE_SERVER_STOPPED = 4004 ;
61
75
public static final int CLOSE_CODE_CONNECTION_CLOSED_BY_APP_USER = 4005 ;
62
76
public static final int CLOSE_CODE_INVALID_JSON_ARRAY = 4006 ;
63
77
public static final int CLOSE_CODE_TOO_FEW_SENSORS = 4007 ;
64
78
public static final int CLOSE_CODE_NO_SENSOR_SPECIFIED = 4008 ;
79
+ public static final int CLOSE_CODE_PERMISSION_DENIED = 4009 ;
80
+ public static final int CLOSE_CODE_INVALID_PROVIDER = 4010 ;
81
+ public static final int CLOSE_CODE_PROVIDER_NOT_FOUND = 4011 ;
82
+
65
83
66
84
67
85
public SensorWebSocketServer (Context context , InetSocketAddress address )
68
86
{
69
87
super (address );
88
+ this .context = context ;
70
89
sensorManager = (SensorManager ) context .getSystemService (context .SENSOR_SERVICE );
71
90
sensorUtil = SensorUtil .getInstance (context );
72
91
registeredSensors = new ArrayList <>();
@@ -84,23 +103,17 @@ public void onOpen(WebSocket clientWebsocket, ClientHandshake handshake)
84
103
85
104
// ws://host:port/sensor/connect?type=<sensorType>
86
105
if (uri .getPath ().equalsIgnoreCase (CONNECTION_PATH_SINGLE_SENSOR ))
87
- {
88
- Log .i (TAG , "param type " + uri .getQueryParameter ("type" ));
89
106
handleSingleSensorRequest (uri , clientWebsocket );
107
+ //ws://host:port/sensors/connect?types=["type1","type2"...]
108
+ else if (uri .getPath ().equalsIgnoreCase (CONNECTION_PATH_MULTIPLE_SENSORS ))
109
+ handleMultiSensorRequest (uri , clientWebsocket );
90
110
91
- //ws://host:port/sensors/connect?types=["type1","type2"...]
92
- } else if (uri . getPath (). equalsIgnoreCase ( CONNECTION_PATH_MULTIPLE_SENSORS ))
93
- {
94
- Log . i ( TAG , "param types " + uri . getQueryParameter ( "types" ));
95
- handleMultiSensorRequest ( uri , clientWebsocket );
111
+ else if ( uri . getPath (). equalsIgnoreCase ( CONNECTION_PATH_LOCATION ))
112
+ handleLocationRequest (uri , clientWebsocket );
113
+
114
+ else
115
+ clientWebsocket . close ( CLOSE_CODE_UNSUPPORTED_REQUEST , "unsupported request" );
96
116
97
- } else
98
- {
99
- String errorMessage = "Unsupported request \n , " +
100
- "use " + CONNECTION_PATH_SINGLE_SENSOR + "?type=<sensorType> for single sensor on single websocket connection and \n " +
101
- " " + CONNECTION_PATH_MULTIPLE_SENSORS + "?types=[\" type1\" ,\" type2\" , . . . ] for multiple sensors on single websocket connection" ;
102
- clientWebsocket .close (CLOSE_CODE_UNSUPPORTED_REQUEST , errorMessage );
103
- }
104
117
105
118
}
106
119
@@ -112,7 +125,7 @@ private void handleMultiSensorRequest(Uri uri, WebSocket clientWebsocket)
112
125
{
113
126
if (uri .getQueryParameter ("types" ) == null )
114
127
{
115
- clientWebsocket .close (CLOSE_CODE_TYPE_PARAMETER_MISSING ,"<Types> parameter required" );
128
+ clientWebsocket .close (CLOSE_CODE_PARAMETER_MISSING ,"<Types> parameter required" );
116
129
return ;
117
130
}
118
131
List <String > requestedSensorTypes = JsonUtil .readJSONArray (uri .getQueryParameter ("types" ));
@@ -167,7 +180,7 @@ private void handleSingleSensorRequest(Uri uri, WebSocket clientWebsocket)
167
180
//if type param doesn't exit in the query
168
181
if (paramType == null )
169
182
{
170
- clientWebsocket .close (CLOSE_CODE_TYPE_PARAMETER_MISSING ,"<type> param required" );
183
+ clientWebsocket .close (CLOSE_CODE_PARAMETER_MISSING ,"<type> param required" );
171
184
//do not proceed further
172
185
return ;
173
186
}
@@ -307,6 +320,158 @@ Use requestTriggerSensor(android.hardware.TriggerEventListener, android.hardware
307
320
}
308
321
}
309
322
323
+ @ SuppressLint ("MissingPermission" )
324
+ private void handleLocationRequest (Uri uri ,WebSocket clientWebsocket )
325
+ {
326
+ LocationManager locationManager = (LocationManager )context .getSystemService (Context .LOCATION_SERVICE );
327
+
328
+ if (!hasLocationPermission ())
329
+ {
330
+ clientWebsocket .close (CLOSE_CODE_PERMISSION_DENIED ,"App has No permission to access location" );
331
+ return ;
332
+ }
333
+
334
+ String provider = uri .getQueryParameter ("provider" );
335
+ Log .i (TAG , "handleLocationRequest() : provider = " + provider );
336
+
337
+ if (provider == null )
338
+ {
339
+ clientWebsocket .close (CLOSE_CODE_PARAMETER_MISSING ,"Provider parameter required" );
340
+ return ;
341
+ }
342
+
343
+
344
+ if (provider .equalsIgnoreCase ("network" ))
345
+ {
346
+ // if device supports network location provider
347
+ if (locationManager .getAllProviders ().contains (LocationManager .NETWORK_PROVIDER ))
348
+ {
349
+ //If some clients already connected for network location provider then no need to register this server for location updates for Network provider again
350
+ if (!locationProviderHasConnections (LocationManager .NETWORK_PROVIDER ))
351
+ locationManager .requestLocationUpdates (LocationManager .NETWORK_PROVIDER , 0 , 0 , this , handlerThread .getLooper ());
352
+
353
+ clientWebsocket .setAttachment (new LocationRequestInfo (LocationManager .NETWORK_PROVIDER ));
354
+
355
+ }// if device does not support network location provider
356
+ else
357
+ clientWebsocket .close (CLOSE_CODE_PROVIDER_NOT_FOUND ,"network provider not found" );
358
+ }
359
+
360
+ else if (provider .equalsIgnoreCase ("GPS" ))
361
+ {
362
+ // if device supports GPS location provider
363
+ if (locationManager .getAllProviders ().contains (LocationManager .GPS_PROVIDER ))
364
+ {
365
+ //If some clients already connected for GPS location provider then no need to register this server for location updates for GPS provider again
366
+ if (!locationProviderHasConnections (LocationManager .GPS_PROVIDER ))
367
+ locationManager .requestLocationUpdates (LocationManager .GPS_PROVIDER , 0 , 0 , this , handlerThread .getLooper ());
368
+
369
+ clientWebsocket .setAttachment (new LocationRequestInfo (LocationManager .GPS_PROVIDER ));
370
+ }
371
+ else // if device does not support GPS location provider
372
+ clientWebsocket .close (CLOSE_CODE_PROVIDER_NOT_FOUND ,"network provider not found" );
373
+ }
374
+ else
375
+ clientWebsocket .close (CLOSE_CODE_INVALID_PROVIDER ,"Provider must be either GPS or network" );
376
+
377
+
378
+ notifyConnectionsChanged ();
379
+
380
+ }
381
+
382
+ private boolean locationProviderHasConnections (String provider )
383
+ {
384
+ int providerConnectionCount = 0 ;
385
+
386
+ for (WebSocket websocket : getConnections ())
387
+ {
388
+ if ( websocket .getAttachment () instanceof LocationRequestInfo )
389
+ {
390
+ LocationRequestInfo locationRequestInfo = websocket .getAttachment ();
391
+ if (locationRequestInfo .getProvider ().equals (provider ))
392
+ providerConnectionCount ++;
393
+ }
394
+ }
395
+
396
+ Log .i (TAG , "connection counts for " + provider + " location provider " + providerConnectionCount );
397
+ return providerConnectionCount > 0 ;
398
+ }
399
+
400
+ @ Override
401
+ public void onLocationChanged (@ NonNull Location location )
402
+ {
403
+
404
+ if (!locationProviderHasConnections (location .getProvider ()))
405
+ {
406
+ Log .w (TAG , "onLocationChanged() : " + "Location update received when no client with " + location .getProvider () + " connected" );
407
+ }
408
+
409
+ for (WebSocket websocket : getConnections ())
410
+ {
411
+ if (websocket .getAttachment () instanceof LocationRequestInfo )
412
+ {
413
+ response .clear ();
414
+ response .put ("longitude" ,location .getLongitude ());
415
+ response .put ("latitude" ,location .getLatitude ());
416
+ response .put ("altitude" ,location .getAltitude ());
417
+ response .put ("bearing" ,location .getBearing ());
418
+ response .put ("accuracy" ,location .getAccuracy ());
419
+ response .put ("speed" ,location .getSpeed ());
420
+ response .put ("time" ,location .getTime ());
421
+ response .put ("provider" ,location .getProvider ());
422
+
423
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O )
424
+ {
425
+ response .put ("speedAccuracyMetersPerSecond" ,location .getSpeedAccuracyMetersPerSecond ());
426
+ response .put ("bearingAccuracyDegrees" ,location .getBearingAccuracyDegrees ());
427
+ response .put ("elapsedRealtimeNanos" ,location .getElapsedRealtimeNanos ());
428
+ response .put ("verticalAccuracyMeters" ,location .getVerticalAccuracyMeters ());
429
+
430
+ }
431
+
432
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU )
433
+ {
434
+ response .put ("elapsedRealtimeAgeMillis" ,location .getElapsedRealtimeAgeMillis ());
435
+ }
436
+
437
+
438
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q )
439
+ {
440
+ response .put ("time" ,location .getElapsedRealtimeUncertaintyNanos ());
441
+ }
442
+
443
+
444
+ LocationRequestInfo locationRequestInfo = websocket .getAttachment ();
445
+
446
+ if (locationRequestInfo .getProvider ().equals (location .getProvider ()))
447
+ websocket .send ( JsonUtil .toJSON (response ) );
448
+ }
449
+ }
450
+ }
451
+
452
+ @ Override
453
+ public void onProviderDisabled (@ NonNull String provider )
454
+ {
455
+ Log .i (TAG , "onProviderDisabled() " + provider );
456
+ }
457
+
458
+ @ Override
459
+ public void onProviderEnabled (@ NonNull String provider )
460
+ {
461
+ Log .i (TAG , "onProviderEnabled() " + provider );
462
+ }
463
+
464
+ private boolean hasLocationPermission ()
465
+ {
466
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M )
467
+ {
468
+ return context .checkSelfPermission (Manifest .permission .ACCESS_FINE_LOCATION ) == PackageManager .PERMISSION_GRANTED ;
469
+ }
470
+
471
+ //prior to android marshmallow dangerous permission are prompt at install time
472
+ return true ;
473
+ }
474
+
310
475
@ Override
311
476
public void onClose (WebSocket clientWebsocket , int code , String reason , boolean remote )
312
477
{
@@ -325,6 +490,19 @@ else if (clientWebsocket.getAttachment() instanceof ArrayList)
325
490
for (Sensor sensor : sensors )
326
491
unregisterSensor (sensor );
327
492
}
493
+ else if (clientWebsocket .getAttachment () instanceof LocationRequestInfo )
494
+ {
495
+
496
+ LocationManager locationManager = (LocationManager ) context .getSystemService (Context .LOCATION_SERVICE );
497
+ LocationRequestInfo locationRequestInfo = clientWebsocket .getAttachment ();
498
+
499
+ // unregister this server for location updates from specific provider ...
500
+ // when there are no clients associated with that provider
501
+ if (!locationProviderHasConnections (locationRequestInfo .getProvider ()))
502
+ locationManager .removeUpdates (this );
503
+
504
+ }
505
+ notifyConnectionsChanged ();
328
506
329
507
330
508
}
@@ -423,6 +601,9 @@ public void onStart()
423
601
public void stop () throws IOException , InterruptedException
424
602
{
425
603
closeAllConnections ();
604
+ LocationManager locationManager = (LocationManager )context .getSystemService (Context .LOCATION_SERVICE );
605
+ locationManager .removeUpdates (this );
606
+
426
607
super .stop ();
427
608
Log .d (TAG , "stop() called" );
428
609
0 commit comments