예제 #1
0
 public bool ContainsCallback(ScheduledCallback callback)
 {
     // we should never get a null callback id, but it seems that we are from android.
     if (callback?.Id == null)
     {
         SensusException.Report("Attempted to check scheduling status of callback with null id.");
         return(false);
     }
     else
     {
         return(_idCallback.ContainsKey(callback.Id));
     }
 }
예제 #2
0
 public bool CallbackIsScheduled(string callbackId)
 {
     // we should never get a null callback id, but it seems that we are from android.
     if (callbackId == null)
     {
         SensusException.Report("Received null callback id.");
         return(false);
     }
     else
     {
         return(_idCallback.ContainsKey(callbackId));
     }
 }
예제 #3
0
        protected ScheduledCallback TryGetCallback(string id)
        {
            ScheduledCallback callback;

            _idCallback.TryGetValue(id, out callback);

            if (callback == null)
            {
                SensusException.Report("Failed to retrieve callback " + id + ".");
            }

            return(callback);
        }
예제 #4
0
        private void OpenFile()
        {
            lock (_locker)
            {
                // it's possible to stop the data store before entering this lock.
                if (!Running)
                {
                    return;
                }

                // open new file
                _path = null;
                Exception mostRecentException = null;
                for (int i = 0; _path == null && i < 5; ++i)
                {
                    try
                    {
                        _path = System.IO.Path.Combine(StorageDirectory, Guid.NewGuid().ToString());
                        Stream file = new FileStream(_path, FileMode.CreateNew, FileAccess.Write);

                        // add gzip stream if doing compression
                        if (_compressionLevel != CompressionLevel.NoCompression)
                        {
                            file = new GZipStream(file, _compressionLevel, false);
                        }

                        // use buffering for compression and runtime performance
                        _file = new BufferedStream(file, _bufferSizeBytes);
                        _dataWrittenToCurrentFile = 0;
                        _filesOpened++;
                    }
                    catch (Exception ex)
                    {
                        mostRecentException = ex;
                        _path = null;
                    }
                }

                // we could not open a file to write, so we cannot proceed. report the most recent exception and bail.
                if (_path == null)
                {
                    throw SensusException.Report("Failed to open file for local data store.", mostRecentException);
                }
                else
                {
                    // open the JSON array
                    byte[] jsonBeginArrayBytes = Encoding.UTF8.GetBytes("[");
                    _file.Write(jsonBeginArrayBytes, 0, jsonBeginArrayBytes.Length);
                }
            }
        }
예제 #5
0
        protected async Task <bool> UpdateFencesAsync(IFenceUpdateRequest updateRequest)
        {
            bool success = false;

            try
            {
                // we've seen cases where the update blocks indefinitely (e.g., due to outdated google play services on the
                // phone). impose a timeout to avoid such blocks.
                Task <Statuses> updateFencesTask = Awareness.FenceApi.UpdateFencesAsync(_awarenessApiClient, updateRequest);
                Task            timeoutTask      = Task.Delay(TimeSpan.FromSeconds(60));
                Task            finishedTask     = await Task.WhenAny(updateFencesTask, timeoutTask);

                if (finishedTask == updateFencesTask)
                {
                    Statuses status = await updateFencesTask;

                    if (status.IsSuccess)
                    {
                        SensusServiceHelper.Get().Logger.Log("Updated Google Awareness API fences.", LoggingLevel.Normal, GetType());
                        success = true;
                    }
                    else if (status.IsCanceled)
                    {
                        SensusServiceHelper.Get().Logger.Log("Google Awareness API fence update canceled.", LoggingLevel.Normal, GetType());
                    }
                    else if (status.IsInterrupted)
                    {
                        SensusServiceHelper.Get().Logger.Log("Google Awareness API fence update interrupted", LoggingLevel.Normal, GetType());
                    }
                    else
                    {
                        string message = "Unrecognized fence update status:  " + status;
                        SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType());
                        SensusException.Report(message);
                        throw new Exception(message);
                    }
                }
                else
                {
                    throw new Exception("Fence update timed out.");
                }
            }
            // catch any errors from calling UpdateFences
            catch (Exception ex)
            {
                // ensure that wait is always set
                SensusServiceHelper.Get().Logger.Log("Exception while updating fences:  " + ex, LoggingLevel.Normal, GetType());
            }

            return(success);
        }
예제 #6
0
        public NSMutableDictionary GetCallbackInfo(ScheduledCallback callback)
        {
            // we've seen cases where the UserInfo dictionary cannot be serialized because one of its values is null. if this happens, the
            // callback won't be serviced, and things won't return to normal until Sensus is activated by the user and the callbacks are
            // refreshed. don't create the UserInfo dictionary if we've got null values.
            if (callback.Id == null)
            {
                SensusException.Report("Failed to get callback information bundle due to null callback ID.");
                return(null);
            }

            return(new NSMutableDictionary(new NSDictionary(SENSUS_CALLBACK_KEY, true,
                                                            iOSNotifier.NOTIFICATION_ID_KEY, callback.Id)));
        }
예제 #7
0
파일: Window.cs 프로젝트: nc3tq/sensus-1
        public Window(string windowString)
        {
            string[] windowStringParts = windowString.Trim().Split('-');  // format is DD-HH:MM-HH:MM, where the first and last components are optional

            // check whether the first element is a DOW abbreviation
            string        firstElement = windowStringParts[0].Trim();
            List <string> dayOfTheWeekAbbreviations = new List <string>(new string[] { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" });

            if (dayOfTheWeekAbbreviations.Contains(firstElement))
            {
                // get enumeration value for abbreviation
                foreach (string dayOfTheWeek in Enum.GetNames(typeof(DayOfWeek)))
                {
                    if (dayOfTheWeek.StartsWith(firstElement))
                    {
                        DayOfTheWeek = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), dayOfTheWeek);
                        break;
                    }
                }

                // the string started with a known abbreviation, so we should have obtained the enumeration value.
                if (DayOfTheWeek == null)
                {
                    throw SensusException.Report("Unable to obtain DayOfWeek for abbreviation:  " + firstElement);
                }

                // trim DOW abbreviation from start of array.
                windowStringParts = windowStringParts.Skip(1).ToArray();
            }

            Start = DateTime.Parse(windowStringParts[0].Trim()).TimeOfDay;  // for some reason DateTime.Parse seems to be more forgiving

            if (windowStringParts.Length == 1)
            {
                End = Start;
            }
            else if (windowStringParts.Length == 2)
            {
                End = DateTime.Parse(windowStringParts[1].Trim()).TimeOfDay;  // for some reason DateTime.Parse seems to be more forgiving

                if (Start > End)
                {
                    throw new Exception($"Improper trigger window ({windowString})");
                }
            }
            else
            {
                throw new Exception($"Improper trigger window ({windowString})");
            }
        }
예제 #8
0
        public override void OnReceive(global::Android.Content.Context context, Intent intent)
        {
            // this method is usually called on the UI thread and can crash the app if it throws an exception
            try
            {
                if (intent == null)
                {
                    throw new ArgumentNullException(nameof(intent));
                }

                if (INCOMING_SMS != null && intent.Action == "android.provider.Telephony.SMS_RECEIVED")
                {
                    Bundle bundle = intent.Extras;

                    if (bundle != null)
                    {
                        Java.Lang.Object[] pdus = (Java.Lang.Object[])bundle.Get("pdus");
                        for (int i = 0; i < pdus.Length; i++)
                        {
                            SmsMessage message;

                            // see the Backwards Compatibility article for more information
#if __ANDROID_23__
                            if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
                            {
                                message = SmsMessage.CreateFromPdu((byte[])pdus[i], intent.GetStringExtra("format"));  // API 23
                            }
                            else
#endif
                            {
                                // ignore deprecation warning
#pragma warning disable 618
                                message = SmsMessage.CreateFromPdu((byte[])pdus[i]);
#pragma warning restore 618
                            }

                            Contact contact   = SensusServiceHelper.GetContactAsync(message.OriginatingAddress).Result;
                            bool    isContact = contact != null;


                            INCOMING_SMS(this, new SmsDatum(DateTimeOffset.FromUnixTimeMilliseconds(message.TimestampMillis), message.OriginatingAddress, null, message.MessageBody, false, isContact, contact?.Name, contact?.Email));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception in SMS broadcast receiver:  " + ex.Message, ex);
            }
        }
예제 #9
0
 public AndroidPowerConnectionChangeListener()
 {
     AndroidPowerConnectionChangeBroadcastReceiver.POWER_CONNECTION_CHANGED += (sender, connected) =>
     {
         try
         {
             PowerConnectionChanged?.Invoke(sender, connected);
         }
         catch (Exception ex)
         {
             SensusException.Report("Failed to process power connection change.", ex);
         }
     };
 }
예제 #10
0
        public override void ReceivedLocalNotification(UIApplication application, UILocalNotification notification)
        {
            // UILocalNotifications were obsoleted in iOS 10.0, and we should not be receiving them via this app delegate
            // method. we won't have any idea how to service them on iOS 10.0 and above. report the problem to Insights and bail.
            if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
            {
                SensusException.Report("Received UILocalNotification in iOS 10 or later.");
            }
            else
            {
                // we're in iOS < 10.0, which means we should have a notifier and scheduler to handle the notification.

                // cancel notification (removing it from the tray), since it has served its purpose
                (SensusContext.Current.Notifier as IUILocalNotificationNotifier)?.CancelNotification(notification);

                IiOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as IiOSCallbackScheduler;
                if (callbackScheduler == null)
                {
                    SensusException.Report("Invalid callback scheduler.");
                }
                else
                {
                    // run asynchronously to release the UI thread
                    System.Threading.Tasks.Task.Run(() =>
                    {
                        // the following must be done on the UI thread because we reference UIApplicationState.Active.
                        SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(() =>
                        {
                            callbackScheduler.ServiceCallbackAsync(notification.UserInfo);

                            // check whether the user opened the notification to open sensus, indicated by an application state that is not active. we'll
                            // also get notifications when the app is active, since we use them for timed callback events. if the user opened the notification,
                            // display the page associated with the notification (if there is one).
                            if (application.ApplicationState != UIApplicationState.Active && notification.UserInfo != null)
                            {
                                callbackScheduler.OpenDisplayPage(notification.UserInfo);

                                // provide some generic feedback if the user responded to a silent notification
                                if ((notification.UserInfo.ValueForKey(new NSString(iOSNotifier.SILENT_NOTIFICATION_KEY)) as NSNumber)?.BoolValue ?? false)
                                {
                                    SensusServiceHelper.Get().FlashNotificationAsync("Study Updated.", false);
                                }
                            }
                        });
                    });
                }
            }
        }
예제 #11
0
        protected override async void OnResume()
        {
            Console.Error.WriteLine("--------------------------- Resuming activity ---------------------------");

            base.OnResume();

            // temporarily hide UI while we bind to service. allowing the user to click around before the service helper is initialized
            // may result in a crash.
            (Xamarin.Forms.Application.Current as App).MasterPage.IsVisible = false;
            (Xamarin.Forms.Application.Current as App).DetailPage.IsVisible = false;

            // ensure the service is bound any time the activity is resumed
            BindService(AndroidSensusService.GetServiceIntent(false), _serviceConnection, Bind.AboveClient);

            // start new task to wait for connection, since we're currently on the UI thread, which the service connection needs in order to complete.
            await Task.Run(() =>
            {
                // we've not seen the binding take more than a second or two; however, we want to be very careful not to block indefinitely
                // here because the UI is currently disabled. if for some strange reason the binding does not work, bail out after 10 seconds
                // and let the user interact with the UI. most likely, a crash will be coming very soon in this case, as the sensus service
                // will probably not be running. again, this has not occurred in practice, but allowing the crash to occur will send us information
                // through the crash analytics service and we'll be able to track it
                TimeSpan serviceBindTimeout = TimeSpan.FromSeconds(10000);
                if (_serviceBindWait.WaitOne(serviceBindTimeout))
                {
                    SensusServiceHelper.Get().Logger.Log("Activity proceeding following service bind.", LoggingLevel.Normal, GetType());
                }
                else
                {
                    SensusException.Report("Timed out waiting " + serviceBindTimeout + " for the service to bind.");
                }

                SensusServiceHelper.Get().CancelPendingSurveysNotification();

                // enable the UI
                try
                {
                    SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(() =>
                    {
                        (Xamarin.Forms.Application.Current as App).MasterPage.IsVisible = true;
                        (Xamarin.Forms.Application.Current as App).DetailPage.IsVisible = true;
                    });
                }
                catch (Exception)
                {
                }
            });
        }
 public iOSPowerConnectionChangeListener()
 {
     UIDevice.Notifications.ObserveBatteryStateDidChange((sender, e) =>
     {
         try
         {
             UIDeviceBatteryState batteryState = UIDevice.CurrentDevice.BatteryState;
             bool connected = batteryState == UIDeviceBatteryState.Charging || batteryState == UIDeviceBatteryState.Full;
             PowerConnectionChanged?.Invoke(this, connected);
         }
         catch (Exception ex)
         {
             SensusException.Report("Failed to process power connection change.", ex);
         }
     });
 }
예제 #13
0
 /// <summary>
 /// Gets the Azure format identifier for the native push notification service.
 /// </summary>
 /// <returns>The Azure format identifier.</returns>
 /// <param name="format">Format.</param>
 public static string GetAzureFormatIdentifier(PushNotificationRequestFormat format)
 {
     if (format == PushNotificationRequestFormat.FirebaseCloudMessaging)
     {
         return("gcm");
     }
     else if (format == PushNotificationRequestFormat.ApplePushNotificationService)
     {
         return("apple");
     }
     else
     {
         SensusException.Report("Unrecognized push notification request format:  " + format);
         return("");
     }
 }
예제 #14
0
 private async Task GetCredentialsFromAuthenticationServiceAsync()
 {
     if (Protocol.AuthenticationService == null)
     {
         SensusException.Report(nameof(GetCredentialsFromAuthenticationServiceAsync) + " called without an authentication service.");
     }
     else
     {
         // set keys/token for use in the data store
         AmazonS3Credentials s3Credentials = await Protocol.AuthenticationService.GetCredentialsAsync();
         _iamAccessKey = s3Credentials.AccessKeyId;
         _iamSecretKey = s3Credentials.SecretAccessKey;
         _sessionToken = s3Credentials.SessionToken;
         _region = s3Credentials.Region;
     }
 }
예제 #15
0
        private void PromoteFiles()
        {
            lock (_locker)
            {
                foreach (string path in Directory.GetFiles(StorageDirectory))
                {
                    try
                    {
                        // promotion applies to files that don't yet have a file extension
                        if (!string.IsNullOrWhiteSpace(System.IO.Path.GetExtension(path)))
                        {
                            continue;
                        }

                        // add the .json file extension, marking the file as complete.
                        string finalPath = path + JSON_FILE_EXTENSION;
                        File.Move(path, finalPath);

                        // add the gzip file extension if we're doing compression
                        if (_compressionLevel != CompressionLevel.NoCompression)
                        {
                            // add the .gz extension to the path
                            string gzipPath = finalPath + GZIP_FILE_EXTENSION;
                            File.Move(finalPath, gzipPath);
                            finalPath = gzipPath;
                        }

                        // encrypt the file if needed
                        if (_encrypt)
                        {
                            string encryptedPath = finalPath + ENCRYPTED_FILE_EXTENSION;

                            Protocol.AsymmetricEncryption.EncryptSymmetrically(File.ReadAllBytes(finalPath), ENCRYPTION_KEY_SIZE_BITS, ENCRYPTION_INITIALIZATION_KEY_SIZE_BITS, encryptedPath);

                            // if everything went through okay, delete the unencrypted file.
                            File.Delete(finalPath);
                        }

                        _filesPromoted++;
                    }
                    catch (Exception ex)
                    {
                        SensusException.Report("Failed to promote file.", ex);
                    }
                }
            }
        }
예제 #16
0
        private Task Put(AmazonS3Client s3, Stream stream, string key, string contentType, CancellationToken cancellationToken)
        {
            return(Task.Run(async() =>
            {
                _putCount++;

                try
                {
                    PutObjectRequest putRequest = new PutObjectRequest
                    {
                        BucketName = _bucket,
                        CannedACL = S3CannedACL.BucketOwnerFullControl,  // without this, the bucket owner will not have access to the uploaded data
                        InputStream = stream,
                        Key = key,
                        ContentType = contentType
                    };

                    HttpStatusCode putStatus = (await s3.PutObjectAsync(putRequest, cancellationToken)).HttpStatusCode;

                    if (putStatus == HttpStatusCode.OK)
                    {
                        _successfulPutCount++;
                    }
                    else
                    {
                        throw new Exception("Bad status code:  " + putStatus);
                    }
                }
                catch (WebException ex)
                {
                    if (ex.Status == WebExceptionStatus.TrustFailure)
                    {
                        string message = "A trust failure has occurred between Sensus and the AWS S3 endpoint. This is likely the result of a failed match between the server's public key and the pinned public key within the Sensus AWS S3 remote data store.";
                        SensusException.Report(message, ex);
                    }

                    throw ex;
                }
                catch (Exception ex)
                {
                    string message = "Failed to write data stream to Amazon S3 bucket \"" + _bucket + "\":  " + ex.Message;
                    SensusServiceHelper.Get().Logger.Log(message + " " + ex.Message, LoggingLevel.Normal, GetType());
                    throw new Exception(message, ex);
                }
            }));
        }
예제 #17
0
        public async Task <Account> CreateAccountAsync(string participantId)
        {
            string deviceType = "";

            if (SensusContext.Current.Platform == Platform.Android)
            {
                deviceType = "android";
            }
            else if (SensusContext.Current.Platform == Platform.iOS)
            {
                deviceType = "ios";
            }
            else
            {
                SensusException.Report("Unrecognized platform:  " + SensusContext.Current.Platform);
            }

            string accountJSON = await new Uri(string.Format(_createAccountURL, SensusServiceHelper.Get().DeviceId, participantId, deviceType)).DownloadStringAsync();

            try
            {
                Account = accountJSON.DeserializeJson <Account>();
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception while deserializing account:  " + ex.Message, ex);
            }

            // check properties. trim while we're at it.

            if (string.IsNullOrWhiteSpace(Account.ParticipantId = Account.ParticipantId?.Trim()))
            {
                SensusException.Report("Empty " + nameof(Account.ParticipantId) + " returned by authentication service for device " + SensusServiceHelper.Get().DeviceId + " and participant " + (participantId ?? "[null]."));
            }

            if (string.IsNullOrWhiteSpace(Account.Password = Account.Password?.Trim()))
            {
                SensusException.Report("Empty " + nameof(Account.Password) + " returned by authentication service for device " + SensusServiceHelper.Get().DeviceId + " and participant " + (participantId ?? "[null]."));
            }

            // save the app state to hang on to the account
            await SensusServiceHelper.Get().SaveAsync();

            return(Account);
        }
예제 #18
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Anonymizable"/> class.
        /// </summary>
        /// <param name="propertyDisplayName">Property display name.</param>
        /// <param name="availableAnonymizerTypes">Available anonymizer types.</param>
        /// <param name="defaultAnonymizerIndex">Default anonymizer index. Pass -1 for no anonymization.</param>
        public Anonymizable(string propertyDisplayName, Type[] availableAnonymizerTypes, int defaultAnonymizerIndex)
        {
            _propertyDisplayName = propertyDisplayName;

            if (availableAnonymizerTypes == null)
            {
                availableAnonymizerTypes = new Type[0];
            }

            // we're always going to add the value omitting anonymizer at the start of the anonymizers list. if
            // the default is >= 0 add 1 to produce the result that the caller desires. only do this if the
            // caller has passed in anonymizer types. if they didn't and they set the default anonymizer to
            // 0, then they are asking for the value omitting anonymizer by default -- in this case we should
            // not increment.
            if (defaultAnonymizerIndex >= 0 && availableAnonymizerTypes.Length > 0)
            {
                ++defaultAnonymizerIndex;
            }

            // instantiate available anonymizers
            _availableAnonymizers = new List <Anonymizer>();
            _availableAnonymizers.Add(new ValueOmittingAnonymizer());  // omitting the value is always an option
            foreach (Type availableAnonymizerType in availableAnonymizerTypes)
            {
                Anonymizer availableAnonymizer = Activator.CreateInstance(availableAnonymizerType) as Anonymizer;

                if (availableAnonymizer == null)
                {
                    throw SensusException.Report("Attempted to create an anonymizer from a type that does not derive from Anonymizer.");
                }

                _availableAnonymizers.Add(availableAnonymizer);
            }

            if (defaultAnonymizerIndex < -1 || defaultAnonymizerIndex >= _availableAnonymizers.Count)
            {
                throw SensusException.Report("Attempted to set default anonymizer for property outside the bounds of available types:  " + defaultAnonymizerIndex);
            }

            // set default anonymizer if requested
            if (defaultAnonymizerIndex >= 0)
            {
                _defaultAnonymizer = _availableAnonymizers[defaultAnonymizerIndex];
            }
        }
예제 #19
0
        /// <summary>
        /// Creates a value provider for a member. Only works for PropertyInfo members and will throw an
        /// exception for all other members.
        /// </summary>
        /// <returns>The member value provider.</returns>
        /// <param name="member">Member.</param>
        protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
        {
            IValueProvider defaultValueProvider = base.CreateMemberValueProvider(member);

            // only datum objects should be serialized, and these should only contain serialized properties.
            PropertyInfo propertyInfo = member as PropertyInfo;

            if (propertyInfo == null)
            {
                // this is unexpected. report the issue and return the default serializer.
                SensusException.Report("Attempted to serialize/anonymize non-property datum member:  " + member);
                return(defaultValueProvider);
            }
            else
            {
                return(new AnonymizedPropertyValueProvider(propertyInfo, defaultValueProvider, this));
            }
        }
예제 #20
0
        private void OpenFile()
        {
            lock (_fileLocker)
            {
                // it's possible to stop the data store before entering this lock.
                if (!Running)
                {
                    return;
                }

                // try a few times to open a new file within the storage directory
                _currentPath = null;
                Exception mostRecentException = null;
                for (int tryNum = 0; _currentPath == null && tryNum < 5; ++tryNum)
                {
                    try
                    {
                        _currentPath = Path.Combine(StorageDirectory, Guid.NewGuid().ToString());
                        _currentFile = new BufferedStream(new FileStream(_currentPath, FileMode.CreateNew, FileAccess.Write), _currentFileBufferSizeBytes);
                        _totalDataWrittenToCurrentFile = 0;
                        _totalFilesOpened++;
                    }
                    catch (Exception ex)
                    {
                        mostRecentException = ex;
                        _currentPath        = null;
                    }
                }

                // we could not open a file to write, so we cannot proceed. report the most recent exception and bail.
                if (_currentPath == null)
                {
                    throw SensusException.Report("Failed to open file for local data store.", mostRecentException);
                }
                else
                {
                    // open the JSON array
                    byte[] jsonOpenArrayBytes = Encoding.UTF8.GetBytes("[");
                    _currentFile.Write(jsonOpenArrayBytes, 0, jsonOpenArrayBytes.Length);
                }
            }
        }
예제 #21
0
            public object GetValue(object target)
            {
                if (target == null)
                {
                    throw SensusException.Report("Attempted to process a null object.");
                }
                // if the target object is a Datum, consider anonymizing the property value
                else if (target is Datum)
                {
                    Datum datum = target as Datum;

                    // if we're processing the Anonymized property, return true so that the output JSON properly reflects the fact that the datum has
                    // been passed through an anonymizer (this regardless of whether an anonymization transformation was actually applied). this also
                    // ensures that, if the JSON is deserialized and then reserialized, we won't attempt to anonymize the JSON again (see checks below).
                    if (_property.DeclaringType == typeof(Datum) && _property.Name == nameof(Datum.Anonymized))
                    {
                        return(true);
                    }
                    else
                    {
                        // first get the property's value in the default way
                        object propertyValue = _defaultMemberValueProvider.GetValue(datum);

                        Anonymizer anonymizer;
                        if (propertyValue == null ||                                                                              // don't attempt anonymization if the property value is null
                            datum.Anonymized ||                                                                                   // don't re-anonymize property values
                            (anonymizer = _contractResolver.GetAnonymizer(datum.GetType().GetProperty(_property.Name))) == null)  // don't anonymize when we don't have an anonymizer. we re-get the PropertyInfo from the datum's type so that it matches our dictionary of PropertyInfo objects (the reflected type needs to be the most-derived, which doesn't happen leading up to this point for some reason).
                        {
                            return(propertyValue);
                        }
                        else
                        {
                            return(anonymizer.Apply(propertyValue, _contractResolver.Protocol));
                        }
                    }
                }
                // if the target is not a datum object (e.g., for InputCompletionRecords stored in ScriptDatum objects), simply return the member value in the default way.
                else
                {
                    return(_defaultMemberValueProvider.GetValue(target));
                }
            }
예제 #22
0
        public void IssueNotificationAsync(UNNotificationRequest request, Action <NSError> errorCallback = null)
        {
            // although we should never, we might be getting in null requests from somewhere. bail if we do.
            if (request == null)
            {
                SensusException.Report("Null notification request.");
                return;
            }

            // don't issue silent notifications from the background, as they will be displayed to the user upon delivery, and this will confuse the user (they're
            // not designed to be seen). this can happen in a race condition where sensus transitions to the background but has a small amount of time to execute,
            // and in that time a silent callback (e.g., for local data store) is scheduled. checking for background state below will help mitigate this.
            bool abort = false;

            SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(() =>
            {
                iOSCallbackScheduler callbackScheduler = SensusContext.Current.CallbackScheduler as iOSCallbackScheduler;
                ScheduledCallback callback             = callbackScheduler.TryGetCallback(request?.Content?.UserInfo);
                abort = (callback?.Silent ?? false) && UIApplication.SharedApplication.ApplicationState == UIApplicationState.Background;
            });

            if (abort)
            {
                SensusServiceHelper.Get().Logger.Log("Aborting notification:  Will not issue silent notification from background.", LoggingLevel.Normal, GetType());
                return;
            }

            UNUserNotificationCenter.Current.AddNotificationRequest(request, error =>
            {
                if (error == null)
                {
                    SensusServiceHelper.Get().Logger.Log("Notification " + request.Identifier + " requested for " + ((request.Trigger as UNCalendarNotificationTrigger)?.NextTriggerDate.ToString() ?? "[time not specified]") + ".", LoggingLevel.Normal, GetType());
                }
                else
                {
                    SensusServiceHelper.Get().Logger.Log("Failed to add notification request:  " + error.Description, LoggingLevel.Normal, GetType());
                    SensusException.Report("Failed to add notification request:  " + error.Description);
                }

                errorCallback?.Invoke(error);
            });
        }
예제 #23
0
        public virtual bool ValueMatches(object conditionValue, bool conjunctive)
        {
            // if either is null, both must be null to be equal
            if (Value == null || conditionValue == null)
            {
                return(Value == null && conditionValue == null);
            }
            // if they're of the same type, compare
            else if (Value.GetType().Equals(conditionValue.GetType()))
            {
                return(Value.Equals(conditionValue));
            }
            else
            {
                // this should never happen
                SensusException.Report(new Exception("Called Input.ValueMatches with conditionValue of type " + conditionValue.GetType() + ". Comparing with value of type " + Value.GetType() + "."));

                return(false);
            }
        }
예제 #24
0
        public override void OnReceive(global::Android.Content.Context context, Intent intent)
        {
            // this method is usually called on the UI thread and can crash the app if it throws an exception
            try
            {
                if (intent == null)
                {
                    throw new ArgumentNullException(nameof(intent));
                }

                if (intent.Action == Intent.ActionNewOutgoingCall)
                {
                    OUTGOING_CALL?.Invoke(this, intent.GetStringExtra(Intent.ExtraPhoneNumber));
                }
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception in telephony broadcast receiver:  " + ex.Message, ex);
            }
        }
예제 #25
0
        public override void OnReceive(global::Android.Content.Context context, Intent intent)
        {
            // this method is usually called on the UI thread and can crash the app if it throws an exception
            try
            {
                if (intent == null)
                {
                    throw new ArgumentNullException(nameof(intent));
                }

                if (intent.Action == Intent.ActionPowerConnected || intent.Action == Intent.ActionPowerDisconnected)
                {
                    POWER_CONNECTION_CHANGED?.Invoke(this, intent.Action == Intent.ActionPowerConnected);
                }
            }
            catch (Exception ex)
            {
                SensusException.Report("Exception in power connection change broadcast receiver:  " + ex.Message, ex);
            }
        }
예제 #26
0
        /// <summary>
        /// Cancels a UILocalNotification. This will succeed in one of two conditions:  (1) if the notification to be
        /// cancelled is scheduled (i.e., not delivered); or (2) if the notification to be cancelled has been delivered
        /// and if the object passed in is the delivered notification and not the one that was passed to
        /// ScheduleLocalNotification -- once passed to ScheduleLocalNotification, a copy is made and the objects won't test equal
        /// for cancellation.
        /// </summary>
        /// <param name="notification">Notification to cancel.</param>
        /// <param name="notificationIdKey">Key for ID in UserInfo of the UILocalNotification.</param>
        private void CancelLocalNotification(UILocalNotification notification, string notificationIdKey)
        {
            // set up action to cancel notification
            SensusContext.Current.MainThreadSynchronizer.ExecuteThreadSafe(() =>
            {
                try
                {
                    string idToCancel = notification.UserInfo.ValueForKey(new NSString(notificationIdKey)).ToString();

                    Get().Logger.Log("Cancelling local notification \"" + idToCancel + "\".", LoggingLevel.Normal, typeof(iOSSensusServiceHelper));

                    // a local notification can be scheduled, in which case it hasn't yet been delivered and should reside within the shared
                    // application's list of scheduled notifications. the tricky part here is that these notification objects aren't reference-equal,
                    // so we can't just pass `notification` to CancelLocalNotification. instead, we must search for the notification by id and
                    // cancel the appropriate scheduled notification object.
                    bool notificationCanceled = false;
                    foreach (UILocalNotification scheduledNotification in UIApplication.SharedApplication.ScheduledLocalNotifications)
                    {
                        string scheduledId = scheduledNotification.UserInfo.ValueForKey(new NSString(notificationIdKey))?.ToString();

                        if (scheduledId == idToCancel)
                        {
                            UIApplication.SharedApplication.CancelLocalNotification(scheduledNotification);
                            notificationCanceled = true;
                        }
                    }

                    // if we didn't cancel the notification above, then it isn't scheduled and should have already been delivered. if it has been
                    // delivered, then our only option for cancelling it is to pass `notification` itself to CancelLocalNotification. this assumes
                    // that `notification` is the actual notification object and not, for example, the one originally passed to ScheduleLocalNotification.
                    if (!notificationCanceled)
                    {
                        UIApplication.SharedApplication.CancelLocalNotification(notification);
                    }
                }
                catch (Exception ex)
                {
                    SensusException.Report("Failed to cancel notification.", ex, false);
                }
            });
        }
예제 #27
0
        private void PromoteFiles()
        {
            lock (_locker)
            {
                foreach (string path in Directory.GetFiles(StorageDirectory))
                {
                    try
                    {
                        // promotion applies to files that don't yet have a file extension
                        if (!string.IsNullOrWhiteSpace(System.IO.Path.GetExtension(path)))
                        {
                            continue;
                        }

                        string promotedPath = path + JSON_FILE_EXTENSION;

                        if (_compressionLevel != CompressionLevel.NoCompression)
                        {
                            promotedPath += GZIP_FILE_EXTENSION;
                        }

                        if (_encrypt)
                        {
                            promotedPath += ENCRYPTED_FILE_EXTENSION;
                            Protocol.AsymmetricEncryption.EncryptSymmetrically(File.ReadAllBytes(path), ENCRYPTION_KEY_SIZE_BITS, ENCRYPTION_INITIALIZATION_KEY_SIZE_BITS, promotedPath);
                            File.Delete(path);
                        }
                        else
                        {
                            File.Move(path, promotedPath);
                        }

                        _filesPromoted++;
                    }
                    catch (Exception ex)
                    {
                        SensusException.Report("Failed to promote file.", ex);
                    }
                }
            }
        }
예제 #28
0
        public override Task LetDeviceSleepAsync()
        {
            if (_wakeLock != null)
            {
                lock (_wakeLock)
                {
                    // ensure the wake lock is held in order to prevent under-locking exceptions
                    if (_wakeLock.IsHeld)
                    {
                        _wakeLock.Release();

                        Logger.Log("Wake lock released" + (_wakeLock.IsHeld ? "" : " for the final time") + ".", LoggingLevel.Normal, GetType());

                        // if wake lock is no longer held, then update the amount of time spent holding it.
                        if (!_wakeLock.IsHeld)
                        {
                            if (_wakeLockTimestamp == null)
                            {
                                SensusException.Report("Released wake lock for the final time without a timestamp on the first acquisition.");
                            }
                            else
                            {
                                _wakeLockTime += DateTime.Now - _wakeLockTimestamp.Value;

                                SensusServiceHelper.Get().Logger.Log("Has spent " + _wakeLockTime + " holding the wake lock.", LoggingLevel.Normal, GetType());

                                _wakeLockTimestamp = null;
                            }
                        }
                    }
                    else
                    {
                        SensusException.Report("Attempted to call " + nameof(LetDeviceSleepAsync) + ", but the wake lock is not currently held.");

                        _wakeLockTimestamp = null;
                    }
                }
            }

            return(Task.CompletedTask);
        }
예제 #29
0
        protected Probe()
        {
            _enabled                   = false;
            _state                     = ProbeState.Stopped;
            _storeData                 = true;
            _startStopTimes            = new List <Tuple <bool, DateTime> >();
            _successfulHealthTestTimes = new List <DateTime>();
            _maxChartDataCount         = 10;
            _chartData                 = new List <ChartDataPoint>(_maxChartDataCount + 1);

            _powerConnectionChanged = async(sender, connected) =>
            {
                if (connected)
                {
                    // ask the probe to start processing its data
                    try
                    {
                        SensusServiceHelper.Get().Logger.Log("AC power connected. Initiating data processing within probe.", LoggingLevel.Normal, GetType());
                        _processDataCanceller = new CancellationTokenSource();
                        await ProcessDataAsync(_processDataCanceller.Token);

                        SensusServiceHelper.Get().Logger.Log("Probe data processing complete.", LoggingLevel.Normal, GetType());
                    }
                    catch (OperationCanceledException)
                    {
                        // don't report task cancellation exceptions. these are expected whenever the user unplugs the device while processing data.
                        SensusServiceHelper.Get().Logger.Log("Data processing task was cancelled.", LoggingLevel.Normal, GetType());
                    }
                    catch (Exception ex)
                    {
                        // the data processing actually failed prior to cancellation. this should not happen, so report it.
                        SensusException.Report("Non-cancellation exception while processing probe data:  " + ex.Message, ex);
                    }
                }
                else
                {
                    // cancel any previous attempt to process data
                    _processDataCanceller?.Cancel();
                }
            };
        }
예제 #30
0
        public ScheduledCallbackState ScheduleCallback(ScheduledCallback callback)
        {
            if (callback.State != ScheduledCallbackState.Created)
            {
                SensusException.Report("Attemped to schedule callback " + callback.Id + ", which is in the " + callback.State + " state and not the " + ScheduledCallbackState.Created + " state.");
                callback.State = ScheduledCallbackState.Unknown;
            }
            else if (_idCallback.TryAdd(callback.Id, callback))
            {
                callback.NextExecution = DateTime.Now + callback.Delay;
                callback.State         = ScheduledCallbackState.Scheduled;

                ScheduleCallbackPlatformSpecific(callback);
            }
            else
            {
                SensusException.Report("Attempted to schedule duplicate callback for " + callback.Id + ".");
            }

            return(callback.State);
        }