private SmsDatum GetSms(global::Android.Net.Uri uri) { SmsDatum smsDatum = null; ICursor queryResults = null; try { queryResults = Application.Context.ContentResolver.Query(uri, null, null, null, null); if (queryResults != null && queryResults.MoveToNext()) { string protocol = queryResults.GetString(queryResults.GetColumnIndex("protocol")); var type = queryResults.GetInt(queryResults.GetColumnIndex("type")); int sentMessageType; // see the Backwards Compatibility article for more information #if __ANDROID_19__ if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat) { sentMessageType = (int)SmsMessageType.Sent; // API level 19 } else #endif { sentMessageType = 2; } if (type != sentMessageType) //note:protocol is never coming in null for me { return(null); } string toNumber = queryResults.GetString(queryResults.GetColumnIndexOrThrow("address")); long unixTimeMS = queryResults.GetLong(queryResults.GetColumnIndexOrThrow("date")); string body = queryResults.GetString(queryResults.GetColumnIndexOrThrow("body")); DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeMilliseconds(unixTimeMS); if (!string.IsNullOrWhiteSpace(body)) { Contact contact = SensusServiceHelper.GetContactAsync(toNumber).Result; bool isContact = contact != null; smsDatum = new SmsDatum(timestamp, null, toNumber, body, true, isContact, contact?.Name, contact?.Email); } } } finally { // always close cursor try { queryResults.Close(); } catch { } } return(smsDatum); }
public override void OnChange(bool selfChange, global::Android.Net.Uri uri) { try { // for some reason, we get multiple calls to OnChange for the same outgoing text. ignore repeats. if (_mostRecentlyObservedSmsURI != null && uri.ToString() == _mostRecentlyObservedSmsURI) { return; } SmsDatum datum = null; // process MMS: https://stackoverflow.com/questions/3012287/how-to-read-mms-data-in-android bool isMMS = uri.ToString().StartsWith("content://sms/raw") || uri.ToString().StartsWith("content://mms-sms"); if (isMMS) { datum = GetMostRecentMMS(); } else { datum = GetSms(uri); } if (!string.IsNullOrWhiteSpace(datum?.Message)) { _outgoingSmsCallback?.Invoke(datum); if (isMMS) { _mostRecentMmsTimestamp = datum.Timestamp; } else { _mostRecentlyObservedSmsURI = uri.ToString(); } } } catch (Exception ex) { // something is probably wrong with our implementation. each manufacturer does things a bit different. SensusServiceHelper.Get().Logger.Log("Exception in " + nameof(OnChange) + ": " + ex.Message, LoggingLevel.Normal, GetType()); } }
private SmsDatum GetMostRecentMMS() { SmsDatum mmsDatum = null; ICursor queryResults = null; try { // get the most recent conversation queryResults = Application.Context.ContentResolver.Query(global::Android.Net.Uri.Parse("content://mms-sms/conversations/"), null, null, null, "_id"); if (queryResults.MoveToLast()) { long unixTimeMS = queryResults.GetLong(queryResults.GetColumnIndexOrThrow("date")) * 1000; int messageId = queryResults.GetInt(queryResults.GetColumnIndexOrThrow("_id")); ICursor innerQueryResults = Application.Context.ContentResolver.Query(global::Android.Net.Uri.Parse("content://mms/part"), null, "mid=" + messageId, null, null); try { if (innerQueryResults.MoveToFirst()) { while (true) { if (innerQueryResults.GetString(innerQueryResults.GetColumnIndexOrThrow("ct")) == "text/plain") { string toNumber = GetAddressNumber(messageId, 151); // 137 is the from and 151 is the to DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeMilliseconds(unixTimeMS); // get the message body string data = innerQueryResults.GetString(innerQueryResults.GetColumnIndexOrThrow("_data")); string body; if (data == null) { body = innerQueryResults.GetString(innerQueryResults.GetColumnIndexOrThrow("text")); } else { int partId = innerQueryResults.GetInt(innerQueryResults.GetColumnIndexOrThrow("_id")); body = GetMmsText(partId); } // only keep if we have a message body if (!string.IsNullOrWhiteSpace(body) && _mostRecentMmsTimestamp != timestamp) { mmsDatum = new SmsDatum(timestamp, null, toNumber, body, true); } break; } else { if (!innerQueryResults.MoveToNext()) { break; } } } } } finally { // always close cursor try { innerQueryResults.Close(); } catch { } } } } catch (Exception ex) { string message = "Exception while getting MMS: " + ex.Message; SensusServiceHelper.Get().Logger.Log(message, LoggingLevel.Normal, GetType()); // throw a NotSupportedException, as our implementation is probably not correct -- and // will never work for -- this device. this message will cause the probe to be disabled // if thrown on protocol startup; otherwise, it will simply be ignored. one reason this // is important is that sensus will continue to foreground the app in an attempt to get // the probe started. but it will always fail, and this will really irritate the user. throw new NotSupportedException(message, ex); } finally { // always close cursor try { queryResults.Close(); } catch { } } return(mmsDatum); }