Finer grained character boundaries in computing SMS fragment lengths

The standard Java character iterator has potentially unbounded
distance between character boundaries, meaning that when breaking an
SMS message into fragments it's not safe to assume that the fragment
will contain such a boundary. This patch special-cases flags (pairs of
Regional Indicator Symbols) and also guarantees some progress in the
case of no boundary found.

Bug: 26210851
Change-Id: Ic16637a834af0dfe40197fbcdb3471c1bf5cd6d2
diff --git a/src/java/com/android/internal/telephony/SmsMessageBase.java b/src/java/com/android/internal/telephony/SmsMessageBase.java
index 1d50834..3338772 100644
--- a/src/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/src/java/com/android/internal/telephony/SmsMessageBase.java
@@ -24,6 +24,7 @@
 
 import android.provider.Telephony;
 import android.telephony.SmsMessage;
+import android.text.Emoji;
 
 /**
  * Base class declaring the specific methods and members for SmsMessage.
@@ -367,7 +368,21 @@
             BreakIterator breakIterator = BreakIterator.getCharacterInstance();
             breakIterator.setText(msgBody.toString());
             if (!breakIterator.isBoundary(nextPos)) {
-                nextPos = breakIterator.preceding(nextPos);
+                int breakPos = breakIterator.preceding(nextPos);
+                while (breakPos + 4 <= nextPos
+                        && Emoji.isRegionalIndicatorSymbol(
+                            Character.codePointAt(msgBody, breakPos))
+                        && Emoji.isRegionalIndicatorSymbol(
+                            Character.codePointAt(msgBody, breakPos + 2))) {
+                    // skip forward over flags (pairs of Regional Indicator Symbol)
+                    breakPos += 4;
+                }
+                if (breakPos > currentPosition) {
+                    nextPos = breakPos;
+                } else if (Character.isHighSurrogate(msgBody.charAt(nextPos - 1))) {
+                    // no character boundary in this fragment, try to at least land on a code point
+                    nextPos -= 1;
+                }
             }
         }
         return nextPos;