Merge tag 'android-11.0.0_r48' into int/11/fp3

Android 11.0.0 Release 48 (RD2A.211001.002)

* tag 'android-11.0.0_r48':

Change-Id: I3088ffad48e5ab2ff896bfb8eafa70d08862076a
diff --git a/src/com/android/cellbroadcastservice/GsmAlphabet.java b/src/com/android/cellbroadcastservice/GsmAlphabet.java
index 1efb7be..5565d44 100644
--- a/src/com/android/cellbroadcastservice/GsmAlphabet.java
+++ b/src/com/android/cellbroadcastservice/GsmAlphabet.java
@@ -16,9 +16,13 @@
 
 package com.android.cellbroadcastservice;
 
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseIntArray;
 
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
 /**
  * This class implements the character set mapping between
  * the GSM SMS 7-bit alphabet specified in TS 23.038 6.2.1
@@ -723,4 +727,103 @@
 
         return ret.toString();
     }
+
+    /**
+     * Convert a GSM alphabet string that's stored in 8-bit unpacked
+     * format (as it often appears in SIM records) into a String
+     *
+     * Field may be padded with trailing 0xff's. The decode stops
+     * at the first 0xff encountered.
+     *
+     * @param data the byte array to decode
+     * @param offset array offset for the first character to decode
+     * @param length the number of bytes to decode
+     * @return the decoded string
+     */
+    public static String gsm8BitUnpackedToString(byte[] data, int offset, int length) {
+        return gsm8BitUnpackedToString(data, offset, length, "");
+    }
+
+    /**
+     * Convert a GSM alphabet string that's stored in 8-bit unpacked
+     * format (as it often appears in SIM records) into a String
+     *
+     * Field may be padded with trailing 0xff's. The decode stops
+     * at the first 0xff encountered.
+     *
+     * Additionally, in some country(ex. Korea), there are non-ASCII or MBCS characters.
+     * If a character set is given, characters in data are treat as MBCS.
+     */
+    public static String gsm8BitUnpackedToString(
+            byte[] data, int offset, int length, String characterset) {
+        boolean isMbcs = false;
+        Charset charset = null;
+        ByteBuffer mbcsBuffer = null;
+
+        if (!TextUtils.isEmpty(characterset)
+                && !characterset.equalsIgnoreCase("us-ascii")
+                && Charset.isSupported(characterset)) {
+            isMbcs = true;
+            charset = Charset.forName(characterset);
+            mbcsBuffer = ByteBuffer.allocate(2);
+        }
+
+        // Always use GSM 7 bit default alphabet table for this method
+        String languageTableToChar = sLanguageTables[0];
+        String shiftTableToChar = sLanguageShiftTables[0];
+
+        StringBuilder ret = new StringBuilder(length);
+        boolean prevWasEscape = false;
+        for (int i = offset; i < offset + length; i++) {
+            // Never underestimate the pain that can be caused
+            // by signed bytes
+            int c = data[i] & 0xff;
+
+            if (c == 0xff) {
+                break;
+            } else if (c == GSM_EXTENDED_ESCAPE) {
+                if (prevWasEscape) {
+                    // Two escape chars in a row
+                    // We treat this as a space
+                    // See Note 1 in table 6.2.1.1 of TS 23.038 v7.00
+                    ret.append(' ');
+                    prevWasEscape = false;
+                } else {
+                    prevWasEscape = true;
+                }
+            } else {
+                if (prevWasEscape) {
+                    char shiftChar =
+                            c < shiftTableToChar.length() ? shiftTableToChar.charAt(c) : ' ';
+                    if (shiftChar == ' ') {
+                        // display character from main table if not present in shift table
+                        if (c < languageTableToChar.length()) {
+                            ret.append(languageTableToChar.charAt(c));
+                        } else {
+                            ret.append(' ');
+                        }
+                    } else {
+                        ret.append(shiftChar);
+                    }
+                } else {
+                    if (!isMbcs || c < 0x80 || i + 1 >= offset + length) {
+                        if (c < languageTableToChar.length()) {
+                            ret.append(languageTableToChar.charAt(c));
+                        } else {
+                            ret.append(' ');
+                        }
+                    } else {
+                        // isMbcs must be true. So both mbcsBuffer and charset are initialized.
+                        mbcsBuffer.clear();
+                        mbcsBuffer.put(data, i++, 2);
+                        mbcsBuffer.flip();
+                        ret.append(charset.decode(mbcsBuffer).toString());
+                    }
+                }
+                prevWasEscape = false;
+            }
+        }
+
+        return ret.toString();
+    }
 }
diff --git a/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java b/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
index 2e14c83..d5d39d7 100644
--- a/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
+++ b/src/com/android/cellbroadcastservice/GsmSmsCbMessage.java
@@ -390,6 +390,12 @@
                 }
                 break;
 
+            case SmsMessage.ENCODING_8BIT:
+                // Support decoding the pdu as pack GSM 8-bit (a GSM alphabet string that's stored
+                // in 8-bit unpacked format) characters.
+                body = GsmAlphabet.gsm8BitUnpackedToString(pdu, offset, length);
+                break;
+
             case SmsMessage.ENCODING_16BIT:
                 if (dcs.hasLanguageIndicator && pdu.length >= offset + 2) {
                     // Language is two GSM characters.
diff --git a/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java b/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java
index e1c9308..0b03f4d 100644
--- a/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java
+++ b/tests/src/com/android/cellbroadcastservice/tests/GsmSmsCbMessageTest.java
@@ -579,37 +579,28 @@
     }
 
     @Test
-    public void testGetMessageBody8Bit() {
+    public void testGetMessageBodyGsm8Bit() {
         byte[] pdu = {
                 (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x32, (byte) 0x44, (byte) 0x11,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47,
-                (byte) 0x41,
-                (byte) 0x42, (byte) 0x43, (byte) 0x44, (byte) 0x45
+                (byte) 0x41, (byte) 0x20, (byte) 0x47, (byte) 0x53, (byte) 0x4D, (byte) 0x20,
+                (byte) 0x38, (byte) 0x2D, (byte) 0x62, (byte) 0x69, (byte) 0x74, (byte) 0x20,
+                (byte) 0x6D, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x61, (byte) 0x67,
+                (byte) 0x65, (byte) 0x20, (byte) 0x77, (byte) 0x69, (byte) 0x74, (byte) 0x68,
+                (byte) 0x20, (byte) 0x63, (byte) 0x61, (byte) 0x72, (byte) 0x72, (byte) 0x69,
+                (byte) 0x61, (byte) 0x67, (byte) 0x65, (byte) 0x20, (byte) 0x72, (byte) 0x65,
+                (byte) 0x74, (byte) 0x75, (byte) 0x72, (byte) 0x6E, (byte) 0x20, (byte) 0x70,
+                (byte) 0x61, (byte) 0x64, (byte) 0x64, (byte) 0x69, (byte) 0x6E, (byte) 0x67,
+                (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+                (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+                (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+                (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+                (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D,
+                (byte) 0x0D, (byte) 0x0D, (byte) 0x0D, (byte) 0x0D
         };
         SmsCbMessage msg = createFromPdu(pdu);
 
-        assertEquals("8-bit message body should be empty", "", msg.getMessageBody());
+        assertEquals("Unexpected 8-bit string decoded",
+                "A GSM 8-bit message with carriage return padding", msg.getMessageBody());
     }
 
     @Test
@@ -643,7 +634,7 @@
         };
         SmsCbMessage msg = createFromPdu(pdu);
 
-        assertEquals("Unexpected 7-bit string decoded",
+        assertEquals("Unexpected UCS2 string decoded",
                 "A UCS2 message containing a \u0434 character", msg.getMessageBody());
     }
 
@@ -673,7 +664,7 @@
         };
         SmsCbMessage msg = createFromPdu(pdu);
 
-        assertEquals("Unexpected 7-bit string decoded",
+        assertEquals("Unexpected UCS2 decoded",
                 "A UCS2 message containing a \u0434 character", msg.getMessageBody());
     }
 
@@ -755,7 +746,7 @@
         };
         SmsCbMessage msg = createFromPdu(pdu);
 
-        assertEquals("Unexpected 7-bit string decoded",
+        assertEquals("Unexpected UCS2 string decoded",
                 "A UCS2 message containing a \u0434 character", msg.getMessageBody());
 
         assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
@@ -787,7 +778,7 @@
         };
         SmsCbMessage msg = createFromPdu(pdu);
 
-        assertEquals("Unexpected 7-bit string decoded",
+        assertEquals("Unexpected UCS2 string decoded",
                 "A UCS2 message containing a \u0434 character", msg.getMessageBody());
 
         assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());