Merge "Snap for 7110675 from 4f1685621b991185fcb8c05cb5dc33f47ee10ec4 to sdk-release" into sdk-release
diff --git a/assets/latest_carrier_id/carrier_list.pb b/assets/latest_carrier_id/carrier_list.pb
index 486f1ae..0770454 100644
--- a/assets/latest_carrier_id/carrier_list.pb
+++ b/assets/latest_carrier_id/carrier_list.pb
Binary files differ
diff --git a/assets/latest_carrier_id/carrier_list.textpb b/assets/latest_carrier_id/carrier_list.textpb
index 60064d6..7d22143 100644
--- a/assets/latest_carrier_id/carrier_list.textpb
+++ b/assets/latest_carrier_id/carrier_list.textpb
Binary files differ
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 309a57b..9bcbd19 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -16,6 +16,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" product="tablet" msgid="9194799012395299737">"تهيئة شبكة الجوال"</string>
+    <string name="app_label" product="tablet" msgid="9194799012395299737">"إعداد شبكة الجوال"</string>
     <string name="app_label" product="default" msgid="8338087656149558019">"مساحة تخزين للهاتف والرسائل"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 1c1c927..dc13503 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -17,5 +17,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" product="tablet" msgid="9194799012395299737">"Мобайль Сүлжээний Тохируулга"</string>
-    <string name="app_label" product="default" msgid="8338087656149558019">"Гар утас болон Зурвасын Санах ой"</string>
+    <string name="app_label" product="default" msgid="8338087656149558019">"Гар утас болон Мессежийн Санах ой"</string>
 </resources>
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index f5aeadc..8e01bcc 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -305,7 +305,6 @@
     private SQLiteOpenHelper mOpenHelper;
 
     private boolean mUseStrictPhoneNumberComparation;
-    private int mMinMatch;
 
     private static final String METHOD_IS_RESTORING = "is_restoring";
     private static final String IS_RESTORING_KEY = "restoring";
@@ -317,9 +316,6 @@
         mUseStrictPhoneNumberComparation =
             getContext().getResources().getBoolean(
                     com.android.internal.R.bool.config_use_strict_phone_number_comparation);
-        mMinMatch =
-            getContext().getResources().getInteger(
-                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
         TelephonyBackupAgent.DeferredSmsMmsRestoreService.startIfFilesExist(getContext());
         return true;
     }
@@ -551,12 +547,15 @@
         String selection = "address=?";
         String[] selectionArgs;
         long retVal = -1L;
+        int minMatch =
+            getContext().getResources().getInteger(
+                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
 
         if (!isPhoneNumber) {
             selectionArgs = new String[] { refinedAddress };
         } else {
             selection += " OR PHONE_NUMBERS_EQUAL(address, ?, " +
-                        (mUseStrictPhoneNumberComparation ? "1)" : "0, " + mMinMatch + ")");
+                        (mUseStrictPhoneNumberComparation ? "1)" : "0, " + minMatch + ")");
             selectionArgs = new String[] { refinedAddress, refinedAddress };
         }
 
@@ -1011,19 +1010,22 @@
      *   FROM pdu, (SELECT msg_id AS address_msg_id
      *              FROM addr
      *              WHERE (address='<phoneNumber>' OR
-     *              PHONE_NUMBERS_EQUAL(addr.address, '<phoneNumber>', 1/0, none/mMinMatch)))
+     *              PHONE_NUMBERS_EQUAL(addr.address, '<phoneNumber>', 1/0, none/minMatch)))
      *             AS matching_addresses
      *   WHERE pdu._id = matching_addresses.address_msg_id
      * UNION
      * SELECT ...
      *   FROM sms
      *   WHERE (address='<phoneNumber>' OR
-     *          PHONE_NUMBERS_EQUAL(sms.address, '<phoneNumber>', 1/0, none/mMinMatch));
+     *          PHONE_NUMBERS_EQUAL(sms.address, '<phoneNumber>', 1/0, none/minMatch));
      */
     private Cursor getMessagesByPhoneNumber(
             String phoneNumber, String[] projection, String selection,
             String sortOrder, String smsTable, String pduTable) {
         String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(phoneNumber);
+        int minMatch =
+            getContext().getResources().getInteger(
+                    com.android.internal.R.integer.config_phonenumber_compare_min_match);
         String finalMmsSelection =
                 concatSelections(
                         selection,
@@ -1033,7 +1035,7 @@
                         selection,
                         "(address=" + escapedPhoneNumber + " OR PHONE_NUMBERS_EQUAL(address, " +
                         escapedPhoneNumber +
-                        (mUseStrictPhoneNumberComparation ? ", 1))" : ", 0, " + mMinMatch + "))"));
+                        (mUseStrictPhoneNumberComparation ? ", 1))" : ", 0, " + minMatch + "))"));
         SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
         SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
 
@@ -1045,7 +1047,7 @@
                 "FROM addr WHERE (address=" + escapedPhoneNumber +
                 " OR PHONE_NUMBERS_EQUAL(addr.address, " +
                 escapedPhoneNumber +
-                (mUseStrictPhoneNumberComparation ? ", 1))) " : ", 0, " + mMinMatch + "))) ") +
+                (mUseStrictPhoneNumberComparation ? ", 1))) " : ", 0, " + minMatch + "))) ") +
                 "AS matching_addresses");
         smsQueryBuilder.setTables(smsTable);
 
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
index 958c929..6ce5da1 100644
--- a/src/com/android/providers/telephony/TelephonyBackupAgent.java
+++ b/src/com/android/providers/telephony/TelephonyBackupAgent.java
@@ -631,12 +631,16 @@
         ContentValues[] values = new ContentValues[bulkInsertSize];
         while (jsonReader.hasNext()) {
             ContentValues cv = readSmsValuesFromReader(jsonReader);
-            if (doesSmsExist(cv)) {
-                continue;
-            }
-            values[(msgCount++) % bulkInsertSize] = cv;
-            if (msgCount % bulkInsertSize == 0) {
-                mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI, values);
+            try {
+                if (mSmsProviderQuery.doesSmsExist(cv)) {
+                    continue;
+                }
+                values[(msgCount++) % bulkInsertSize] = cv;
+                if (msgCount % bulkInsertSize == 0) {
+                    mContentResolver.bulkInsert(Telephony.Sms.CONTENT_URI, values);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "putSmsMessagesToProvider", e);
             }
         }
         if (msgCount % bulkInsertSize > 0) {
@@ -655,16 +659,20 @@
             if (DEBUG) {
                 Log.d(TAG, "putMmsMessagesToProvider " + mms);
             }
-            if (doesMmsExist(mms)) {
-                if (DEBUG) {
-                    Log.e(TAG, String.format("Mms: %s already exists", mms.toString()));
-                } else {
-                    Log.w(TAG, "Mms: Found duplicate MMS");
+            try {
+                if (doesMmsExist(mms)) {
+                    if (DEBUG) {
+                        Log.e(TAG, String.format("Mms: %s already exists", mms.toString()));
+                    } else {
+                        Log.w(TAG, "Mms: Found duplicate MMS");
+                    }
+                    continue;
                 }
-                continue;
+                total++;
+                addMmsMessage(mms);
+            } catch (Exception e) {
+                Log.e(TAG, "putMmsMessagesToProvider", e);
             }
-            total++;
-            addMmsMessage(mms);
         }
         Log.d(TAG, "putMmsMessagesToProvider handled " + total + " new messages.");
     }
@@ -673,15 +681,34 @@
     static final String[] PROJECTION_ID = {BaseColumns._ID};
     private static final int ID_IDX = 0;
 
-    private boolean doesSmsExist(ContentValues smsValues) {
-        final String where = String.format(Locale.US, "%s = %d and %s = %s",
-                Telephony.Sms.DATE, smsValues.getAsLong(Telephony.Sms.DATE),
-                Telephony.Sms.BODY,
-                DatabaseUtils.sqlEscapeString(smsValues.getAsString(Telephony.Sms.BODY)));
-        try (Cursor cursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, PROJECTION_ID, where,
-                null, null)) {
-            return cursor != null && cursor.getCount() > 0;
+    /**
+     * Interface to allow mocking method for testing.
+     */
+    public interface SmsProviderQuery {
+        boolean doesSmsExist(ContentValues smsValues);
+    }
+
+    private SmsProviderQuery mSmsProviderQuery = new SmsProviderQuery() {
+        @Override
+        public boolean doesSmsExist(ContentValues smsValues) {
+            // The SMS body might contain '\0' characters (U+0000) such as in the case of
+            // http://b/160801497 . SQLite does not allow '\0' in String literals, but as of SQLite
+            // version 3.32.2 2020-06-04, it does allow them as selectionArgs; therefore, we're
+            // using the latter approach here.
+            final String selection = String.format(Locale.US, "%s=%d AND %s=?",
+                    Telephony.Sms.DATE, smsValues.getAsLong(Telephony.Sms.DATE),
+                    Telephony.Sms.BODY);
+            String[] selectionArgs = new String[] { smsValues.getAsString(Telephony.Sms.BODY)};
+            try (Cursor cursor = mContentResolver.query(Telephony.Sms.CONTENT_URI, PROJECTION_ID,
+                    selection, selectionArgs, null)) {
+                return cursor != null && cursor.getCount() > 0;
+            }
         }
+    };
+
+    @VisibleForTesting
+    public void setSmsProviderQuery(SmsProviderQuery smsProviderQuery) {
+        mSmsProviderQuery = smsProviderQuery;
     }
 
     private boolean doesMmsExist(Mms mms) {
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 98bc81e..8e89ab3 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -128,6 +128,7 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.Integer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -147,7 +148,7 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false; // STOPSHIP if true
 
-    private static final int DATABASE_VERSION = 45 << 16;
+    private static final int DATABASE_VERSION = 47 << 16;
     private static final int URL_UNKNOWN = 0;
     private static final int URL_TELEPHONY = 1;
     private static final int URL_CURRENT = 2;
@@ -470,7 +471,9 @@
                 + Telephony.SimInfo.COLUMN_IMSI + " TEXT,"
                 + Telephony.SimInfo.COLUMN_UICC_APPLICATIONS_ENABLED + " INTEGER DEFAULT 1,"
                 + Telephony.SimInfo.COLUMN_ALLOWED_NETWORK_TYPES + " BIGINT DEFAULT -1,"
-                + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0"
+                + Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED + " INTEGER DEFAULT 0,"
+                + Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED + " INTEGER DEFAULT 0,"
+                + Telephony.SimInfo.COLUMN_RCS_CONFIG + " BLOB"
                 + ");";
     }
 
@@ -752,7 +755,7 @@
                 try {
                     XmlUtils.beginDocument(parser, "apns");
                     publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
-                    loadApns(db, parser);
+                    loadApns(db, parser, true);
                 } catch (Exception e) {
                     loge("Got exception while loading APN database." + e);
                 } finally {
@@ -782,7 +785,7 @@
                             + confFile.getAbsolutePath());
                 }
 
-                loadApns(db, confparser);
+                loadApns(db, confparser, false);
             } catch (FileNotFoundException e) {
                 // It's ok if the file isn't found. It means there isn't a confidential file
                 // Log.e(TAG, "File not found: '" + confFile.getAbsolutePath() + "'");
@@ -1484,6 +1487,36 @@
                 oldVersion = 45 << 16 | 6;
             }
 
+            if (oldVersion < (46 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED
+                            + " INTEGER DEFAULT 0;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                "The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 46 << 16 | 6;
+            }
+
+            if (oldVersion < (47 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE + " ADD COLUMN "
+                            + Telephony.SimInfo.COLUMN_RCS_CONFIG
+                            + " BLOB;");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                "The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 47 << 16 | 6;
+            }
+
             if (DBG) {
                 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
             }
@@ -1669,7 +1702,7 @@
                 try {
                     XmlUtils.nextElement(parser);
                     while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        ContentValues row = getRow(parser);
+                        ContentValues row = getRow(parser, false);
                         if (row == null) {
                             throw new XmlPullParserException("Expected 'apn' tag", parser, null);
                         }
@@ -2076,9 +2109,10 @@
          * Gets the next row of apn values.
          *
          * @param parser the parser
+         * @param isOverlay If the xml file comes from an overlay MCC/MNC are treated as integers
          * @return the row or null if it's not an apn
          */
-        private ContentValues getRow(XmlPullParser parser) {
+        private ContentValues getRow(XmlPullParser parser, boolean isOverlay) {
             if (!"apn".equals(parser.getName())) {
                 return null;
             }
@@ -2087,11 +2121,21 @@
 
             String mcc = parser.getAttributeValue(null, "mcc");
             String mnc = parser.getAttributeValue(null, "mnc");
-            String numeric = mcc + mnc;
+            String mccString = mcc;
+            String mncString = mnc;
+            // Since an mnc can have both two and three digits and it is hard to verify
+            // all OEM's Global APN lists we only do this for overlays.
+            if (isOverlay) {
+                mccString = String.format("%03d", Integer.parseInt(mcc));
+                // Looks up a two digit mnc in the carrier id DB
+                // if not found a three digit mnc value is chosen
+                mncString = getBestStringMnc(mContext, mccString, Integer.parseInt(mnc));
+            }
 
+            String numeric = mccString + mncString;
             map.put(NUMERIC, numeric);
-            map.put(MCC, mcc);
-            map.put(MNC, mnc);
+            map.put(MCC, mccString);
+            map.put(MNC, mncString);
             map.put(NAME, parser.getAttributeValue(null, "carrier"));
 
             // do not add NULL to the map so that default values can be inserted in db
@@ -2160,7 +2204,6 @@
                     map.put(MVNO_MATCH_DATA, mvno_match_data);
                 }
             }
-
             return map;
         }
 
@@ -2193,15 +2236,15 @@
          *
          * @param db the sqlite database to write to
          * @param parser the xml parser
-         *
+         * @param isOverlay, if we are parsing an xml in an overlay
          */
-        private void loadApns(SQLiteDatabase db, XmlPullParser parser) {
+        private void loadApns(SQLiteDatabase db, XmlPullParser parser, boolean isOverlay) {
             if (parser != null) {
                 try {
                     db.beginTransaction();
                     XmlUtils.nextElement(parser);
                     while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
-                        ContentValues row = getRow(parser);
+                        ContentValues row = getRow(parser, isOverlay);
                         if (row == null) {
                             throw new XmlPullParserException("Expected 'apn' tag", parser, null);
                         }
@@ -3474,6 +3517,7 @@
                 if (initialValues != null) {
                     if(initialValues.containsKey(COLUMN_APN_ID)) {
                         setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId, true);
+                        notify = true;
                     }
                 }
                 break;
@@ -3923,6 +3967,12 @@
                                         Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED), usingSubId, subId),
                                 null, true, UserHandle.USER_ALL);
                     }
+                    if (values.containsKey(Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED)) {
+                        getContext().getContentResolver().notifyChange(getNotifyContentUri(
+                                Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+                                        Telephony.SimInfo.COLUMN_CROSS_SIM_CALLING_ENABLED),
+                                usingSubId, subId), null, true, UserHandle.USER_ALL);
+                    }
                     break;
                 default:
                     getContext().getContentResolver().notifyChange(
diff --git a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
index 643a18f..00bb15e 100644
--- a/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyBackupAgentTest.java
@@ -615,6 +615,35 @@
     }
 
     /**
+     * Test that crashing for one sms does not block restore of other messages.
+     * @throws Exception
+     */
+    public void testRestoreSms_WithException() throws Exception {
+        mTelephonyBackupAgent.initUnknownSender();
+        JsonReader jsonReader = new JsonReader(new StringReader(addRandomDataToJson(mAllSmsJson)));
+        FakeSmsProvider smsProvider = new FakeSmsProvider(mSmsRows, false);
+        mMockContentResolver.addProvider("sms", smsProvider);
+        TelephonyBackupAgent.SmsProviderQuery smsProviderQuery =
+                new TelephonyBackupAgent.SmsProviderQuery() {
+                    int mIteration = 0;
+                    @Override
+                    public boolean doesSmsExist(ContentValues smsValues) {
+                        if (mIteration == 0) {
+                            mIteration++;
+                            throw new RuntimeException("fake crash for first message");
+                        }
+                        return false;
+                    }
+        };
+        mTelephonyBackupAgent.setSmsProviderQuery(smsProviderQuery);
+
+        mTelephonyBackupAgent.putSmsMessagesToProvider(jsonReader);
+        // the "- 1" is due to exception thrown for one of the messages
+        assertEquals(mSmsRows.length - 1, smsProvider.getRowsAdded());
+        assertEquals(mThreadProvider.mIsThreadArchived, mThreadProvider.mUpdateThreadsArchived);
+    }
+
+    /**
      * Test restore mms with the empty json array "[]".
      * @throws Exception
      */
@@ -750,11 +779,17 @@
     private class FakeSmsProvider extends MockContentProvider {
         private int nextRow = 0;
         private ContentValues[] mSms;
+        private boolean mCheckInsertedValues = true;
 
         public FakeSmsProvider(ContentValues[] sms) {
             this.mSms = sms;
         }
 
+        public FakeSmsProvider(ContentValues[] sms, boolean checkInsertedValues) {
+            this.mSms = sms;
+            mCheckInsertedValues = checkInsertedValues;
+        }
+
         @Override
         public Uri insert(Uri uri, ContentValues values) {
             assertEquals(Telephony.Sms.CONTENT_URI, uri);
@@ -770,7 +805,7 @@
                 modifiedValues.put(Telephony.Sms.ADDRESS, TelephonyBackupAgent.UNKNOWN_SENDER);
             }
 
-            assertEquals(modifiedValues, values);
+            if (mCheckInsertedValues) assertEquals(modifiedValues, values);
             return null;
         }
 
diff --git a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
index 0666169..50be3b3 100644
--- a/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
+++ b/tests/src/com/android/providers/telephony/TelephonyDatabaseHelperTest.java
@@ -201,6 +201,22 @@
                 Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED));
     }
 
+    @Test
+    public void databaseHelperOnUpgrade_hasRcsConfigField() {
+        Log.d(TAG, "databaseHelperOnUpgrade_hasRcsConfigField");
+        // (5 << 16 | 6) is the first upgrade trigger in onUpgrade
+        SQLiteDatabase db = mInMemoryDbHelper.getWritableDatabase();
+        mHelper.onUpgrade(db, (4 << 16), TelephonyProvider.getVersion(mContext));
+
+        // the upgraded db must have the Telephony.SimInfo.COLUMN_RCS_CONFIG field
+        Cursor cursor = db.query("siminfo", null, null, null, null, null, null);
+        String[] upgradedColumns = cursor.getColumnNames();
+        Log.d(TAG, "siminfo columns: " + Arrays.toString(upgradedColumns));
+
+        assertTrue(Arrays.asList(upgradedColumns).contains(
+                Telephony.SimInfo.COLUMN_RCS_CONFIG));
+    }
+
     /**
      * Helper for an in memory DB used to test the TelephonyProvider#DatabaseHelper.
      *