Merge "Ensure integrity of sim-settings backupfile with AtomicFile." into sc-dev
diff --git a/assets/latest_carrier_id/carrier_list.pb b/assets/latest_carrier_id/carrier_list.pb
index 1dbde32..ebdaf74 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 3d15780..c9b97c5 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/config.xml b/res/values/config.xml
index e4be55f..efe772f 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -15,9 +15,10 @@
          conf xml file.  -->
     <string name="apn_source_service" translatable="false"></string>
 
-    <!-- Countries, in iso country code format, where wfc entitlement is
-         required-->
-    <string-array name="wfc_entitlement_required_countries">
+    <!-- Countries, in iso country code format, where WFC entitlement or
+         other user action is required before user can turn on WFC setting,
+         hence WFC setting should not be restored automatically. -->
+    <string-array name="wfc_restore_blocked_countries" translatable="false">
         <item>us</item>
         <item>ca</item>
     </string-array>
diff --git a/src/com/android/providers/telephony/TelephonyBackupAgent.java b/src/com/android/providers/telephony/TelephonyBackupAgent.java
index 6032f5c..34fed99 100644
--- a/src/com/android/providers/telephony/TelephonyBackupAgent.java
+++ b/src/com/android/providers/telephony/TelephonyBackupAgent.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
+import android.preference.PreferenceManager;
 import android.provider.BaseColumns;
 import android.provider.Telephony;
 import android.telephony.PhoneNumberUtils;
@@ -50,6 +51,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneFactory;
 
 import com.google.android.mms.ContentType;
 import com.google.android.mms.pdu.CharacterSets;
@@ -114,6 +116,15 @@
     private static final boolean DEBUG = false;
     private static volatile boolean sIsRestoring;
 
+    // SharedPreferences keys
+    private static final String NUM_SMS_RESTORED = "num_sms_restored";
+    private static final String NUM_SMS_EXCEPTIONS = "num_sms_exceptions";
+    private static final String NUM_SMS_FILES_STORED = "num_sms_files_restored";
+    private static final String NUM_SMS_FILES_WITH_EXCEPTIONS = "num_sms_files_with_exceptions";
+    private static final String NUM_MMS_RESTORED = "num_mms_restored";
+    private static final String NUM_MMS_EXCEPTIONS = "num_mms_exceptions";
+    private static final String NUM_MMS_FILES_STORED = "num_mms_files_restored";
+    private static final String NUM_MMS_FILES_WITH_EXCEPTIONS = "num_mms_files_with_exceptions";
 
     // Copied from packages/apps/Messaging/src/com/android/messaging/sms/MmsUtils.java.
     private static final int DEFAULT_DURATION = 5000; //ms
@@ -316,7 +327,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-
+        Log.d(TAG, "onCreate");
         final SubscriptionManager subscriptionManager = SubscriptionManager.from(this);
         if (subscriptionManager != null) {
             final List<SubscriptionInfo> subInfo =
@@ -505,6 +516,28 @@
 
     public static class DeferredSmsMmsRestoreService extends IntentService {
         private static final String TAG = "DeferredSmsMmsRestoreService";
+        private static boolean sSharedPrefsAddedToLocalLogs = false;
+
+        public static void addAllSharedPrefToLocalLog(Context context) {
+            if (sSharedPrefsAddedToLocalLogs) return;
+            localLog("addAllSharedPrefToLocalLog");
+            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+            Map<String, ?> allPref = sp.getAll();
+            if (allPref.keySet() == null || allPref.keySet().size() == 0) return;
+            for (String key : allPref.keySet()) {
+                try {
+                    localLog(key + ":" + allPref.get(key).toString());
+                } catch (Exception e) {
+                    localLog("Skipping over key " + key + " due to exception " + e);
+                }
+            }
+            sSharedPrefsAddedToLocalLogs = true;
+        }
+
+        public static void localLog(String logMsg) {
+            Log.d(TAG, logMsg);
+            PhoneFactory.localLog(TAG, logMsg);
+        }
 
         private final Comparator<File> mFileComparator = new Comparator<File>() {
             @Override
@@ -515,6 +548,7 @@
 
         public DeferredSmsMmsRestoreService() {
             super(TAG);
+            Log.d(TAG, "DeferredSmsMmsRestoreService");
             setIntentRedelivery(true);
         }
 
@@ -523,6 +557,7 @@
 
         @Override
         protected void onHandleIntent(Intent intent) {
+            Log.d(TAG, "onHandleIntent");
             try {
                 mWakeLock.acquire();
                 sIsRestoring = true;
@@ -545,6 +580,7 @@
                     } catch (Exception e) {
                         // Either IOException or RuntimeException.
                         Log.e(TAG, "onHandleIntent", e);
+                        localLog("onHandleIntent: Exception " + e);
                     } finally {
                         file.delete();
                     }
@@ -552,11 +588,12 @@
                 if (didRestore) {
                   // Tell the default sms app to do a full sync now that the messages have been
                   // restored.
-                  Log.d(TAG, "onHandleIntent done - notifying default sms app");
+                  localLog("onHandleIntent: done - notifying default sms app");
                   ProviderUtil.notifyIfNotDefaultSmsApp(null /*uri*/, null /*calling package*/,
                       this);
                 }
            } finally {
+                addAllSharedPrefToLocalLog(this);
                 sIsRestoring = false;
                 mWakeLock.release();
             }
@@ -565,6 +602,12 @@
         @Override
         public void onCreate() {
             super.onCreate();
+            Log.d(TAG, "onCreate");
+            try {
+                PhoneFactory.addLocalLog(TAG, 32);
+            } catch (IllegalArgumentException e) {
+                // ignore
+            }
             mTelephonyBackupAgent = new TelephonyBackupAgent();
             mTelephonyBackupAgent.attach(this);
             mTelephonyBackupAgent.onCreate();
@@ -583,8 +626,15 @@
         }
 
         static void startIfFilesExist(Context context) {
+            try {
+                PhoneFactory.addLocalLog(TAG, 32);
+            } catch (IllegalArgumentException e) {
+                // ignore
+            }
             File[] files = getFilesToRestore(context);
             if (files == null || files.length == 0) {
+                Log.d(TAG, "startIfFilesExist: no files to restore");
+                addAllSharedPrefToLocalLog(context);
                 return;
             }
             context.startService(new Intent(context, DeferredSmsMmsRestoreService.class));
@@ -618,7 +668,7 @@
                 Log.d(TAG, "Restoring text MMS");
                 putMmsMessagesToProvider(jsonReader);
             } else {
-                Log.e(TAG, "Unknown file to restore:" + fileName);
+                DeferredSmsMmsRestoreService.localLog("Unknown file to restore:" + fileName);
             }
         }
     }
@@ -627,6 +677,7 @@
     void putSmsMessagesToProvider(JsonReader jsonReader) throws IOException {
         jsonReader.beginArray();
         int msgCount = 0;
+        int numExceptions = 0;
         final int bulkInsertSize = mMaxMsgPerFile;
         ContentValues[] values = new ContentValues[bulkInsertSize];
         while (jsonReader.hasNext()) {
@@ -641,6 +692,8 @@
                 }
             } catch (RuntimeException e) {
                 Log.e(TAG, "putSmsMessagesToProvider", e);
+                DeferredSmsMmsRestoreService.localLog("putSmsMessagesToProvider: Exception " + e);
+                numExceptions++;
             }
         }
         if (msgCount % bulkInsertSize > 0) {
@@ -648,12 +701,37 @@
                     Arrays.copyOf(values, msgCount % bulkInsertSize));
         }
         jsonReader.endArray();
+        incremenentSharedPref(true, msgCount, numExceptions);
+    }
+
+    void incremenentSharedPref(boolean sms, int msgCount, int numExceptions) {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        SharedPreferences.Editor editor = sp.edit();
+        if (sms) {
+            editor.putInt(NUM_SMS_RESTORED, sp.getInt(NUM_SMS_RESTORED, 0) + msgCount);
+            editor.putInt(NUM_SMS_EXCEPTIONS, sp.getInt(NUM_SMS_EXCEPTIONS, 0) + numExceptions);
+            editor.putInt(NUM_SMS_FILES_STORED, sp.getInt(NUM_SMS_FILES_STORED, 0) + 1);
+            if (numExceptions > 0) {
+                editor.putInt(NUM_SMS_FILES_WITH_EXCEPTIONS,
+                        sp.getInt(NUM_SMS_FILES_WITH_EXCEPTIONS, 0) + 1);
+            }
+        } else {
+            editor.putInt(NUM_MMS_RESTORED, sp.getInt(NUM_MMS_RESTORED, 0) + msgCount);
+            editor.putInt(NUM_MMS_EXCEPTIONS, sp.getInt(NUM_MMS_EXCEPTIONS, 0) + numExceptions);
+            editor.putInt(NUM_MMS_FILES_STORED, sp.getInt(NUM_MMS_FILES_STORED, 0) + 1);
+            if (numExceptions > 0) {
+                editor.putInt(NUM_MMS_FILES_WITH_EXCEPTIONS,
+                        sp.getInt(NUM_MMS_FILES_WITH_EXCEPTIONS, 0) + 1);
+            }
+        }
+        editor.commit();
     }
 
     @VisibleForTesting
     void putMmsMessagesToProvider(JsonReader jsonReader) throws IOException {
         jsonReader.beginArray();
         int total = 0;
+        int numExceptions = 0;
         while (jsonReader.hasNext()) {
             final Mms mms = readMmsFromReader(jsonReader);
             if (DEBUG) {
@@ -672,9 +750,12 @@
                 addMmsMessage(mms);
             } catch (Exception e) {
                 Log.e(TAG, "putMmsMessagesToProvider", e);
+                numExceptions++;
+                DeferredSmsMmsRestoreService.localLog("putMmsMessagesToProvider: Exception " + e);
             }
         }
         Log.d(TAG, "putMmsMessagesToProvider handled " + total + " new messages.");
+        incremenentSharedPref(false, total, numExceptions);
     }
 
     @VisibleForTesting
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 3d9a2b0..ac6ec2e 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -283,6 +283,40 @@
     private boolean mManagedApnEnforced;
 
     /**
+     * Mobile country codes where there is a high likelyhood that the MNC has 3 digits
+     * and need one more prefix zero to set correct mobile network code value.
+     *
+     * Please note! The best solution is to add the MCCMNC combo to carrier id
+     * carrier_list, this is just a best effort.
+     */
+    private static final String[] COUNTRY_MCC_WITH_THREE_DIGIT_MNC = {
+            "302" // Canada
+           ,"310" // Guam, USA
+           ,"311" // USA
+           ,"312" // USA
+           ,"313" // USA
+           ,"316" // USA
+           ,"334" // Mexico
+           ,"338" // Bermuda, Jamaica
+           ,"342" // Barbados
+           ,"344" // Antigua and Barbuda
+           ,"346" // Cayman Islands
+           ,"348" // British Virgin Islands
+           ,"356" // Saint Kitts and Nevis
+           ,"358" // Saint Lucia
+           ,"360" // Saint Vincent and the Grenadines
+           ,"365" // Anguilla
+           ,"366" // Dominica
+           ,"376" // Turks and Caicos Islands
+           ,"405" // India
+           ,"708" // Honduras
+           ,"722" // Argentina
+           ,"732" // Colombia
+           ,"738" // Guyana
+           ,"750" // Falkland Islands
+            };
+
+    /**
      * Available radio technologies for GSM, UMTS and CDMA.
      * Duplicates the constants from hardware/radio/include/ril.h
      * This should only be used by agents working with the ril.  Others
@@ -3242,8 +3276,8 @@
                 .getInt(KEY_BACKUP_DATA_FORMAT_VERSION, -1);
 
         Resources r = getContext().getResources();
-        List<String> wfcEntitlementRequiredCountries = Arrays.asList(r.getStringArray(
-                    R.array.wfc_entitlement_required_countries));
+        List<String> wfcRestoreBlockedCountries = Arrays.asList(r.getStringArray(
+                    R.array.wfc_restore_blocked_countries));
 
         while (cursor != null && cursor.moveToNext()) {
             // Get all the possible matching criteria.
@@ -3281,7 +3315,7 @@
 
                 SimRestoreMatch currSimRestoreMatch = new SimRestoreMatch(
                         currIccIdFromDb, currCarrierIdFromDb, currPhoneNumberFromDb,
-                        isoCountryCodeFromDb, wfcEntitlementRequiredCountries, currRow,
+                        isoCountryCodeFromDb, wfcRestoreBlockedCountries, currRow,
                         backupDataFormatVersion);
 
                 if (currSimRestoreMatch == null) {
@@ -3369,7 +3403,7 @@
 
         public SimRestoreMatch(String iccIdFromDb, int carrierIdFromDb,
                 String phoneNumberFromDb, String isoCountryCodeFromDb,
-                List<String> wfcEntitlementRequiredCountries,
+                List<String> wfcRestoreBlockedCountries,
                 PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion) {
             subId = backedUpSimInfoEntry.getInt(
                 Telephony.SimInfo.COLUMN_UNIQUE_KEY_SUBSCRIPTION_ID,
@@ -3399,7 +3433,7 @@
 
             contentValues = convertBackedUpDataToContentValues(
                     backedUpSimInfoEntry, backupDataFormatVersion, isoCountryCodeFromDb,
-                    wfcEntitlementRequiredCountries);
+                    wfcRestoreBlockedCountries);
             matchScore = calculateMatchScore();
             matchingCriteria = calculateMatchingCriteria();
         }
@@ -3455,7 +3489,7 @@
         private ContentValues convertBackedUpDataToContentValues(
                 PersistableBundle backedUpSimInfoEntry, int backupDataFormatVersion,
                 String isoCountryCodeFromDb,
-                List<String> wfcEntitlementRequiredCountries) {
+                List<String> wfcRestoreBlockedCountries) {
             if (DATABASE_VERSION != 51 << 16) {
                 throw new AssertionError("The database schema has been updated which might make "
                     + "the format of #BACKED_UP_SIM_SPECIFIC_SETTINGS_FILE outdated. Make sure to "
@@ -3523,7 +3557,7 @@
                             Telephony.SimInfo.COLUMN_VT_IMS_ENABLED,
                             DEFAULT_INT_COLUMN_VALUE));
             if (isoCountryCodeFromDb != null
-                    && !wfcEntitlementRequiredCountries
+                    && !wfcRestoreBlockedCountries
                             .contains(isoCountryCodeFromDb.toLowerCase())) {
                 // Don't restore COLUMN_WFC_IMS_ENABLED if the sim is from one of the countries that
                 // requires WFC entitlement.
@@ -4898,18 +4932,32 @@
         }
         String twoDigitMnc = String.format(Locale.getDefault(), "%02d", mnc);
         String threeDigitMnc = "0" + twoDigitMnc;
+        boolean threeDigitNetworkCode =
+                Arrays.asList(COUNTRY_MCC_WITH_THREE_DIGIT_MNC).contains(mcc);
+        int twoDigitResult = countMccMncInCarrierList(context, mcc + twoDigitMnc);
+        int threeDigitResult = countMccMncInCarrierList(context, mcc + threeDigitMnc);
 
-        try (
-                Cursor twoDigitMncCursor = context.getContentResolver().query(
-                        Telephony.CarrierId.All.CONTENT_URI,
-                        /* projection */ null,
-                        /* selection */ Telephony.CarrierId.All.MCCMNC + "=?",
-                        /* selectionArgs */ new String[]{mcc + twoDigitMnc}, null)
-        ) {
-            if (twoDigitMncCursor.getCount() > 0) {
-                return twoDigitMnc;
-            }
+        if ((threeDigitResult > twoDigitResult) ||
+                (threeDigitNetworkCode && (twoDigitResult == threeDigitResult))) {
             return threeDigitMnc;
+        } else {
+            return twoDigitMnc;
+        }
+    }
+
+    /**
+     * Check carrier_list how many mcc mnc combo matches there are
+     */
+    private static int countMccMncInCarrierList(Context ctx, String mccMncCombo) {
+        try (
+            Cursor mccMncCursor = ctx.getContentResolver().query(
+                    Telephony.CarrierId.All.CONTENT_URI,
+                    /* projection */ null,
+                    /* selection */ Telephony.CarrierId.All.MCCMNC + "=?",
+                    /* selectionArgs */ new String[]{mccMncCombo}, null);
+        )
+        {
+            return mccMncCursor.getCount();
         }
     }