/// <summary> /// Requests remote invocation for a <see cref="ScheduledCallback"/>, to be delivered in parallel with the /// local invocation loop on iOS. Only one of these (the remote or local) will ultimately be allowed to /// run -- whichever arrives first. /// </summary> /// <returns>Task.</returns> /// <param name="callback">Callback.</param> private async Task RequestRemoteInvocationAsync(ScheduledCallback callback) { // not all callbacks are associated with a protocol (e.g., the app-level health test). because push notifications are // currently tied to the remote data store of the protocol, we don't currently provide PNR support for such callbacks. // on race conditions, it might be the case that the system attempts to schedule a duplicate callback. if this happens // the duplicate will not be assigned a next execution, and the system will try to unschedule/delete it. skip such // callbacks below. if (callback.Protocol != null && callback.NextExecution.HasValue) { try { // the request id must differentiate the current device. furthermore, it needs to identify the // request as one for a callback. lastly, it needs to identify the particular callback that it // targets. the id does not include the callback invocation, as any newer requests for the // callback should obsolete older requests. string id = SensusServiceHelper.Get().DeviceId + "." + SENSUS_CALLBACK_KEY + "." + callback.Id; PushNotificationUpdate update = new PushNotificationUpdate { Type = PushNotificationUpdateType.Callback, Content = JObject.Parse("{" + "\"callback-id\":" + JsonConvert.ToString(callback.Id) + "," + "\"invocation-id\":" + JsonConvert.ToString(callback.InvocationId) + "}") }; PushNotificationRequest request = new PushNotificationRequest(id, SensusServiceHelper.Get().DeviceId, callback.Protocol, update, PushNotificationRequest.LocalFormat, callback.NextExecution.Value, callback.PushNotificationBackendKey); await SensusContext.Current.Notifier.SendPushNotificationRequestAsync(request, CancellationToken.None); } catch (Exception ex) { SensusException.Report("Exception while sending push notification request for scheduled callback: " + ex.Message, ex); } } }
public override async Task<List<PushNotificationUpdate>> GetPushNotificationUpdatesAsync(CancellationToken cancellationToken) { Dictionary<string, PushNotificationUpdate> idUpdate = new Dictionary<string, PushNotificationUpdate>(); // only let one caller at a time download the updates lock (_downloadingUpdatesLocker) { if (_downloadingUpdates) { throw new Exception("Push notification updates are being downloaded on another thread."); } else { _downloadingUpdates = true; } } AmazonS3Client s3 = null; try { s3 = await CreateS3ClientAsync(); // retrieve updates sorted with most recently modified first. this will let us keep only the most recent version // of each update with the same id (note use of TryAdd below, which will only retain the first update per id). foreach (string updateKey in await ListKeysAsync(s3, PUSH_NOTIFICATIONS_UPDATES_DIRECTORY + "/" + SensusServiceHelper.Get().DeviceId, true, cancellationToken)) { try { GetObjectResponse getResponse = await s3.GetObjectAsync(_bucket, updateKey, cancellationToken); if (getResponse.HttpStatusCode == HttpStatusCode.OK) { // catch any exceptions when trying to delete the update. we already retrieved it, and // we might just be lacking connectivity or might have been cancelled. the update will // be pushed again in the future and we'll try deleting it again then. try { await DeleteAsync(s3, updateKey, cancellationToken); } catch (Exception) { } string updatesJSON; using (StreamReader reader = new StreamReader(getResponse.ResponseStream)) { updatesJSON = reader.ReadToEnd().Trim(); } foreach (JObject updateObject in JArray.Parse(updatesJSON)) { PushNotificationUpdate update = updateObject.ToObject<PushNotificationUpdate>(); idUpdate.TryAdd(update.Id, update); } } else { throw new Exception(getResponse.HttpStatusCode.ToString()); } } catch (Exception ex) { SensusServiceHelper.Get().Logger.Log("Exception while getting update object: " + ex.Message, LoggingLevel.Normal, GetType()); } } SensusServiceHelper.Get().Logger.Log("Retrieved " + idUpdate.Count + " update(s).", LoggingLevel.Normal, GetType()); return idUpdate.Values.ToList(); } finally { lock (_downloadingUpdatesLocker) { _downloadingUpdates = false; } DisposeS3(s3); } }