Implemented background telemetry
This commit is contained in:
parent
ecf13e0041
commit
19cacd0e86
|
@ -10,7 +10,7 @@ source.exclude_patterns = app_storage/*,venv/*,Makefile,./Makefil*,requirements,
|
|||
|
||||
version.regex = __version__ = ['"](.*)['"]
|
||||
version.filename = %(source.dir)s/main.py
|
||||
android.numeric_version = 20231114
|
||||
android.numeric_version = 20231203
|
||||
|
||||
# Cryptography recipe is currently broken, using RNS-internal crypto for now
|
||||
requirements = kivy==2.2.1,libbz2,pillow,qrcode==7.3.1,usb4a,usbserial4a
|
||||
|
@ -25,7 +25,8 @@ android.presplash_color = #00000000
|
|||
orientation = portrait
|
||||
fullscreen = 0
|
||||
|
||||
android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT,ACCESS_NETWORK_STATE,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,MANAGE_EXTERNAL_STORAGE
|
||||
android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT,ACCESS_NETWORK_STATE,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,MANAGE_EXTERNAL_STORAGE,ACCESS_BACKGROUND_LOCATION
|
||||
|
||||
android.api = 30
|
||||
android.minapi = 24
|
||||
android.ndk = 25b
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
__debug_build__ = False
|
||||
__disable_shaders__ = False
|
||||
__version__ = "0.7.6"
|
||||
__version__ = "0.7.7"
|
||||
__variant__ = "beta"
|
||||
|
||||
import sys
|
||||
|
|
|
@ -30,8 +30,7 @@ if RNS.vendor.platformutils.get_platform() == "android":
|
|||
|
||||
from android import python_act
|
||||
android_api_version = autoclass('android.os.Build$VERSION').SDK_INT
|
||||
|
||||
Context = autoclass('android.content.Context')
|
||||
|
||||
Intent = autoclass('android.content.Intent')
|
||||
BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
||||
Icon = autoclass("android.graphics.drawable.Icon")
|
||||
|
@ -60,6 +59,7 @@ class SidebandService():
|
|||
0x0483: [0x5740], # ST CDC
|
||||
0x2E8A: [0x0005, 0x000A], # Raspberry Pi Pico
|
||||
}
|
||||
|
||||
def android_notification(self, title="", content="", ticker="", group=None, context_id=None):
|
||||
if android_api_version < 26:
|
||||
return
|
||||
|
@ -121,12 +121,78 @@ class SidebandService():
|
|||
built_notification = notification.build()
|
||||
self.notification_service.notify(0, built_notification)
|
||||
|
||||
def check_permission(self, permission):
|
||||
if RNS.vendor.platformutils.is_android():
|
||||
try:
|
||||
result = self.android_service.checkSelfPermission("android.permission."+permission)
|
||||
if result == 0:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while checking permission: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
return False
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
def background_location_allowed(self):
|
||||
if not RNS.vendor.platformutils.is_android():
|
||||
return False
|
||||
|
||||
perms = ["ACCESS_FINE_LOCATION","ACCESS_COARSE_LOCATION","ACCESS_BACKGROUND_LOCATION"]
|
||||
for perm in perms:
|
||||
if not self.check_permission(perm):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def android_location_callback(self, **kwargs):
|
||||
self._raw_gps = kwargs
|
||||
self._last_gps_update = time.time()
|
||||
|
||||
def should_update_location(self):
|
||||
if self.sideband.config["telemetry_enabled"] and self.sideband.config["telemetry_s_location"] and self.background_location_allowed():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def update_location_provider(self):
|
||||
if RNS.vendor.platformutils.is_android():
|
||||
if self.should_update_location():
|
||||
if not self._gps_started:
|
||||
RNS.log("Starting service location provider", RNS.LOG_DEBUG)
|
||||
|
||||
if self.gps == None:
|
||||
from plyer import gps
|
||||
self.gps = gps
|
||||
|
||||
self.gps.configure(on_location=self.android_location_callback)
|
||||
self.gps.start(minTime=self._gps_stale_time, minDistance=self._gps_min_distance)
|
||||
self._gps_started = True
|
||||
|
||||
else:
|
||||
if self._gps_started:
|
||||
RNS.log("Stopping service location provider", RNS.LOG_DEBUG)
|
||||
if self.gps != None:
|
||||
self.gps.stop()
|
||||
self._gps_started = False
|
||||
self._raw_gps = None
|
||||
|
||||
def get_location(self):
|
||||
return self._last_gps_update, self._raw_gps
|
||||
|
||||
def __init__(self):
|
||||
self.argument = environ.get('PYTHON_SERVICE_ARGUMENT', '')
|
||||
self.app_dir = self.argument
|
||||
self.multicast_lock = None
|
||||
self.wake_lock = None
|
||||
self.should_run = False
|
||||
self.gps = None
|
||||
self._gps_started = False
|
||||
self._gps_stale_time = 299
|
||||
self._gps_min_distance = 25
|
||||
self._raw_gps = None
|
||||
|
||||
self.android_service = None
|
||||
self.app_context = None
|
||||
|
@ -149,9 +215,7 @@ class SidebandService():
|
|||
|
||||
self.discover_usb_devices()
|
||||
|
||||
self.sideband = SidebandCore(self, is_service=True, android_app_dir=self.app_dir, verbose=__debug_build__)
|
||||
self.sideband.service_context = self.android_service
|
||||
self.sideband.owner_service = self
|
||||
self.sideband = SidebandCore(self, is_service=True, android_app_dir=self.app_dir, verbose=__debug_build__, owner_service=self, service_context=self.android_service)
|
||||
|
||||
if self.sideband.config["debug"]:
|
||||
Logger.setLevel(LOG_LEVELS["debug"])
|
||||
|
@ -161,6 +225,7 @@ class SidebandService():
|
|||
|
||||
if RNS.vendor.platformutils.is_android():
|
||||
RNS.log("Discovered USB devices: "+str(self.usb_devices), RNS.LOG_EXTREME)
|
||||
self.update_location_provider()
|
||||
|
||||
|
||||
def discover_usb_devices(self):
|
||||
|
|
|
@ -90,6 +90,7 @@ class SidebandCore():
|
|||
PERIODIC_JOBS_INTERVAL = 60
|
||||
PERIODIC_SYNC_RETRY = 360
|
||||
TELEMETRY_INTERVAL = 60
|
||||
SERVICE_TELEMETRY_INTERVAL = 300
|
||||
|
||||
IF_CHANGE_ANNOUNCE_MIN_INTERVAL = 6 # In seconds
|
||||
AUTO_ANNOUNCE_RANDOM_MIN = 90 # In minutes
|
||||
|
@ -103,7 +104,7 @@ class SidebandCore():
|
|||
# stream logger
|
||||
self.log_announce(destination_hash, app_data, dest_type=SidebandCore.aspect_filter)
|
||||
|
||||
def __init__(self, owner_app, is_service=False, is_client=False, android_app_dir=None, verbose=False):
|
||||
def __init__(self, owner_app, is_service=False, is_client=False, android_app_dir=None, verbose=False, owner_service=None, service_context=None):
|
||||
self.is_service = is_service
|
||||
self.is_client = is_client
|
||||
self.db = None
|
||||
|
@ -132,6 +133,8 @@ class SidebandCore():
|
|||
self.state_lock = Lock()
|
||||
self.rpc_connection = None
|
||||
self.service_stopped = False
|
||||
self.service_context = service_context
|
||||
self.owner_service = owner_service
|
||||
|
||||
self.app_dir = plyer.storagepath.get_home_dir()+"/.config/sideband"
|
||||
self.cache_dir = self.app_dir+"/cache"
|
||||
|
@ -2244,6 +2247,23 @@ class SidebandCore():
|
|||
|
||||
threading.Thread(target=telemetry_job, daemon=True).start()
|
||||
|
||||
def run_service_telemetry(self):
|
||||
if not self.telemetry_running:
|
||||
self.telemetry_running = True
|
||||
def telemetry_job():
|
||||
while self.telemetry_running:
|
||||
try:
|
||||
self.update_telemetry()
|
||||
except Exception as e:
|
||||
import traceback
|
||||
exception_info = "".join(traceback.TracebackException.from_exception(e).format())
|
||||
RNS.log(f"An {str(type(e))} occurred while updating service telemetry: {str(e)}", RNS.LOG_ERROR)
|
||||
RNS.log(exception_info, RNS.LOG_ERROR)
|
||||
|
||||
time.sleep(SidebandCore.SERVICE_TELEMETRY_INTERVAL)
|
||||
|
||||
threading.Thread(target=telemetry_job, daemon=True).start()
|
||||
|
||||
def stop_telemetry(self):
|
||||
self.telemetry_running = False
|
||||
self.telemeter.stop_all()
|
||||
|
@ -2298,7 +2318,10 @@ class SidebandCore():
|
|||
def update_telemeter_config(self):
|
||||
if self.config["telemetry_enabled"] == True:
|
||||
if self.telemeter == None:
|
||||
self.telemeter = Telemeter()
|
||||
if self.service_context == None:
|
||||
self.telemeter = Telemeter()
|
||||
else:
|
||||
self.telemeter = Telemeter(android_context=self.service_context, service=True, location_provider=self.owner_service)
|
||||
|
||||
sensors = ["location", "information", "battery", "pressure", "temperature", "humidity", "magnetic_field", "ambient_light", "gravity", "angular_velocity", "acceleration", "proximity"]
|
||||
for sensor in sensors:
|
||||
|
@ -2561,6 +2584,8 @@ class SidebandCore():
|
|||
if self.is_service or self.is_standalone:
|
||||
while True:
|
||||
time.sleep(SidebandCore.PERIODIC_JOBS_INTERVAL)
|
||||
self.owner_service.update_location_provider()
|
||||
|
||||
if self.config["lxmf_periodic_sync"] == True:
|
||||
if self.getpersistent("lxmf.lastsync") == None:
|
||||
self.setpersistent("lxmf.lastsync", time.time())
|
||||
|
@ -2660,6 +2685,8 @@ class SidebandCore():
|
|||
if self.is_standalone or self.is_client:
|
||||
if self.config["telemetry_enabled"]:
|
||||
self.run_telemetry()
|
||||
elif self.is_service:
|
||||
self.run_service_telemetry()
|
||||
|
||||
def __add_localinterface(self, delay=None):
|
||||
self.interface_local_adding = True
|
||||
|
|
|
@ -40,7 +40,7 @@ class Telemeter():
|
|||
RNS.log("An error occurred while unpacking telemetry. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
def __init__(self, from_packed=False):
|
||||
def __init__(self, from_packed=False, android_context=None, service=False, location_provider=None):
|
||||
self.sids = {
|
||||
Sensor.SID_TIME: Time,
|
||||
Sensor.SID_RECEIVED: Received,
|
||||
|
@ -80,10 +80,15 @@ class Telemeter():
|
|||
if not self.from_packed:
|
||||
self.enable("time")
|
||||
|
||||
self.location_provider = location_provider
|
||||
self.android_context = android_context
|
||||
self.service = service
|
||||
|
||||
def synthesize(self, sensor):
|
||||
if sensor in self.available:
|
||||
if not sensor in self.sensors:
|
||||
self.sensors[sensor] = self.sids[self.available[sensor]]()
|
||||
self.sensors[sensor]._telemeter = self
|
||||
self.sensors[sensor].active = True
|
||||
self.sensors[sensor].synthesized = True
|
||||
|
||||
|
@ -92,6 +97,7 @@ class Telemeter():
|
|||
if sensor in self.available:
|
||||
if not sensor in self.sensors:
|
||||
self.sensors[sensor] = self.sids[self.available[sensor]]()
|
||||
self.sensors[sensor]._telemeter = self
|
||||
if not self.sensors[sensor].active:
|
||||
self.sensors[sensor].start()
|
||||
|
||||
|
@ -150,6 +156,28 @@ class Telemeter():
|
|||
|
||||
return rendered
|
||||
|
||||
def check_permission(self, permission):
|
||||
if RNS.vendor.platformutils.is_android():
|
||||
if self.android_context != None:
|
||||
try:
|
||||
result = self.android_context.checkSelfPermission("android.permission."+permission)
|
||||
if result == 0:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
RNS.log("Error while checking permission: "+str(e), RNS.LOG_ERROR)
|
||||
|
||||
return False
|
||||
|
||||
else:
|
||||
from android.permissions import check_permission
|
||||
return check_permission("android.permission."+permission)
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
class Sensor():
|
||||
SID_NONE = 0x00
|
||||
SID_TIME = 0x01
|
||||
|
@ -169,6 +197,7 @@ class Sensor():
|
|||
SID_RECEIVED = 0x10
|
||||
|
||||
def __init__(self, sid = None, stale_time = None):
|
||||
self._telemeter = None
|
||||
self._sid = sid or Sensor.SID_NONE
|
||||
self._stale_time = stale_time
|
||||
self._data = None
|
||||
|
@ -238,6 +267,13 @@ class Sensor():
|
|||
def render(self, relative_to=None):
|
||||
return None
|
||||
|
||||
def check_permission(self, permission):
|
||||
if self._telemeter != None:
|
||||
return self._telemeter.check_permission(permission)
|
||||
else:
|
||||
from android.permissions import check_permission
|
||||
return check_permission("android.permission."+permission)
|
||||
|
||||
class Time(Sensor):
|
||||
SID = Sensor.SID_TIME
|
||||
STALE_TIME = 0.1
|
||||
|
@ -587,6 +623,7 @@ class Location(Sensor):
|
|||
self._last_update = None
|
||||
self._min_distance = Location.MIN_DISTANCE
|
||||
self._accuracy_target = Location.ACCURACY_TARGET
|
||||
self._query_method = None
|
||||
|
||||
self.latitude = None
|
||||
self.longitude = None
|
||||
|
@ -619,12 +656,15 @@ class Location(Sensor):
|
|||
|
||||
def setup_sensor(self):
|
||||
if RNS.vendor.platformutils.is_android():
|
||||
from android.permissions import request_permissions, check_permission
|
||||
|
||||
if check_permission("android.permission.ACCESS_COARSE_LOCATION") and check_permission("android.permission.ACCESS_FINE_LOCATION"):
|
||||
self.gps.configure(on_location=self.android_location_callback)
|
||||
self.gps.start(minTime=self._stale_time, minDistance=self._min_distance)
|
||||
|
||||
if self._telemeter and self._telemeter.service:
|
||||
self._location_provider = self._telemeter.location_provider
|
||||
else:
|
||||
if self.check_permission("ACCESS_COARSE_LOCATION") and self.check_permission("ACCESS_FINE_LOCATION"):
|
||||
self.gps.configure(on_location=self.android_location_callback)
|
||||
self.gps.start(minTime=self._stale_time, minDistance=self._min_distance)
|
||||
self._location_provider = self
|
||||
|
||||
self.update_data()
|
||||
|
||||
def teardown_sensor(self):
|
||||
|
@ -669,33 +709,37 @@ class Location(Sensor):
|
|||
}
|
||||
|
||||
elif RNS.vendor.platformutils.is_android():
|
||||
if "lat" in self._raw:
|
||||
self.latitude = self._raw["lat"]
|
||||
if "lon" in self._raw:
|
||||
self.longitude = self._raw["lon"]
|
||||
if "altitude" in self._raw:
|
||||
self.altitude = self._raw["altitude"]
|
||||
if "speed" in self._raw:
|
||||
self.speed = self._raw["speed"]
|
||||
if self.speed < 0:
|
||||
self.speed = 0
|
||||
if "bearing" in self._raw:
|
||||
self.bearing = self._raw["bearing"]
|
||||
if "accuracy" in self._raw:
|
||||
self.accuracy = self._raw["accuracy"]
|
||||
if self.accuracy < 0:
|
||||
self.accuracy = 0
|
||||
if self._location_provider != None:
|
||||
if self._location_provider != self:
|
||||
self._last_update, self._raw = self._location_provider.get_location()
|
||||
|
||||
if self.accuracy != None and self.accuracy <= self._accuracy_target:
|
||||
self.data = {
|
||||
"latitude": round(self.latitude, 6),
|
||||
"longitude": round(self.longitude, 6),
|
||||
"altitude": round(self.altitude, 2),
|
||||
"speed": round(self.speed, 2),
|
||||
"bearing": round(self.bearing, 2),
|
||||
"accuracy": round(self.accuracy, 2),
|
||||
"last_update": int(self._last_update),
|
||||
}
|
||||
if "lat" in self._raw:
|
||||
self.latitude = self._raw["lat"]
|
||||
if "lon" in self._raw:
|
||||
self.longitude = self._raw["lon"]
|
||||
if "altitude" in self._raw:
|
||||
self.altitude = self._raw["altitude"]
|
||||
if "speed" in self._raw:
|
||||
self.speed = self._raw["speed"]
|
||||
if self.speed < 0:
|
||||
self.speed = 0
|
||||
if "bearing" in self._raw:
|
||||
self.bearing = self._raw["bearing"]
|
||||
if "accuracy" in self._raw:
|
||||
self.accuracy = self._raw["accuracy"]
|
||||
if self.accuracy < 0:
|
||||
self.accuracy = 0
|
||||
|
||||
if self.accuracy != None and self.accuracy <= self._accuracy_target:
|
||||
self.data = {
|
||||
"latitude": round(self.latitude, 6),
|
||||
"longitude": round(self.longitude, 6),
|
||||
"altitude": round(self.altitude, 2),
|
||||
"speed": round(self.speed, 2),
|
||||
"bearing": round(self.bearing, 2),
|
||||
"accuracy": round(self.accuracy, 2),
|
||||
"last_update": int(self._last_update),
|
||||
}
|
||||
|
||||
except:
|
||||
self.data = None
|
||||
|
|
Loading…
Reference in New Issue