Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
NetworkService.cs
1using System.Net;
2using System.Runtime.CompilerServices;
3using System.Runtime.ExceptionServices;
11
13{
14 [Singleton]
15 internal class NetworkService : LoadableService, INetworkService
16 {
17 private const int defaultXmppPortNumber = 5222;
18
19 public event EventHandler<ConnectivityChangedEventArgs>? ConnectivityChanged;
20
21 public NetworkService()
22 {
23 }
24
26 public override Task Load(bool IsResuming, CancellationToken CancellationToken)
27 {
28 if (this.BeginLoad(IsResuming, CancellationToken))
29 {
30 if (DeviceInfo.Platform != DevicePlatform.Unknown && !DesignMode.IsDesignModeEnabled) // Need to check this, as Xamarin.Essentials doesn't work in unit tests. It has no effect when running on a real phone.
31 Connectivity.ConnectivityChanged += this.Connectivity_ConnectivityChanged;
32
33 this.EndLoad(true);
34 }
35
36 return Task.CompletedTask;
37 }
38
40 public override Task Unload()
41 {
42 if (this.BeginUnload())
43 {
44 if (DeviceInfo.Platform != DevicePlatform.Unknown)
45 Connectivity.ConnectivityChanged -= this.Connectivity_ConnectivityChanged;
46
47 this.EndUnload();
48 }
49
50 return Task.CompletedTask;
51 }
52
53 private void Connectivity_ConnectivityChanged(object? Sender, ConnectivityChangedEventArgs e)
54 {
55 this.ConnectivityChanged?.Invoke(this, e);
56 }
57
58 public virtual bool IsOnline =>
59 Connectivity.NetworkAccess == NetworkAccess.Internet ||
60 Connectivity.NetworkAccess == NetworkAccess.ConstrainedInternet;
61
62 public async Task<(string HostName, int Port, bool IsIpAddress)> LookupXmppHostnameAndPort(string DomainName)
63 {
64 if (IPAddress.TryParse(DomainName, out IPAddress? _))
65 return (DomainName, defaultXmppPortNumber, true);
66
67 try
68 {
69 SRV endpoint = await DnsResolver.LookupServiceEndpoint(DomainName, "xmpp-client", "tcp");
70
71 if (endpoint is not null && !string.IsNullOrWhiteSpace(endpoint.TargetHost) && endpoint.Port > 0)
72 return (endpoint.TargetHost, endpoint.Port, false);
73 }
74 catch (Exception)
75 {
76 // No service endpoint registered
77 }
78
79 return (DomainName, defaultXmppPortNumber, false);
80 }
81
82 public async Task<bool> TryRequest(Func<Task> func, bool rethrowException = false, bool displayAlert = true,
83 [CallerMemberName] string memberName = "")
84 {
85 (bool succeeded, bool _) = await this.PerformRequestInner(async () =>
86 {
87 await func();
88 return true;
89 }, memberName, rethrowException, displayAlert);
90
91 return succeeded;
92 }
93
94 public Task<(bool Succeeded, TReturn? ReturnValue)> TryRequest<TReturn>(Func<Task<TReturn>> func, bool rethrowException = false, bool displayAlert = true, [CallerMemberName] string memberName = "")
95 {
96 return this.PerformRequestInner(async () => await func(), memberName, rethrowException, displayAlert);
97 }
98
99 private async Task<(bool Succeeded, TReturn? ReturnValue)> PerformRequestInner<TReturn>(Func<Task<TReturn>> func, string memberName, bool rethrowException = false, bool displayAlert = true)
100 {
101 Exception thrownException;
102 try
103 {
104 if (!this.IsOnline)
105 {
106 thrownException = new MissingNetworkException(ServiceRef.Localizer[nameof(AppResources.ThereIsNoNetwork)]);
107 ServiceRef.LogService.LogException(thrownException, GetParameter(memberName));
108
109 if (displayAlert)
110 {
111 await ServiceRef.UiService.DisplayAlert(
112 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
113 ServiceRef.Localizer[nameof(AppResources.ThereIsNoNetwork)]);
114 }
115 }
116 else
117 {
118 TReturn t = await func().TimeoutAfter(Constants.Timeouts.GenericRequest);
119 return (true, t);
120 }
121 }
122 catch (AggregateException ae)
123 {
124 thrownException = ae;
125
126 if (ae.InnerException is TimeoutException te)
127 {
128 ServiceRef.LogService.LogException(te, GetParameter(memberName));
129
130 if (displayAlert)
131 {
132 await ServiceRef.UiService.DisplayAlert(
133 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
134 ServiceRef.Localizer[nameof(AppResources.RequestTimedOut)]);
135 }
136 }
137 else if (ae.InnerException is TaskCanceledException tce)
138 {
139 ServiceRef.LogService.LogException(tce, GetParameter(memberName));
140
141 if (displayAlert)
142 {
143 await ServiceRef.UiService.DisplayAlert(
144 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
145 ServiceRef.Localizer[nameof(AppResources.RequestWasCancelled)]);
146 }
147 }
148 else if (ae.InnerException is not null)
149 {
150 ServiceRef.LogService.LogException(ae.InnerException, GetParameter(memberName));
151
152 if (displayAlert)
153 {
154 await ServiceRef.UiService.DisplayAlert(
155 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
156 ae.InnerException.Message);
157 }
158 }
159 else
160 {
161 ServiceRef.LogService.LogException(ae, GetParameter(memberName));
162
163 if (displayAlert)
164 {
165 await ServiceRef.UiService.DisplayAlert(
166 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
167 ae.Message);
168 }
169 }
170 }
171 catch (TimeoutException te)
172 {
173 thrownException = te;
174 ServiceRef.LogService.LogException(te, GetParameter(memberName));
175
176 if (displayAlert)
177 {
178 await ServiceRef.UiService.DisplayAlert(
179 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
180 ServiceRef.Localizer[nameof(AppResources.RequestTimedOut)]);
181 }
182 }
183 catch (TaskCanceledException tce)
184 {
185 thrownException = tce;
186 ServiceRef.LogService.LogException(tce, GetParameter(memberName));
187
188 if (displayAlert)
189 {
190 await ServiceRef.UiService.DisplayAlert(
191 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
192 ServiceRef.Localizer[nameof(AppResources.RequestWasCancelled)]);
193 }
194 }
195 catch (Exception ex)
196 {
197 string message;
198
199 thrownException = ex;
200
201 if (ex is XmppException xe && xe.Stanza is not null)
202 message = xe.Stanza.InnerText;
203 else
204 message = ex.Message;
205
206 ServiceRef.LogService.LogException(ex, GetParameter(memberName));
207
208 if (displayAlert)
209 {
210 await ServiceRef.UiService.DisplayAlert(
211 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
212 message);
213 }
214 }
215
216 if (rethrowException)
217 ExceptionDispatchInfo.Capture(thrownException).Throw();
218
219 return (false, default);
220 }
221
222 private static KeyValuePair<string, object?>[] GetParameter(string MemberName)
223 {
224 if (!string.IsNullOrWhiteSpace(MemberName))
225 {
226 return
227 [
228 new KeyValuePair<string, object?>("Caller", MemberName)
229 ];
230 }
231
232 return [];
233 }
234 }
235}
static readonly TimeSpan GenericRequest
Generic request timeout
Definition: Constants.cs:561
A set of never changing property constants and helpful values.
Definition: Constants.cs:7
bool BeginLoad(bool IsResuming, CancellationToken CancellationToken)
Sets the IsLoading flag if the service isn't already loading.
void EndLoad(bool isLoaded)
Sets the IsLoading and IsLoaded flags and fires an event representing the current load state of the s...
bool IsResuming
If App is resuming service.
bool BeginUnload()
Sets the IsLoading flag if the service isn't already unloading.
void EndUnload()
Sets the IsLoading and IsLoaded flags and fires an event representing the current load state of the s...
DNS resolver, as defined in:
Definition: DnsResolver.cs:32
static Task< SRV > LookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Looks up a service endpoint for a domain. If multiple are available, an appropriate one is selected a...
Definition: DnsResolver.cs:825
Base class of XMPP exceptions
XmlElement Stanza
Stanza causing exception.