Fixing iPhone SMS handling
iPhones don't return senderContactUri (encoded phone #) via MAP (and don't
support sending messages over MAP either). To address this:
- Updated MapMessageMonitor to use combination of senderName/senderContactUri
in SenderKey. Auto-reply logic aborts if senderContactUri is not known.
- Also updated MapMessage docs.
- I am independently waiting on b/36368127 which is to provide a MAP API
indicating if MAP device supports send. Once that's addressed we will
no longer provide auto-reply as an option for iPhone like devices.
Bug: 33280056
Test: Receiving and send auto-reply with iPhone and Android (Pixel,
Samsung)
Change-Id: If4887db8cad721b5aeae033253599e1de2b8816c
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5d0391a..d59587d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,9 +19,10 @@
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="25"/>
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application android:label="CarMessenger">
<service android:name=".MessengerService" android:exported="false">
diff --git a/src/com/android/car/messenger/MapMessage.java b/src/com/android/car/messenger/MapMessage.java
index 1369976..2bca390 100644
--- a/src/com/android/car/messenger/MapMessage.java
+++ b/src/com/android/car/messenger/MapMessage.java
@@ -28,11 +28,10 @@
private BluetoothDevice mDevice;
private String mHandle;
private long mReceivedTimeMs;
- private String mText;
+ private String mSenderName;
@Nullable
private String mSenderContactUri;
- @Nullable
- private String mSenderName;
+ private String mText;
/**
* Constructs Message from {@code intent} that was received from MAP service via
@@ -50,18 +49,19 @@
String senderContactName = intent.getStringExtra(
BluetoothMapClient.EXTRA_SENDER_CONTACT_NAME);
String text = intent.getStringExtra(android.content.Intent.EXTRA_TEXT);
- return new MapMessage(device, handle, System.currentTimeMillis(), text,
- senderContactUri, senderContactName);
+ return new MapMessage(device, handle, System.currentTimeMillis(), senderContactName,
+ senderContactUri, text);
}
private MapMessage(BluetoothDevice device,
String handle,
long receivedTimeMs,
- String text,
+ String senderName,
@Nullable String senderContactUri,
- @Nullable String senderName) {
+ String text) {
boolean missingDevice = (device == null);
boolean missingHandle = (handle == null);
+ boolean missingSenderName = (senderName == null);
boolean missingText = (text == null);
if (missingDevice || missingHandle || missingText) {
StringBuilder builder = new StringBuilder("Missing required fields:");
@@ -71,6 +71,9 @@
if (missingHandle) {
builder.append(" handle");
}
+ if (missingSenderName) {
+ builder.append(" senderName");
+ }
if (missingText) {
builder.append(" text");
}
@@ -88,26 +91,43 @@
return mDevice;
}
+ /**
+ * @return Unique handle for this message. NOTE: The handle is only required to be unique for
+ * the lifetime of a single MAP session.
+ */
public String getHandle() {
return mHandle;
}
+ /**
+ * @return Milliseconds since epoch at which this message notification was received on the head-
+ * unit.
+ */
public long getReceivedTimeMs() {
return mReceivedTimeMs;
}
- public String getText() {
- return mText;
+ /**
+ * @return Contact name as obtained from the device. If contact is in the device's address-book,
+ * this is typically the contact name. Otherwise it will be the phone number.
+ */
+ public String getSenderName() {
+ return mSenderName;
}
+ /**
+ * @return Sender phone number available as a URI string. iPhone's don't provide these.
+ */
@Nullable
public String getSenderContactUri() {
return mSenderContactUri;
}
- @Nullable
- public String getSenderName() {
- return mSenderName;
+ /**
+ * @return Actual content of the message.
+ */
+ public String getText() {
+ return mText;
}
@Override
diff --git a/src/com/android/car/messenger/MapMessageMonitor.java b/src/com/android/car/messenger/MapMessageMonitor.java
index 0de9d1e..926579d 100644
--- a/src/com/android/car/messenger/MapMessageMonitor.java
+++ b/src/com/android/car/messenger/MapMessageMonitor.java
@@ -28,6 +28,7 @@
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.widget.Toast;
@@ -87,12 +88,12 @@
}
}
- // TODO(sriniv): Handle unknown senders. b/33280056
private void updateNotificationInfo(MapMessage message, MessageKey messageKey) {
SenderKey senderKey = new SenderKey(message);
NotificationInfo notificationInfo = mNotificationInfos.get(senderKey);
if (notificationInfo == null) {
- notificationInfo = new NotificationInfo(message.getSenderName());
+ notificationInfo =
+ new NotificationInfo(message.getSenderName(), message.getSenderContactUri());
mNotificationInfos.put(senderKey, notificationInfo);
}
notificationInfo.mMessageKeys.add(messageKey);
@@ -192,7 +193,16 @@
}
BluetoothDevice device =
BluetoothAdapter.getDefaultAdapter().getRemoteDevice(senderKey.mDeviceAddress);
- Uri recipientUris[] = { Uri.parse(senderKey.mSubKey) };
+ NotificationInfo notificationInfo = mNotificationInfos.get(senderKey);
+ if (notificationInfo == null) {
+ Log.w(TAG, "No notificationInfo found for senderKey: " + senderKey);
+ return false;
+ }
+ if (notificationInfo.mSenderContactUri == null) {
+ Log.w(TAG, "Do not have contact URI for sender!");
+ return false;
+ }
+ Uri recipientUris[] = { Uri.parse(notificationInfo.mSenderContactUri) };
final int requestCode = senderKey.hashCode();
PendingIntent sentIntent =
@@ -264,7 +274,9 @@
}
}
- // Key used in HashMap that is composed from a BT device-address and device-specific "sub key"
+ /**
+ * Key used in HashMap that is composed from a BT device-address and device-specific "sub key"
+ */
private abstract static class CompositeKey {
final String mDeviceAddress;
final String mSubKey;
@@ -304,22 +316,32 @@
}
}
- // CompositeKey used to identify specific messages; it uses message-handle as the secondary key.
+ /**
+ * {@link CompositeKey} subclass used to identify specific messages; it uses message-handle as
+ * the secondary key.
+ */
private static class MessageKey extends CompositeKey {
MessageKey(MapMessage message) {
super(message.getDevice().getAddress(), message.getHandle());
}
}
- // CompositeKey used to identify Notification info for a sender. It uses senderContactUri as
- // the secondary key.
+ /**
+ * CompositeKey used to identify Notification info for a sender; it uses a combination of
+ * senderContactUri and senderContactName as the secondary key.
+ */
static class SenderKey extends CompositeKey implements Parcelable {
private SenderKey(String deviceAddress, String key) {
super(deviceAddress, key);
}
SenderKey(MapMessage message) {
- this(message.getDevice().getAddress(), message.getSenderContactUri());
+ // Use a combination of senderName and senderContactUri for key. Ideally we would use
+ // only senderContactUri (which is encoded phone no.). However since some phones don't
+ // provide these, we fall back to senderName. Since senderName may not be unique, we
+ // include senderContactUri also to provide uniqueness in cases it is available.
+ this(message.getDevice().getAddress(),
+ message.getSenderName() + "/" + message.getSenderContactUri());
}
@Override
@@ -347,16 +369,21 @@
};
}
- // Information about a single notification displayed.
+ /**
+ * Information about a single notification that is displayed.
+ */
private static class NotificationInfo {
private static int NEXT_NOTIFICATION_ID = 0;
final int mNotificationId = NEXT_NOTIFICATION_ID++;
final String mSenderName;
+ @Nullable
+ final String mSenderContactUri;
final List<MessageKey> mMessageKeys = new LinkedList<>();
- NotificationInfo(String senderName) {
+ NotificationInfo(String senderName, @Nullable String senderContactUri) {
mSenderName = senderName;
+ mSenderContactUri = senderContactUri;
}
}
}
diff --git a/src/com/android/car/messenger/MessengerService.java b/src/com/android/car/messenger/MessengerService.java
index 963d17d..c2de6aa 100644
--- a/src/com/android/car/messenger/MessengerService.java
+++ b/src/com/android/car/messenger/MessengerService.java
@@ -111,14 +111,15 @@
}
switch (intent.getAction()) {
case ACTION_AUTO_REPLY:
- boolean failed = true;
+ boolean success;
if (mMapClient != null) {
- failed = mMessageMonitor.sendAutoReply(
+ success = mMessageMonitor.sendAutoReply(
intent.getParcelableExtra(EXTRA_SENDER_KEY), mMapClient);
} else {
Log.e(TAG, "Unable to send reply; MAP profile disconnected!");
+ success = false;
}
- if (failed) {
+ if (!success) {
Toast.makeText(this, R.string.auto_reply_failed_message, Toast.LENGTH_SHORT)
.show();
}