public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper; // there might be a race condition between the calling of this method and the stopping/disposal of the service helper. // if the service helper is stopped/disposed before the service is stopped but after this method is called (e.g., by // an alarm callback), the service helper will be null. if (serviceHelper != null) { serviceHelper.Logger.Log("Sensus service received start command (startId=" + startId + ", flags=" + flags + ").", LoggingLevel.Normal, GetType()); // promote this service to a foreground, for several reasons: it's honest and transparent. it lets us work effectively with the // android 8.0 restrictions on background services (we can run forever without being killed, we receive background location // updates). it's okay to call this multiple times. doing so will simply update the notification. if (_foregroundServiceNotificationBuilder == null) { PendingIntent mainActivityPendingIntent = PendingIntent.GetActivity(this, 0, new Intent(this, typeof(AndroidMainActivity)), 0); _foregroundServiceNotificationBuilder = (SensusContext.Current.Notifier as AndroidNotifier).CreateNotificationBuilder(this, AndroidNotifier.SensusNotificationChannel.ForegroundService) .SetSmallIcon(Resource.Drawable.ic_launcher) .SetContentIntent(mainActivityPendingIntent) .SetOngoing(true); UpdateForegroundServiceNotificationBuilder(); StartForeground(FOREGROUND_SERVICE_NOTIFICATION_ID, _foregroundServiceNotificationBuilder.Build()); } else { ReissueForegroundServiceNotification(); } // acquire wake lock before this method returns to ensure that the device does not sleep prematurely, interrupting the execution of a callback. serviceHelper.KeepDeviceAwake(); Task.Run(async() => { // the service can be stopped without destroying the service object. in such cases, // subsequent calls to start the service will not call OnCreate. therefore, it's // important that any code called here is okay to call multiple times, even if the // service is running. calling this when the service is running can happen because // sensus receives a signal on device boot and for any callback alarms that are // requested. furthermore, all calls here should be nonblocking / async so we don't // tie up the UI thread. await serviceHelper.StartAsync(); if (intent == null) { serviceHelper.LetDeviceSleep(); } else { AndroidCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as AndroidCallbackScheduler; DisplayPage displayPage; // is this a callback intent? if (callbackScheduler.IsCallback(intent)) { // service the callback -- the matching LetDeviceSleep will be called therein await callbackScheduler.ServiceCallbackAsync(intent); } // should we display a page? else if (Enum.TryParse(intent.GetStringExtra(Notifier.DISPLAY_PAGE_KEY), out displayPage)) { await serviceHelper.BringToForegroundAsync(); SensusContext.Current.Notifier.OpenDisplayPage(displayPage); serviceHelper.LetDeviceSleep(); } else { serviceHelper.LetDeviceSleep(); } } }); } // if the service is killed by the system (e.g., due to resource constraints), ask the system to restart // the service when possible. return(StartCommandResult.Sticky); }