public static void Set(SensusServiceHelper sensusServiceHelper) { SENSUS_SERVICE_HELPER = sensusServiceHelper; // the helper is set to null, e.g., when the app is stopped. if (SENSUS_SERVICE_HELPER == null) SENSUS_SERVICE_HELPER_WAIT.Reset(); else SENSUS_SERVICE_HELPER_WAIT.Set(); }
/// <summary> /// Initializes the sensus service helper. Must be called when app first starts, from the main / UI thread. /// </summary> /// <param name="createNew">Function for creating a new service helper, if one is needed.</param> public static void Initialize(Func<SensusServiceHelper> createNew) { if (SINGLETON != null) { SINGLETON.Logger.Log("Serivce helper already initialized. Nothing to do.", LoggingLevel.Normal, SINGLETON.GetType()); return; } try { SINGLETON = JsonConvert.DeserializeObject<SensusServiceHelper>(Decrypt(ReadAllBytes(SERIALIZATION_PATH)), JSON_SERIALIZER_SETTINGS); SINGLETON.Logger.Log("Deserialized service helper with " + SINGLETON.RegisteredProtocols.Count + " protocols.", LoggingLevel.Normal, SINGLETON.GetType()); } catch (Exception deserializeException) { Console.Error.WriteLine("Failed to deserialize Sensus service helper: " + deserializeException.Message); try { Console.Error.WriteLine("Creating new Sensus service helper."); SINGLETON = createNew(); } catch (Exception singletonCreationException) { #region crash app and report to insights string error = "Failed to construct service helper: " + singletonCreationException.Message + System.Environment.NewLine + singletonCreationException.StackTrace; Console.Error.WriteLine(error); Exception exceptionToReport = new Exception(error); try { Insights.Report(exceptionToReport, Xamarin.Insights.Severity.Error); } catch (Exception insightsReportException) { Console.Error.WriteLine("Failed to report exception to Xamarin Insights: " + insightsReportException.Message); } throw exceptionToReport; #endregion } #region save newly created helper try { SINGLETON.SaveAsync(); } catch (Exception singletonSaveException) { Console.Error.WriteLine("Failed to save new Sensus service helper: " + singletonSaveException.Message); } #endregion } }
public virtual void Dispose() { try { Stop(); } catch (Exception ex) { Console.Error.WriteLine("Failed to stop service helper: " + ex.Message); } SINGLETON = null; }
/// <summary> /// Initializes the sensus service helper. Must be called when app first starts, from the main / UI thread. /// </summary> /// <param name="createNew">Function for creating a new service helper, if one is needed.</param> public static void Initialize(Func<SensusServiceHelper> createNew) { if (SINGLETON == null) { Exception deserializeException; if (!TryDeserializeSingleton(out deserializeException)) { // we failed to deserialize. wait a bit and try again. but don't wait too long since we're holding up the // app-load sequence, which is not allowed to take too much time. Thread.Sleep(5000); if (!TryDeserializeSingleton(out deserializeException)) { // we really couldn't deserialize the service helper! try to create a new service helper... try { SINGLETON = createNew(); } catch (Exception singletonCreationException) { #region crash app and report to insights string error = "Failed to construct service helper: " + singletonCreationException.Message + System.Environment.NewLine + singletonCreationException.StackTrace; Console.Error.WriteLine(error); Exception exceptionToReport = new Exception(error); try { Insights.Report(exceptionToReport, Xamarin.Insights.Severity.Error); } catch (Exception insightsReportException) { Console.Error.WriteLine("Failed to report exception to Xamarin Insights: " + insightsReportException.Message); } throw exceptionToReport; #endregion } SINGLETON.Logger.Log("Repeatedly failed to deserialize service helper. Most recent exception: " + deserializeException.Message, LoggingLevel.Normal, SINGLETON.GetType()); SINGLETON.Logger.Log("Created new service helper after failing to deserialize the old one.", LoggingLevel.Normal, SINGLETON.GetType()); } } } else SINGLETON.Logger.Log("Serivce helper already initialized. Nothing to do.", LoggingLevel.Normal, SINGLETON.GetType()); }
private static bool TryDeserializeSingleton(out Exception ex) { ex = null; string errorMessage = null; // read bytes byte[] encryptedJsonBytes = null; try { encryptedJsonBytes = ReadAllBytes(SERIALIZATION_PATH); } catch (Exception exception) { errorMessage = "Failed to read service helper file into byte array: " + exception.Message; Console.Error.WriteLine(errorMessage); } if (encryptedJsonBytes != null) { // decrypt JSON string decryptedJSON = null; try { decryptedJSON = Decrypt(encryptedJsonBytes); } catch (Exception exception) { errorMessage = "Failed to decrypt service helper byte array (length=" + encryptedJsonBytes.Length + ") into JSON: " + exception.Message; Console.Error.WriteLine(errorMessage); } if (decryptedJSON != null) { // deserialize service helper try { SINGLETON = JsonConvert.DeserializeObject<SensusServiceHelper>(decryptedJSON, JSON_SERIALIZER_SETTINGS); } catch (Exception exception) { errorMessage = "Failed to deserialize service helper JSON (length=" + decryptedJSON + ") into service helper: " + exception.Message; Console.Error.WriteLine(errorMessage); } } } if (errorMessage != null) ex = new Exception(errorMessage); if (SINGLETON != null) SINGLETON.Logger.Log("Deserialized service helper with " + SINGLETON.RegisteredProtocols.Count + " protocols.", LoggingLevel.Normal, SINGLETON.GetType()); return SINGLETON != null; }
public virtual void Stop() { // stop all protocols lock (_registeredProtocols) { _logger.Log("Stopping protocols.", LoggingLevel.Normal, GetType()); foreach (Protocol protocol in _registeredProtocols) { try { protocol.Stop(); } catch (Exception ex) { _logger.Log("Failed to stop protocol \"" + protocol.Name + "\": " + ex.Message, LoggingLevel.Normal, GetType()); } } } // make sure all logged messages get into the file. _logger.CommitMessageBuffer(); SINGLETON = null; }
public void Stop() { lock (_locker) { if (_running) { _running = false; } else { return; } if (ProtocolRunningChanged != null) { ProtocolRunningChanged(this, _running); } SensusServiceHelper.Get().RemoveRunningProtocolId(_id); SensusServiceHelper.Get().Logger.Log("Stopping protocol \"" + _name + "\".", LoggingLevel.Normal, GetType()); foreach (Probe probe in _probes) { if (probe.Running) { try { probe.Stop(); } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Failed to stop " + probe.GetType().FullName + ": " + ex.Message, LoggingLevel.Normal, GetType()); } } } if (_localDataStore != null && _localDataStore.Running) { try { _localDataStore.Stop(); } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Failed to stop local data store: " + ex.Message, LoggingLevel.Normal, GetType()); } } if (_remoteDataStore != null && _remoteDataStore.Running) { try { _remoteDataStore.Stop(); } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Failed to stop remote data store: " + ex.Message, LoggingLevel.Normal, GetType()); } } SensusServiceHelper.Get().Logger.Log("Stopped protocol \"" + _name + "\".", LoggingLevel.Normal, GetType()); } }
public void TestHealth(bool userInitiated) { lock (_locker) { if (!userInitiated) { lock (_healthTestTimes) { _healthTestTimes.Add(DateTime.Now); // remove health test times prior to the participation horizon int cutoff = _healthTestTimes.FindIndex(healthTestTime => healthTestTime >= DateTime.Now.AddDays(-_participationHorizonDays)); if (cutoff > 0) { _healthTestTimes.RemoveRange(0, cutoff); } } } string error = null; string warning = null; string misc = null; if (!_running) { error += "Restarting protocol \"" + _name + "\"..."; try { Stop(); Start(); } catch (Exception ex) { error += ex.Message + "..."; } if (_running) { error += "restarted protocol." + Environment.NewLine; } else { error += "failed to restart protocol." + Environment.NewLine; } } if (_running) { if (_localDataStore == null) { error += "No local data store present on protocol." + Environment.NewLine; } else if (_localDataStore.TestHealth(ref error, ref warning, ref misc)) { error += "Restarting local data store..."; try { _localDataStore.Restart(); } catch (Exception ex) { error += ex.Message + "..."; } if (!_localDataStore.Running) { error += "failed to restart local data store." + Environment.NewLine; } } if (_remoteDataStore == null) { error += "No remote data store present on protocol." + Environment.NewLine; } else if (_remoteDataStore.TestHealth(ref error, ref warning, ref misc)) { error += "Restarting remote data store..."; try { _remoteDataStore.Restart(); } catch (Exception ex) { error += ex.Message + "..."; } if (!_remoteDataStore.Running) { error += "failed to restart remote data store." + Environment.NewLine; } } foreach (Probe probe in _probes) { if (probe.Enabled && probe.TestHealth(ref error, ref warning, ref misc)) { error += "Restarting probe \"" + probe.GetType().FullName + "\"..."; try { probe.Restart(); } catch (Exception ex) { error += ex.Message + "..."; } if (!probe.Running) { error += "failed to restart probe \"" + probe.GetType().FullName + "\"." + Environment.NewLine; } } } } _mostRecentReport = new ProtocolReport(DateTimeOffset.UtcNow, error, warning, misc); SensusServiceHelper.Get().Logger.Log("Protocol report:" + Environment.NewLine + _mostRecentReport, LoggingLevel.Normal, GetType()); SensusServiceHelper.Get().Logger.Log("Storing protocol report locally.", LoggingLevel.Normal, GetType()); _localDataStore.AddNonProbeDatum(_mostRecentReport); if (!_localDataStore.UploadToRemoteDataStore && _forceProtocolReportsToRemoteDataStore) { SensusServiceHelper.Get().Logger.Log("Local data aren't pushed to remote, so we're copying the report datum directly to the remote cache.", LoggingLevel.Normal, GetType()); _remoteDataStore.AddNonProbeDatum(_mostRecentReport); } int runningProtocols = SensusServiceHelper.Get().RunningProtocolIds.Count; SensusServiceHelper.Get().UpdateApplicationStatus(runningProtocols + " protocol" + (runningProtocols == 1 ? " is " : "s are") + " running"); } }
public void Start() { lock (_locker) { if (_running) { return; } else { _running = true; } if (ProtocolRunningChanged != null) { ProtocolRunningChanged(this, _running); } // let the service helper know that the current protocol is running (saves helper) SensusServiceHelper.Get().RegisterProtocol(this); SensusServiceHelper.Get().AddRunningProtocolId(_id); bool stopProtocol = false; // start local data store try { if (_localDataStore == null) { throw new Exception("Local data store not defined."); } _localDataStore.Start(); // start remote data store try { if (_remoteDataStore == null) { throw new Exception("Remote data store not defined."); } _remoteDataStore.Start(); // start probes try { SensusServiceHelper.Get().Logger.Log("Starting probes for protocol " + _name + ".", LoggingLevel.Normal, GetType()); int probesEnabled = 0; int probesStarted = 0; foreach (Probe probe in _probes) { if (probe.Enabled) { ++probesEnabled; try { probe.Start(); probesStarted++; } catch (Exception ex) { // stop probe to clean up any inconsistent state information try { probe.Stop(); } catch (Exception ex2) { SensusServiceHelper.Get().Logger.Log("Failed to stop probe after failing to start it: " + ex2.Message, LoggingLevel.Normal, GetType()); } string message = "Failed to start probe \"" + probe.GetType().FullName + "\": " + ex.Message; SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType()); SensusServiceHelper.Get().FlashNotificationAsync(message); // disable probe if it is not supported on the device if (ex is NotSupportedException) { probe.Enabled = false; } } } } if (probesEnabled == 0) { throw new Exception("No probes were enabled."); } else if (probesStarted == 0) { throw new Exception("No probes started."); } } catch (Exception ex) { string message = "Failure while starting probes: " + ex.Message; SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType()); SensusServiceHelper.Get().FlashNotificationAsync(message); stopProtocol = true; } } catch (Exception ex) { string message = "Remote data store failed to start: " + ex.Message; SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType()); SensusServiceHelper.Get().FlashNotificationAsync(message); stopProtocol = true; } } catch (Exception ex) { string message = "Local data store failed to start: " + ex.Message; SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType()); SensusServiceHelper.Get().FlashNotificationAsync(message); stopProtocol = true; } if (stopProtocol) { Stop(); } } }
/// <summary> /// Converts JSON to a Protocol object. Private because Protocols should always be serialized as encrypted binary codes, and this function works with unencrypted strings (it's called in service of the former). /// </summary> /// <param name="json">JSON to deserialize.</param> private static void DisplayFromJsonAsync(string json) { new Thread(() => { try { #region allow protocols to be opened across platforms by manually editing the namespaces in the JSON string newJSON; switch (SensusServiceHelper.Get().GetType().Name) { case "AndroidSensusServiceHelper": newJSON = json.Replace(".iOS", ".Android").Replace(".WinPhone", ".Android"); break; case "iOSSensusServiceHelper": newJSON = json.Replace(".Android", ".iOS").Replace(".WinPhone", ".iOS"); break; case "WinPhone": newJSON = json.Replace(".Android", ".WinPhone").Replace(".iOS", ".WinPhone"); break; default: throw new SensusException("Attempted to deserialize JSON into unknown service helper type: " + SensusServiceHelper.Get().GetType().FullName); } if (newJSON == json) { SensusServiceHelper.Get().Logger.Log("No cross-platform conversion required for service helper JSON.", LoggingLevel.Normal, typeof(Protocol)); } else { SensusServiceHelper.Get().Logger.Log("Performed cross-platform conversion of service helper JSON.", LoggingLevel.Normal, typeof(Protocol)); json = newJSON; } #endregion Protocol protocol = null; ManualResetEvent protocolWait = new ManualResetEvent(false); // always deserialize protocols on the main thread (e.g., since a looper might be required for android) Device.BeginInvokeOnMainThread(() => { try { protocol = JsonConvert.DeserializeObject <Protocol>(json, SensusServiceHelper.JSON_SERIALIZER_SETTINGS); } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Error while deserializing protocol: " + ex.Message, LoggingLevel.Normal, typeof(Protocol)); } finally { protocolWait.Set(); } }); protocolWait.WaitOne(); if (protocol == null) { SensusServiceHelper.Get().Logger.Log("Failed to deserialize protocol.", LoggingLevel.Normal, typeof(Protocol)); SensusServiceHelper.Get().FlashNotificationAsync("Failed to deserialize protocol."); return; } else { Action <Protocol> StartProtocol = p => { Device.BeginInvokeOnMainThread(async() => { if (!(App.Current.MainPage.Navigation.NavigationStack.Last() is ProtocolsPage)) { await App.Current.MainPage.Navigation.PushAsync(new ProtocolsPage()); } p.StartWithUserAgreement("You just opened a protocol named \"" + p.Name + "\" within Sensus." + (string.IsNullOrWhiteSpace(p.StartupAgreement) ? "" : " Please read the following terms and conditions.")); }); }; Protocol existingProtocol = SensusServiceHelper.Get().RegisteredProtocols.FirstOrDefault(p => p.Id == protocol.Id); if (existingProtocol == null) { Probe.GetAllAsync(probes => { // add any probes for the current platform that didn't come through when deserializing. for example, android has a listening WLAN probe, but iOS has a polling WLAN probe. neither will come through on the other platform when deserializing, since the types are not defined. List <Type> deserializedProbeTypes = protocol.Probes.Select(p => p.GetType()).ToList(); foreach (Probe probe in probes) { if (!deserializedProbeTypes.Contains(probe.GetType())) { SensusServiceHelper.Get().Logger.Log("Adding missing probe to protocol: " + probe.GetType().FullName, LoggingLevel.Normal, typeof(Protocol)); protocol.AddProbe(probe); } } // reset the random time anchor -- we shouldn't use the same one that someone else used protocol.ResetRandomTimeAnchor(); // reset the storage directory protocol.StorageDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), protocol.Id); if (!Directory.Exists(protocol.StorageDirectory)) { Directory.CreateDirectory(protocol.StorageDirectory); } SensusServiceHelper.Get().RegisterProtocol(protocol); StartProtocol(protocol); }); } else if (existingProtocol.Running) { SensusServiceHelper.Get().FlashNotificationAsync("Protocol \"" + existingProtocol.Name + "\" is already running."); } else { StartProtocol(existingProtocol); } } } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Failed to deserialize/display protocol from JSON: " + ex.Message, LoggingLevel.Normal, typeof(Protocol)); SensusServiceHelper.Get().FlashNotificationAsync("Failed to deserialize and/or display protocol."); } }).Start(); }