Exemplo n.º 1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Sensus.Notifications.PushNotificationRequest"/> class. The
 /// intent of this instance will be to deliver a <see cref="PushNotificationUpdate"/> to the app. This update
 /// does not necessarily have any user-targeted content.
 /// </summary>
 /// <param name="id">Identifier of the request. To update a previously issued request, submit a new request
 /// with the same identifier.</param>
 /// <param name="deviceId">Identifier of target device.</param>
 /// <param name="protocol">Target protocol.</param>
 /// <param name="update">Update.</param>
 /// <param name="format">Format.</param>
 /// <param name="notificationTime">Time to deliver the notification.</param>
 /// <param name="backendKey">Backend storage key. It is reasonable to supply a new <see cref="Guid"/> for each
 /// <see cref="PushNotificationRequest"/> instance.</param>
 public PushNotificationRequest(string id,
                                string deviceId,
                                Protocol protocol,
                                PushNotificationUpdate update,
                                PushNotificationRequestFormat format,
                                DateTimeOffset notificationTime,
                                Guid backendKey)
     : this(id, deviceId, protocol, null, null, null, format, notificationTime, backendKey)
 {
     _update = update;
 }
Exemplo n.º 2
0
        private async Task ApplyPendingUpdatesAsync(CancellationToken cancellationToken)
        {
            // apply pending updates until we're cancelled (e.g., due to ios background timeout).
            List <Tuple <PushNotificationUpdate, Protocol> > pendingUpdateProtocols;

            lock (_pendingUpdateProtocols)
            {
                pendingUpdateProtocols = _pendingUpdateProtocols.ToList();
            }

            SensusServiceHelper.Get().Logger.Log("Attempting to apply " + pendingUpdateProtocols.Count + " pending update(s).", LoggingLevel.Normal, GetType());

            foreach (Tuple <PushNotificationUpdate, Protocol> pendingUpdateProtocol in pendingUpdateProtocols)
            {
                try
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        SensusServiceHelper.Get().Logger.Log("Cancellation token cancelled while applying pending update(s). ", LoggingLevel.Normal, GetType());
                        break;
                    }

                    PushNotificationUpdate pendingUpdate = pendingUpdateProtocol.Item1;
                    Protocol protocol = pendingUpdateProtocol.Item2;

                    #region callback
                    if (pendingUpdate.Type == PushNotificationUpdateType.Callback)
                    {
                        string callbackId   = pendingUpdate.Content.Value <string>("callback-id");
                        string invocationId = pendingUpdate.Content.Value <string>("invocation-id");

#if __IOS__
                        // cancel any previously delivered local notifications for the callback. we do not need to cancel
                        // any pending notifications, as they will either (a) be canceled if the callback is non-repeating
                        // or (b) be replaced if the callback is repeating and gets rescheduled. furthermore, there is a
                        // race condition on app activation in which the callback is updated, run, and rescheduled, after
                        // which the push notification is delivered and is processed. cancelling the newly rescheduled
                        // pending local push notification at this point will terminate the local invocation loop, and the
                        // callback command at this point will contain an invalid invocation ID causing it to not be
                        // rescheduled). thus, both local and remote invocation will terminate and the probe will halt.
                        UserNotifications.UNUserNotificationCenter.Current.RemoveDeliveredNotifications(new[] { callbackId });
#endif

                        await SensusContext.Current.CallbackScheduler.RaiseCallbackAsync(callbackId, invocationId);
                    }
                    #endregion

                    #region clear pnr backlog
                    else if (pendingUpdate.Type == PushNotificationUpdateType.ClearPushNotificationRequestBacklog)
                    {
                        await ClearPushNotificationRequestBacklogAsync(cancellationToken);
                    }
                    #endregion

                    #region protocol
                    else if (pendingUpdate.Type == PushNotificationUpdateType.Protocol)
                    {
                        List <ProtocolSetting> settings = pendingUpdate.Content.Value <JArray>("settings")
                                                          .Select(setting => setting.ToObject <ProtocolSetting>())
                                                          .ToList();

                        if (await protocol.ApplySettingsAsync(settings, cancellationToken))
                        {
                            JObject userNotificationObject = pendingUpdate.Content.Value <JObject>("user-notification");

                            if (userNotificationObject != null)
                            {
                                string message = userNotificationObject.Value <string>("message");
                                message = "Your study has been updated" + (string.IsNullOrWhiteSpace(message) ? "." : ":  " + message.Trim());
                                await IssueNotificationAsync("Study Updated", message, pendingUpdate.Id, true, protocol, null, NotificationUserResponseAction.None, message);
                            }
                        }
                    }
                    #endregion

                    #region survey agent policy
                    else if (pendingUpdate.Type == PushNotificationUpdateType.SurveyAgentPolicy)
                    {
                        await protocol.UpdateScriptAgentPolicyAsync(pendingUpdate.Content);
                    }
                    #endregion

                    #region sensing agent policy
                    else if (pendingUpdate.Type == PushNotificationUpdateType.SensingAgentPolicy)
                    {
                        await protocol.UpdateSensingAgentPolicyAsync(pendingUpdate.Content);
                    }
                    #endregion
                }
                catch (Exception ex)
                {
                    SensusServiceHelper.Get().Logger.Log("Exception while applying update:  " + ex.Message, LoggingLevel.Normal, GetType());
                }
                finally
                {
                    // if we haven't been cancelled, then consider the pending update to be complete. this
                    // leaves open the race condition in which the update was actually applied just prior
                    // to the cancellation. in this case, the update will be retained and will be run again
                    // the next time we receive a push notification. we don't see much harm in this for the
                    // operations listed above.
                    if (!cancellationToken.IsCancellationRequested)
                    {
                        lock (_pendingUpdateProtocols)
                        {
                            _pendingUpdateProtocols.Remove(pendingUpdateProtocol);
                        }
                    }
                }
            }

            lock (_pendingUpdateProtocols)
            {
                SensusServiceHelper.Get().Logger.Log(_pendingUpdateProtocols.Count + " pending update(s) left.", LoggingLevel.Normal, GetType());
            }
        }