I've got an Android foreground service that uses voice recognition and geolocation. Xamarin doesn't help with voice recognition but I'm using Essentials built-in support for geolocation. I've put these in a foreground service because they need to run for several hours while in the background.
The service is started while the app is in the foreground and the voice recognition part of the service works fine. However, the geolocation stops working at random times. Sometimes it stays working for 60 minutes, and sometimes for just 5 minutes. The call to Geolocation.GetLocationAsync just hangs. I finally implemented a timer so I can tell the user to turn on the phone. This gets geolocation working again...for a while.
I thought I understood from this, https://developer.android.com/about/versions/oreo/background-location-limits, that geolocation would be available as long as the service was running.
Relevant code:
private CancellationTokenSource cts;
private bool CancelFlag = false;
//Initialize location
public void LocationServicesInit()
{
CancelFlag = false;
cts = null;
LocationTimer = null;
}
private void StartLocationTimer( double NewInterval )
{
if (LocationTimer == null)
{
LocationTimer = new System.Timers.Timer();
LocationTimer.Elapsed += LocationTimer_Elapsed;
}
LocationTimer.Interval = NewInterval;
LocationTimer.Enabled = true;
}
private void StopLocationTimer()
{
if (LocationTimer != null)
LocationTimer.Enabled = false;
}
private void LocationTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
GlobalVariables.LogMessage.RaiseLogMessageEvent("Location loop has unexpectedly been terminated or is stuck and needs to be restarted.");
_ = Xamarin.Essentials.TextToSpeech.SpeakAsync("Location loop has unexpectedly been terminated or is stuck and needs to be restarted.");
}
public void StopLocationLoop()
{
GlobalVariables.LogMessage.RaiseLogMessageEvent("Location measurment stopped.");
StopLocationTimer();
CancelFlag = true;
if (cts != null)
cts.Cancel();
}
const double YardsPerMile = 1760.0;
/// <summary>
/// Continuously request location info until cancelled.
/// </summary>
/// <param name="CurrentPositionData"></param>
/// <returns></returns>
public async Task StartLocationLoop(ESPosition KeepCurrentPos)
{
const double LocationTime = 10.0;
const double LocationTimerInterval = LocationTime * 1500;
int LocationStuckCount;
ESPosition CurrentPos;
GlobalVariables.LogMessage.RaiseLogMessageEvent("Location measurment started.");
GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(LocationTimerInterval));
CancelFlag = false;
LocationStuckCount = 0;
StartLocationTimer(LocationTimerInterval);
while (!CancelFlag)
{
CurrentPos = await GetCurrentLocation(request);
if (CurrentPos != null)
{
LocationStuckCount = 0;
KeepCurrentPos.CopyHere(CurrentPos);
KeepCurrentPos.IncrementCount();
}
else
++LocationStuckCount;
if( LocationStuckCount > 10 )
{
GlobalVariables.LogMessage.RaiseLogMessageEvent("Location has stopped working and needs to be restarted.");
_ = Xamarin.Essentials.TextToSpeech.SpeakAsync("Location has stopped working and needs to be restarted.");
StopLocationLoop();
}
StopLocationTimer();
StartLocationTimer(LocationTimerInterval);
await Task.Delay(1000);
}
StopLocationTimer();
}
//=======================================================================
/// <summary>
/// Returns current location information using Xamarin. This method
/// will normally be awaited as the returned data is the only way
/// to see the geolocation data.
/// </summary>
/// <returns>Xamarin location object</returns>
//=======================================================================
public async Task<ESPosition> GetCurrentLocation( GeolocationRequest req )
{
Xamarin.Essentials.Location xlocation;
ESPosition Location = null;
try
{
if (ESPermissions.LocationGranted)
{
cts = new CancellationTokenSource();
xlocation = await Geolocation.GetLocationAsync(req, cts.Token);
if (xlocation != null)
Location = new ESPosition(xlocation);
cts = null;
}
}
catch (FeatureNotSupportedException fnsEx)
{
GlobalVariables.LogMessage.RaiseLogMessageEvent(fnsEx.Message);
}
catch (FeatureNotEnabledException fneEx)
{
GlobalVariables.LogMessage.RaiseLogMessageEvent(fneEx.Message);
}
catch (PermissionException pEx)
{
GlobalVariables.LogMessage.RaiseLogMessageEvent(pEx.Message);
}
catch (Exception ex)
{
GlobalVariables.LogMessage.RaiseLogMessageEvent(ex.Message);
}
return (Location);
}
Any help understanding what's happening would be greatly appreciated.
Brian