Loading android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java +12 −6 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import com.android.vcard.VCardConstants; import com.android.vcard.VCardEntry; import com.android.vcard.VCardProperty; import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; Loading Loading @@ -165,7 +166,8 @@ class MceStateMachine extends StateMachine { * Note: In the future it may be best to use the entries from the MessageListing in full instead * of this small subset. */ private class MessageMetadata { @VisibleForTesting static class MessageMetadata { private final String mHandle; private final Long mTimestamp; private boolean mRead; Loading Loading @@ -201,7 +203,8 @@ class MceStateMachine extends StateMachine { } // Map each message to its metadata via the handle private ConcurrentHashMap<String, MessageMetadata> mMessages = @VisibleForTesting ConcurrentHashMap<String, MessageMetadata> mMessages = new ConcurrentHashMap<String, MessageMetadata>(); MceStateMachine(MapClientService service, BluetoothDevice device) { Loading Loading @@ -751,12 +754,15 @@ class MceStateMachine extends StateMachine { switch (event.getType()) { case NEW_MESSAGE: // Infer the timestamp for this message as 'now' and read status false // instead of getting the message listing data for it if (!mMessages.containsKey(event.getHandle())) { Calendar calendar = Calendar.getInstance(); Long timestamp = event.getTimestamp(); if (timestamp == null) { // Infer the timestamp for this message as 'now' and read status // false instead of getting the message listing data for it timestamp = new Long(Instant.now().toEpochMilli()); } MessageMetadata metadata = new MessageMetadata(event.getHandle(), calendar.getTime().getTime(), false, MESSAGE_NOT_SEEN); timestamp, false, MESSAGE_NOT_SEEN); mMessages.put(event.getHandle(), metadata); } mMasClient.makeRequest(new RequestGetMessage(event.getHandle(), Loading android/app/src/com/android/bluetooth/mapclient/obex/EventReport.java +31 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.bluetooth.mapclient; import android.annotation.Nullable; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading @@ -40,6 +41,7 @@ import java.util.HashMap; public class EventReport { private static final String TAG = "EventReport"; private final Type mType; private final String mDateTime; private final String mHandle; private final String mFolder; private final String mOldFolder; Loading Loading @@ -67,6 +69,8 @@ public class EventReport { mOldFolder = attrs.get("old_folder"); mDateTime = attrs.get("datetime"); if (mType != Type.MEMORY_FULL && mType != Type.MEMORY_AVAILABLE) { String s = attrs.get("msg_type"); Loading Loading @@ -183,12 +187,39 @@ public class EventReport { return mMsgType; } /** * @return value corresponding to <code>datetime</code> parameter in MAP * specification for NEW_MESSAGE (can be null) */ @Nullable public String getDateTime() { return mDateTime; } /** * @return timestamp from the value corresponding to <code>datetime</code> parameter in MAP * specification for NEW_MESSAGE (can be null) */ @Nullable public Long getTimestamp() { if (mDateTime != null) { ObexTime obexTime = new ObexTime(mDateTime); if (obexTime != null) { return obexTime.getInstant().toEpochMilli(); } } return null; } @Override public String toString() { JSONObject json = new JSONObject(); try { json.put("type", mType); if (mDateTime != null) { json.put("datetime", mDateTime); } json.put("handle", mHandle); json.put("folder", mFolder); json.put("old_folder", mOldFolder); Loading android/app/tests/unit/src/com/android/bluetooth/mapclient/EventReportTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,8 @@ public class EventReportTest { EventReport report = EventReport.fromStream(new DataInputStream(stream)); assertThat(report.getType()).isEqualTo(type); assertThat(report.getDateTime()).isNull(); assertThat(report.getTimestamp()).isNull(); assertThat(report.getHandle()).isEqualTo(handle); assertThat(report.getFolder()).isEqualTo(folder); assertThat(report.getOldFolder()).isEqualTo(oldFolder); Loading @@ -76,6 +78,39 @@ public class EventReportTest { assertThat(report).isNull(); } @Test public void fromStreamWithDateTime() throws Exception { EventReport.Type type = EventReport.Type.PARTICIPANT_CHAT_STATE_CHANGED; String handle = "FFAB"; String dateTime = "20190101T121314"; String folder = "test_folder"; String oldFolder = "old_folder"; Bmessage.Type msgType = Bmessage.Type.MMS; final StringBuilder xml = new StringBuilder(); xml.append("<event\n"); xml.append("type=\"" + type.toString() + "\"\n"); xml.append("datetime=\"" + dateTime + "\"\n"); xml.append("handle=\"" + handle + "\"\n"); xml.append("folder=\"" + folder + "\"\n"); xml.append("old_folder=\"" + oldFolder + "\"\n"); xml.append("msg_type=\"" + msgType + "\"\n"); xml.append("/>\n"); ByteArrayInputStream stream = new ByteArrayInputStream(xml.toString().getBytes()); EventReport report = EventReport.fromStream(new DataInputStream(stream)); assertThat(report.getType()).isEqualTo(type); assertThat(report.getDateTime()).isEqualTo(dateTime); assertThat(report.getTimestamp()).isEqualTo( new ObexTime(dateTime).getInstant().toEpochMilli()); assertThat(report.getHandle()).isEqualTo(handle); assertThat(report.getFolder()).isEqualTo(folder); assertThat(report.getOldFolder()).isEqualTo(oldFolder); assertThat(report.getMsgType()).isEqualTo(msgType); assertThat(report.toString()).isNotEmpty(); } @Test public void fromStream_withIOException_doesNotCrash_andReturnNull() throws Exception { InputStream stream = mock(InputStream.class); Loading android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java +45 −4 Original line number Diff line number Diff line Loading @@ -74,6 +74,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; Loading Loading @@ -627,7 +628,8 @@ public class MapClientStateMachineTest { any(BroadcastOptions.class)); assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED); EventReport event = createNewEventReport("NewMessage", mTestMessageSmsHandle, String dateTime = new ObexTime(Instant.now()).toString(); EventReport event = createNewEventReport("NewMessage", dateTime, mTestMessageSmsHandle, "telecom/msg/inbox", null, "SMS_GSM"); mMceStateMachine.receiveEvent(event); Loading Loading @@ -661,7 +663,8 @@ public class MapClientStateMachineTest { any(BroadcastOptions.class)); assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED); EventReport event = createNewEventReport("NewMessage", mTestMessageMmsHandle, String dateTime = new ObexTime(Instant.now()).toString(); EventReport event = createNewEventReport("NewMessage", dateTime, mTestMessageMmsHandle, "telecom/msg/inbox", null, "MMS"); when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage); Loading Loading @@ -777,6 +780,43 @@ public class MapClientStateMachineTest { any(), eq(MESSAGE_SEEN)); } /** * Test receiving a new message notification. */ @Test public void testReceiveNewMessageNotification() { setupSdpRecordReceipt(); Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED); mMceStateMachine.sendMessage(msg); verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(BroadcastOptions.class)); assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED); // Receive a new message notification. String dateTime = new ObexTime(Instant.now()).toString(); EventReport event = createNewEventReport("NewMessage", dateTime, mTestMessageSmsHandle, "telecom/msg/inbox", null, "SMS_GSM"); Message notificationMessage = Message.obtain(mHandler, MceStateMachine.MSG_NOTIFICATION, (Object)event); mMceStateMachine.getCurrentState().processMessage(notificationMessage); verify(mMockMasClient, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .makeRequest(any(RequestGetMessage.class)); MceStateMachine.MessageMetadata messageMetadata = mMceStateMachine.mMessages.get(mTestMessageSmsHandle); Assert.assertEquals(messageMetadata.getHandle(), mTestMessageSmsHandle); Assert.assertEquals( new ObexTime(Instant.ofEpochMilli(messageMetadata.getTimestamp())).toString(), dateTime); } private void setupSdpRecordReceipt() { // Perform first part of MAP connection logic. verify(mMockMapClientService, Loading Loading @@ -849,12 +889,13 @@ public class MapClientStateMachineTest { return message; } EventReport createNewEventReport(String mType, String mHandle, String mFolder, String mOldFolder, String mMsgType){ EventReport createNewEventReport(String mType, String mDateTime, String mHandle, String mFolder, String mOldFolder, String mMsgType){ HashMap<String, String> attrs = new HashMap<String, String>(); attrs.put("type", mType); attrs.put("datetime", mDateTime); attrs.put("handle", mHandle); attrs.put("folder", mFolder); attrs.put("old_folder", mOldFolder); Loading Loading
android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java +12 −6 Original line number Diff line number Diff line Loading @@ -72,6 +72,7 @@ import com.android.vcard.VCardConstants; import com.android.vcard.VCardEntry; import com.android.vcard.VCardProperty; import java.time.Instant; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; Loading Loading @@ -165,7 +166,8 @@ class MceStateMachine extends StateMachine { * Note: In the future it may be best to use the entries from the MessageListing in full instead * of this small subset. */ private class MessageMetadata { @VisibleForTesting static class MessageMetadata { private final String mHandle; private final Long mTimestamp; private boolean mRead; Loading Loading @@ -201,7 +203,8 @@ class MceStateMachine extends StateMachine { } // Map each message to its metadata via the handle private ConcurrentHashMap<String, MessageMetadata> mMessages = @VisibleForTesting ConcurrentHashMap<String, MessageMetadata> mMessages = new ConcurrentHashMap<String, MessageMetadata>(); MceStateMachine(MapClientService service, BluetoothDevice device) { Loading Loading @@ -751,12 +754,15 @@ class MceStateMachine extends StateMachine { switch (event.getType()) { case NEW_MESSAGE: // Infer the timestamp for this message as 'now' and read status false // instead of getting the message listing data for it if (!mMessages.containsKey(event.getHandle())) { Calendar calendar = Calendar.getInstance(); Long timestamp = event.getTimestamp(); if (timestamp == null) { // Infer the timestamp for this message as 'now' and read status // false instead of getting the message listing data for it timestamp = new Long(Instant.now().toEpochMilli()); } MessageMetadata metadata = new MessageMetadata(event.getHandle(), calendar.getTime().getTime(), false, MESSAGE_NOT_SEEN); timestamp, false, MESSAGE_NOT_SEEN); mMessages.put(event.getHandle(), metadata); } mMasClient.makeRequest(new RequestGetMessage(event.getHandle(), Loading
android/app/src/com/android/bluetooth/mapclient/obex/EventReport.java +31 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.bluetooth.mapclient; import android.annotation.Nullable; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; Loading @@ -40,6 +41,7 @@ import java.util.HashMap; public class EventReport { private static final String TAG = "EventReport"; private final Type mType; private final String mDateTime; private final String mHandle; private final String mFolder; private final String mOldFolder; Loading Loading @@ -67,6 +69,8 @@ public class EventReport { mOldFolder = attrs.get("old_folder"); mDateTime = attrs.get("datetime"); if (mType != Type.MEMORY_FULL && mType != Type.MEMORY_AVAILABLE) { String s = attrs.get("msg_type"); Loading Loading @@ -183,12 +187,39 @@ public class EventReport { return mMsgType; } /** * @return value corresponding to <code>datetime</code> parameter in MAP * specification for NEW_MESSAGE (can be null) */ @Nullable public String getDateTime() { return mDateTime; } /** * @return timestamp from the value corresponding to <code>datetime</code> parameter in MAP * specification for NEW_MESSAGE (can be null) */ @Nullable public Long getTimestamp() { if (mDateTime != null) { ObexTime obexTime = new ObexTime(mDateTime); if (obexTime != null) { return obexTime.getInstant().toEpochMilli(); } } return null; } @Override public String toString() { JSONObject json = new JSONObject(); try { json.put("type", mType); if (mDateTime != null) { json.put("datetime", mDateTime); } json.put("handle", mHandle); json.put("folder", mFolder); json.put("old_folder", mOldFolder); Loading
android/app/tests/unit/src/com/android/bluetooth/mapclient/EventReportTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,8 @@ public class EventReportTest { EventReport report = EventReport.fromStream(new DataInputStream(stream)); assertThat(report.getType()).isEqualTo(type); assertThat(report.getDateTime()).isNull(); assertThat(report.getTimestamp()).isNull(); assertThat(report.getHandle()).isEqualTo(handle); assertThat(report.getFolder()).isEqualTo(folder); assertThat(report.getOldFolder()).isEqualTo(oldFolder); Loading @@ -76,6 +78,39 @@ public class EventReportTest { assertThat(report).isNull(); } @Test public void fromStreamWithDateTime() throws Exception { EventReport.Type type = EventReport.Type.PARTICIPANT_CHAT_STATE_CHANGED; String handle = "FFAB"; String dateTime = "20190101T121314"; String folder = "test_folder"; String oldFolder = "old_folder"; Bmessage.Type msgType = Bmessage.Type.MMS; final StringBuilder xml = new StringBuilder(); xml.append("<event\n"); xml.append("type=\"" + type.toString() + "\"\n"); xml.append("datetime=\"" + dateTime + "\"\n"); xml.append("handle=\"" + handle + "\"\n"); xml.append("folder=\"" + folder + "\"\n"); xml.append("old_folder=\"" + oldFolder + "\"\n"); xml.append("msg_type=\"" + msgType + "\"\n"); xml.append("/>\n"); ByteArrayInputStream stream = new ByteArrayInputStream(xml.toString().getBytes()); EventReport report = EventReport.fromStream(new DataInputStream(stream)); assertThat(report.getType()).isEqualTo(type); assertThat(report.getDateTime()).isEqualTo(dateTime); assertThat(report.getTimestamp()).isEqualTo( new ObexTime(dateTime).getInstant().toEpochMilli()); assertThat(report.getHandle()).isEqualTo(handle); assertThat(report.getFolder()).isEqualTo(folder); assertThat(report.getOldFolder()).isEqualTo(oldFolder); assertThat(report.getMsgType()).isEqualTo(msgType); assertThat(report.toString()).isNotEmpty(); } @Test public void fromStream_withIOException_doesNotCrash_andReturnNull() throws Exception { InputStream stream = mock(InputStream.class); Loading
android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java +45 −4 Original line number Diff line number Diff line Loading @@ -74,6 +74,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; Loading Loading @@ -627,7 +628,8 @@ public class MapClientStateMachineTest { any(BroadcastOptions.class)); assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED); EventReport event = createNewEventReport("NewMessage", mTestMessageSmsHandle, String dateTime = new ObexTime(Instant.now()).toString(); EventReport event = createNewEventReport("NewMessage", dateTime, mTestMessageSmsHandle, "telecom/msg/inbox", null, "SMS_GSM"); mMceStateMachine.receiveEvent(event); Loading Loading @@ -661,7 +663,8 @@ public class MapClientStateMachineTest { any(BroadcastOptions.class)); assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED); EventReport event = createNewEventReport("NewMessage", mTestMessageMmsHandle, String dateTime = new ObexTime(Instant.now()).toString(); EventReport event = createNewEventReport("NewMessage", dateTime, mTestMessageMmsHandle, "telecom/msg/inbox", null, "MMS"); when(mMockRequestGetMessage.getMessage()).thenReturn(mTestIncomingMmsBmessage); Loading Loading @@ -777,6 +780,43 @@ public class MapClientStateMachineTest { any(), eq(MESSAGE_SEEN)); } /** * Test receiving a new message notification. */ @Test public void testReceiveNewMessageNotification() { setupSdpRecordReceipt(); Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED); mMceStateMachine.sendMessage(msg); verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(BroadcastOptions.class)); assertThat(mMceStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED); // Receive a new message notification. String dateTime = new ObexTime(Instant.now()).toString(); EventReport event = createNewEventReport("NewMessage", dateTime, mTestMessageSmsHandle, "telecom/msg/inbox", null, "SMS_GSM"); Message notificationMessage = Message.obtain(mHandler, MceStateMachine.MSG_NOTIFICATION, (Object)event); mMceStateMachine.getCurrentState().processMessage(notificationMessage); verify(mMockMasClient, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) .makeRequest(any(RequestGetMessage.class)); MceStateMachine.MessageMetadata messageMetadata = mMceStateMachine.mMessages.get(mTestMessageSmsHandle); Assert.assertEquals(messageMetadata.getHandle(), mTestMessageSmsHandle); Assert.assertEquals( new ObexTime(Instant.ofEpochMilli(messageMetadata.getTimestamp())).toString(), dateTime); } private void setupSdpRecordReceipt() { // Perform first part of MAP connection logic. verify(mMockMapClientService, Loading Loading @@ -849,12 +889,13 @@ public class MapClientStateMachineTest { return message; } EventReport createNewEventReport(String mType, String mHandle, String mFolder, String mOldFolder, String mMsgType){ EventReport createNewEventReport(String mType, String mDateTime, String mHandle, String mFolder, String mOldFolder, String mMsgType){ HashMap<String, String> attrs = new HashMap<String, String>(); attrs.put("type", mType); attrs.put("datetime", mDateTime); attrs.put("handle", mHandle); attrs.put("folder", mFolder); attrs.put("old_folder", mOldFolder); Loading