Esempio n. 1
0
        public override void OnWindowFocusChanged(bool hasFocus)
        {
            base.OnWindowFocusChanged(hasFocus);

            // the service helper is responsible for running actions that depend on the main activity. if the main activity
            // is not showing, the service helper starts the main activity and then runs requested actions. there is a race
            // condition between actions that wish to show a dialog (e.g., starting speech recognition) and the display of
            // the activity. in order to ensure that the activity is showing before any actions are run, we override this
            // focus changed event and let the service helper know when the activity is focused and when it is not. this
            // way, any actions that the service helper runs will certainly be run after the main activity is running
            // and focused.
            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            if (serviceHelper != null)
            {
                if (hasFocus)
                {
                    serviceHelper.SetFocusedMainActivity(this);
                }
                else
                {
                    serviceHelper.SetFocusedMainActivity(null);
                }
            }
        }
Esempio n. 2
0
        public override void OnCreate()
        {
            base.OnCreate();

            SensusContext.Current = new AndroidSensusContext
            {
                Platform = Platform.Android,
                MainThreadSynchronizer = new MainConcurrent(),
                SymmetricEncryption    = new SymmetricEncryption(SensusServiceHelper.ENCRYPTION_KEY),
                CallbackScheduler      = new AndroidCallbackScheduler(this),
                Notifier = new AndroidNotifier(this)
            };

            SensusServiceHelper.Initialize(() => new AndroidSensusServiceHelper());

            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // we might have failed to create the service helper. it's also happened that the service is created after the
            // service helper is disposed:  https://insights.xamarin.com/app/Sensus-Production/issues/46
            if (serviceHelper == null)
            {
                Stop();
            }
            else
            {
                serviceHelper.SetService(this);
            }
        }
        public override void OnCreate()
        {
            base.OnCreate();

            // insights should be initialized first to maximize coverage of exception reporting
            InsightsInitialization.Initialize(new AndroidInsightsInitializer(Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId)), SensusServiceHelper.XAMARIN_INSIGHTS_APP_KEY);

            SensusContext.Current = new AndroidSensusContext
            {
                Platform = Platform.Android,
                MainThreadSynchronizer = new MainConcurrent(),
                SymmetricEncryption    = new SymmetricEncryption(SensusServiceHelper.ENCRYPTION_KEY),
                CallbackScheduler      = new AndroidCallbackScheduler(this),
                Notifier = new AndroidNotifier(this)
            };

            SensusServiceHelper.Initialize(() => new AndroidSensusServiceHelper());

            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // we might have failed to create the service helper. it's also happened that the service is created after the
            // service helper is disposed:  https://insights.xamarin.com/app/Sensus-Production/issues/46
            if (serviceHelper == null)
            {
                Stop();
            }
            else
            {
                serviceHelper.SetService(this);
            }
        }
        public override void OnCreate()
        {
            base.OnCreate();

            _serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;
            _serviceHelper.SetService(this);
            _serviceHelper.UpdateApplicationStatus("0 protocols are running");
        }
Esempio n. 5
0
        public override void OnDestroy()
        {
            base.OnDestroy();

            if (_serviceHelper != null)
            {
                _serviceHelper.Logger.Log("Destroying service.", LoggingLevel.Normal, GetType());
                _serviceHelper.Dispose();
                _serviceHelper = null;
            }
        }
Esempio n. 6
0
        public override void OnDestroy()
        {
            base.OnDestroy();

            if (_serviceHelper != null)
            {
                _serviceHelper.Logger.Log("Destroying service.", LoggingLevel.Normal, GetType());
                _serviceHelper.Dispose();
                _serviceHelper = null;
            }
        }
Esempio n. 7
0
        public override void OnCreate()
        {
            base.OnCreate();

            // initialize the current activity plugin here as well as in the main activity
            // since this service may be created by iteself without a main activity (e.g.,
            // on boot or on OS restart of the service). we want the plugin to have be
            // initialized regardless of how the app comes to be created.
            CrossCurrentActivity.Current.Init(Application);

            SensusContext.Current = new AndroidSensusContext
            {
                Platform = Platform.Android,
                MainThreadSynchronizer = new MainConcurrent(),
                SymmetricEncryption    = new SymmetricEncryption(SensusServiceHelper.ENCRYPTION_KEY),
                CallbackScheduler      = new AndroidCallbackScheduler(this),
                Notifier = new AndroidNotifier(),
                PowerConnectionChangeListener = new AndroidPowerConnectionChangeListener()
            };

            // promote this service to a foreground service as soon as possible. we use a foreground service 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, etc.
            (SensusContext.Current.Notifier as AndroidNotifier).UpdateForegroundServiceNotificationBuilder();
            StartForeground(AndroidNotifier.FOREGROUND_SERVICE_NOTIFICATION_ID, (SensusContext.Current.Notifier as AndroidNotifier).BuildForegroundServiceNotification());

            // https://developer.android.com/reference/android/content/Intent#ACTION_POWER_CONNECTED
            // This is intended for applications that wish to register specifically to this notification. Unlike ACTION_BATTERY_CHANGED,
            // applications will be woken for this and so do not have to stay active to receive this notification. This action can be
            // used to implement actions that wait until power is available to trigger.
            //
            // We use the same receiver for both the connected and disconnected intents.
            _powerBroadcastReceiver = new AndroidPowerConnectionChangeBroadcastReceiver();
            IntentFilter powerConnectFilter = new IntentFilter();

            powerConnectFilter.AddAction(Intent.ActionPowerConnected);
            powerConnectFilter.AddAction(Intent.ActionPowerDisconnected);
            powerConnectFilter.AddCategory(Intent.CategoryDefault);
            RegisterReceiver(_powerBroadcastReceiver, powerConnectFilter);

            // must come after context initialization. also, it is here -- below StartForeground -- because it can
            // take a while to complete and we don't want to run afoul of the short timing requirements on calling
            // StartForeground.
            SensusServiceHelper.Initialize(() => new AndroidSensusServiceHelper());

            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // we might have failed to create the service helper. it's also happened that the service is created
            // after the service helper is disposed.
            if (serviceHelper == null)
            {
                Stop();
            }
        }
        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, the service
            // helper will be null.
            if (serviceHelper != null)
            {
                serviceHelper.Logger.Log("Sensus service received start command (startId=" + startId + ", flags=" + flags + ").", LoggingLevel.Normal, GetType());

                // acquire wake lock before this method returns to ensure that the device does not sleep prematurely, interrupting the execution of a callback.
                serviceHelper.KeepDeviceAwake();

                // 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.
                serviceHelper.StartAsync(() =>
                {
                    if (intent == null)
                    {
                        serviceHelper.LetDeviceSleep();
                    }
                    else
                    {
                        DisplayPage displayPage;

                        // is this a callback intent?
                        if (intent.GetBooleanExtra(CallbackScheduler.SENSUS_CALLBACK_KEY, false))
                        {
                            (SensusContext.Current.CallbackScheduler as AndroidCallbackScheduler).ServiceCallbackAsync(intent);
                        }
                        // should we display a page?
                        else if (Enum.TryParse(intent.GetStringExtra(Notifier.DISPLAY_PAGE_KEY), out displayPage))
                        {
                            serviceHelper.BringToForeground();
                            SensusContext.Current.Notifier.OpenDisplayPage(displayPage);
                            serviceHelper.LetDeviceSleep();
                        }
                        else
                        {
                            serviceHelper.LetDeviceSleep();
                        }
                    }
                });
            }

            return(StartCommandResult.Sticky);
        }
Esempio n. 9
0
        public override void OnCreate()
        {
            base.OnCreate();

            SensusServiceHelper.Initialize(() => new AndroidSensusServiceHelper());

            _serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // it's happened that the service is created after the service helper is disposed:  https://insights.xamarin.com/app/Sensus-Production/issues/46
            if (_serviceHelper == null)
            {
                StopSelf();
                return;
            }

            _serviceHelper.SetService(this);
            _serviceHelper.UpdateApplicationStatus("0 protocols are running");
        }
Esempio n. 10
0
        public override void OnCreate()
        {
            base.OnCreate();

            SensusServiceHelper.Initialize(() => new AndroidSensusServiceHelper());

            _serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // it's happened that the service is created after the service helper is disposed:  https://insights.xamarin.com/app/Sensus-Production/issues/46
            if (_serviceHelper == null)
            {
                StopSelf();
                return;
            }

            _serviceHelper.SetService(this);
            _serviceHelper.UpdateApplicationStatus("0 protocols are running");
        }
Esempio n. 11
0
        public override void OnCreate()
        {
            base.OnCreate();

            InsightsInitialization.Initialize(new AndroidInsightsInitializer(Settings.Secure.GetString(ContentResolver, Settings.Secure.AndroidId)), SensusServiceHelper.XAMARIN_INSIGHTS_APP_KEY);

            SensusContext.Current = new AndroidSensusContext(SensusServiceHelper.ENCRYPTION_KEY);
            SensusServiceHelper.Initialize(() => new AndroidSensusServiceHelper());

            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // we might have failed to create the service helper. it's also happened that the service is created after the
            // service helper is disposed:  https://insights.xamarin.com/app/Sensus-Production/issues/46
            if (serviceHelper == null)
            {
                Stop();
            }
            else
            {
                serviceHelper.SetService(this);
            }
        }
Esempio n. 12
0
        public override void OnDestroy()
        {
            Console.Error.WriteLine("--------------------------- Destroying Service ---------------------------");

            base.OnDestroy();

            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            // the service helper will be null if we failed to create it within OnCreate, so first check that. also,
            // OnDestroy can be called either when the user stops Sensus (in Android) and when the system reclaims
            // the service under memory pressure. in the former case, we'll already have done the notification and
            // stopping of protocols; however, we have no way to know how we reached OnDestroy, so to cover the latter
            // case we're going to do the notification and stopping again. this will be duplicative in the case where
            // the user has stopped sensus. in sum, anything we do below must be safe to run repeatedly.
            if (serviceHelper != null)
            {
                serviceHelper.Logger.Log("Destroying service.", LoggingLevel.Normal, GetType());
                NotifyBindingsOfStop();
                serviceHelper.StopProtocols();
                serviceHelper.SetService(null);
            }
        }
Esempio n. 13
0
        private void UpdateForegroundServiceNotificationBuilder()
        {
            AndroidSensusServiceHelper serviceHelper = SensusServiceHelper.Get() as AndroidSensusServiceHelper;

            int numRunningStudies = serviceHelper.RunningProtocolIds.Count;

            _foregroundServiceNotificationBuilder.SetContentTitle("You are enrolled in " + numRunningStudies + " " + (numRunningStudies == 1 ? "study" : "studies") + ".");

            string contentText = "";

            // although the number of studies might be greater than 0, the protocols might not yet be started (e.g., after starting sensus).
            List <Protocol> runningProtocols = serviceHelper.GetRunningProtocols();

            if (runningProtocols.Count > 0)
            {
                double avgParticipation = runningProtocols.Average(protocol => protocol.Participation) * 100;
                contentText += "Your overall participation level is " + Math.Round(avgParticipation, 0) + "%. ";
            }

            contentText += "Tap to open Sensus.";

            _foregroundServiceNotificationBuilder.SetContentText(contentText);
        }
Esempio n. 14
0
        public override void OnCreate()
        {
            base.OnCreate();

            _notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;

            UpdateNotification("Sensus", "");

            _sensusServiceHelper = SensusServiceHelper.Load <AndroidSensusServiceHelper>(new Geolocator(this)) as AndroidSensusServiceHelper;
            if (_sensusServiceHelper == null)
            {
                _sensusServiceHelper = new AndroidSensusServiceHelper();
                _sensusServiceHelper.Initialize(new Geolocator(this));
                _sensusServiceHelper.Save();
            }

            _sensusServiceHelper.SetService(this);

            _sensusServiceHelper.Stopped += (o, e) =>
            {
                _notificationManager.Cancel(SERVICE_NOTIFICATION_ID);
                StopSelf();
            };
        }
Esempio n. 15
0
 public AndroidSensusServiceBinder(AndroidSensusServiceHelper sensusServiceHelper)
 {
     _sensusServiceHelper = sensusServiceHelper;
 }
Esempio n. 16
0
        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, the service
            // helper will be null.
            if (serviceHelper != null)
            {
                serviceHelper.Logger.Log("Sensus service received start command (startId=" + startId + ", flags=" + flags + ").", LoggingLevel.Normal, GetType());

                // acquire wake lock before this method returns to ensure that the device does not sleep prematurely, interrupting the execution of a callback.
                serviceHelper.KeepDeviceAwake();

                // 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.
                serviceHelper.StartAsync(() =>
                {
                    if (intent != null)
                    {
                        // is this a callback intent?
                        if (intent.GetBooleanExtra(SensusServiceHelper.SENSUS_CALLBACK_KEY, false))
                        {
                            string callbackId = intent.GetStringExtra(SensusServiceHelper.SENSUS_CALLBACK_ID_KEY);

                            // if the user removes the main activity from the switcher, the service's process will be killed and restarted without notice, and
                            // we'll have no opportunity to unschedule repeating callbacks. when the service is restarted we'll reinitialize the service
                            // helper, restart the repeating callbacks, and we'll then have duplicate repeating callbacks. handle the invalid callbacks below.
                            // if the callback is scheduled, it's fine. if it's not, then unschedule it.
                            if (serviceHelper.CallbackIsScheduled(callbackId))
                            {
                                bool repeating        = intent.GetBooleanExtra(SensusServiceHelper.SENSUS_CALLBACK_REPEATING_KEY, false);
                                int repeatDelayMS     = intent.GetIntExtra(SensusServiceHelper.SENSUS_CALLBACK_REPEAT_DELAY_KEY, -1);
                                bool repeatLag        = intent.GetBooleanExtra(SensusServiceHelper.SENSUS_CALLBACK_REPEAT_LAG_KEY, false);
                                bool wakeLockReleased = false;

                                // raise callback and notify the user if there is a message. we wouldn't have presented the user with the message yet.
                                serviceHelper.RaiseCallbackAsync(callbackId, repeating, repeatDelayMS, repeatLag, true,

                                                                 // schedule a new callback at the given time.
                                                                 repeatCallbackTime =>
                                {
                                    serviceHelper.ScheduleCallbackAlarm(serviceHelper.CreateCallbackPendingIntent(intent), callbackId, repeatCallbackTime);
                                },

                                                                 // if the callback indicates that it's okay for the device to sleep, release the wake lock now.
                                                                 () =>
                                {
                                    wakeLockReleased = true;
                                    serviceHelper.LetDeviceSleep();
                                    serviceHelper.Logger.Log("Wake lock released preemptively for scheduled callback action.", LoggingLevel.Normal, GetType());
                                },

                                                                 // release wake lock now if we didn't while the callback action was executing.
                                                                 () =>
                                {
                                    if (!wakeLockReleased)
                                    {
                                        serviceHelper.LetDeviceSleep();
                                        serviceHelper.Logger.Log("Wake lock released after scheduled callback action completed.", LoggingLevel.Normal, GetType());
                                    }
                                });
                            }
                            else
                            {
                                serviceHelper.UnscheduleCallback(callbackId);
                                serviceHelper.LetDeviceSleep();
                            }
                        }
                        else if (intent.GetStringExtra(SensusServiceHelper.NOTIFICATION_ID_KEY) == SensusServiceHelper.PENDING_SURVEY_NOTIFICATION_ID)
                        {
                            serviceHelper.BringToForeground();

                            // display the pending scripts page if it is not already on the top of the navigation stack
                            SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(async() =>
                            {
                                IReadOnlyList <Page> navigationStack = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack;
                                Page topPage = navigationStack.Count == 0 ? null : navigationStack.Last();
                                if (!(topPage is PendingScriptsPage))
                                {
                                    await Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new PendingScriptsPage());
                                }

                                serviceHelper.LetDeviceSleep();
                            });
                        }
                        else
                        {
                            serviceHelper.LetDeviceSleep();
                        }
                    }
                    else
                    {
                        serviceHelper.LetDeviceSleep();
                    }
                });
            }

            return(StartCommandResult.Sticky);
        }
Esempio n. 17
0
        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);
        }
Esempio n. 18
0
 public AndroidSensusServiceBinder(AndroidSensusServiceHelper sensusServiceHelper)
 {
     _sensusServiceHelper = sensusServiceHelper;
 }
Esempio n. 19
0
        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
                    {
                        DisplayPage displayPage;

                        // is this a callback intent?
                        if (intent.GetBooleanExtra(CallbackScheduler.SENSUS_CALLBACK_KEY, false))
                        {
                            // service the callback -- the matching LetDeviceSleep will be called therein
                            await(SensusContext.Current.CallbackScheduler as AndroidCallbackScheduler).ServiceCallbackAsync(intent);
                        }
                        // should we display a page?
                        else if (Enum.TryParse(intent.GetStringExtra(Notifier.DISPLAY_PAGE_KEY), out displayPage))
                        {
                            serviceHelper.BringToForeground();
                            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);
        }