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()); // update the foreground service notification with information about loaded/running studies. (SensusContext.Current.Notifier as AndroidNotifier).ReissueForegroundServiceNotification(); // if the service started but there are no protocols that should be running, then stop the app now. there is no // reason for the app to be running in this situation, and the user will likely be annoyed at the presence of the // foreground service notification. if (intent != null && intent.GetBooleanExtra(KEY_STOP_SERVICE_IF_NO_PROTOCOLS_SHOULD_RUN, false) && serviceHelper.RunningProtocolIds.Count == 0) { serviceHelper.Logger.Log("Started service without running protocols. Stopping service now.", LoggingLevel.Normal, GetType()); Stop(); return(StartCommandResult.NotSticky); } // acquire wake lock before this method returns to ensure that the device does not sleep prematurely, interrupting the execution of a callback. serviceHelper.KeepDeviceAwakeAsync().Wait(); Task.Run(async() => { try { // 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) { AndroidCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as AndroidCallbackScheduler; if (callbackScheduler.IsCallback(intent)) { await callbackScheduler.RaiseCallbackAsync(intent); } } } catch (Exception ex) { serviceHelper.Logger.Log("Exception while responding to on-start command: " + ex.Message, LoggingLevel.Normal, GetType()); } finally { serviceHelper.LetDeviceSleepAsync().Wait(); } }); } // 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); }