Import translations. DO NOT MERGE
am: d51baf20ba  -s ours

Change-Id: I04a086ab0018053ac6fa7c3efa98b66cdeb2c99f
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..691af77
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,5 @@
+amitmahajan@google.com
+fionaxu@google.com
+jminjie@google.com
+rgreenwalt@google.com
+tomtaylor@google.com
diff --git a/res/values-en-rCA/config.xml b/res/values-en-rCA/config.xml
new file mode 100644
index 0000000..99877a6
--- /dev/null
+++ b/res/values-en-rCA/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string-array name="persist_apns_for_plmn">
+    <item msgid="6413072509259000954">"20404"</item>
+    <item msgid="5639159280778239123">"310004"</item>
+    <item msgid="3860605521380788028">"310120"</item>
+    <item msgid="537693705785480198">"311480"</item>
+  </string-array>
+</resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..f11997b
--- /dev/null
+++ b/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<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">"Mobile Network Configuration"</string>
+    <string name="app_label" product="default" msgid="8338087656149558019">"Phone and Messaging Storage"</string>
+</resources>
diff --git a/res/values-en-rXC/config.xml b/res/values-en-rXC/config.xml
new file mode 100644
index 0000000..274d8aa
--- /dev/null
+++ b/res/values-en-rXC/config.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string-array name="persist_apns_for_plmn">
+    <item msgid="6413072509259000954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎20404‎‏‎‎‏‎"</item>
+    <item msgid="5639159280778239123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎310004‎‏‎‎‏‎"</item>
+    <item msgid="3860605521380788028">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‎310120‎‏‎‎‏‎"</item>
+    <item msgid="537693705785480198">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‏‎‎311480‎‏‎‎‏‎"</item>
+  </string-array>
+</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..22950f7
--- /dev/null
+++ b/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‎Mobile Network Configuration‎‏‎‎‏‎"</string>
+    <string name="app_label" product="default" msgid="8338087656149558019">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‎Phone and Messaging Storage‎‏‎‎‏‎"</string>
+</resources>
diff --git a/src/com/android/providers/telephony/CarrierDatabaseHelper.java b/src/com/android/providers/telephony/CarrierDatabaseHelper.java
index 5236b89..b654a77 100644
--- a/src/com/android/providers/telephony/CarrierDatabaseHelper.java
+++ b/src/com/android/providers/telephony/CarrierDatabaseHelper.java
@@ -21,6 +21,8 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.text.TextUtils;
+import android.util.Log;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -30,7 +32,7 @@
 
     private static final String DATABASE_NAME = "CarrierInformation.db";
     public static final String CARRIER_KEY_TABLE = "carrier_key";
-    private static final int DATABASE_VERSION = 1;
+    private static final int DATABASE_VERSION = 2;
 
     /**
      * CarrierDatabaseHelper carrier database helper class.
@@ -40,14 +42,15 @@
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
     }
 
-    static final String KEY_TYPE = "key_type";
-    static final String KEY = "key";
-    static final String MCC = "mcc";
-    static final String MNC = "mnc";
-    static final String MVNO_TYPE = "mvno_type";
-    static final String MVNO_MATCH_DATA = "mvno_match_data";
-    static final String PUBLIC_CERTIFICATE = "public_certificate";
-    static final String LAST_MODIFIED = "last_modified";
+    public static final String KEY_TYPE = "key_type";
+    public static final String MCC = "mcc";
+    public static final String MNC = "mnc";
+    public static final String MVNO_TYPE = "mvno_type";
+    public static final String MVNO_MATCH_DATA = "mvno_match_data";
+    public static final String PUBLIC_KEY = "public_key";
+    public static final String KEY_IDENTIFIER = "key_identifier";
+    public static final String EXPIRATION_TIME = "expiration_time";
+    public static final String LAST_MODIFIED = "last_modified";
 
     private static final List<String> CARRIERS_UNIQUE_FIELDS = new ArrayList<String>();
 
@@ -67,8 +70,9 @@
                 MVNO_TYPE + " TEXT DEFAULT ''," +
                 MVNO_MATCH_DATA + " TEXT DEFAULT ''," +
                 KEY_TYPE + " TEXT DEFAULT ''," +
-                KEY + " TEXT DEFAULT ''," +
-                PUBLIC_CERTIFICATE + " TEXT DEFAULT ''," +
+                KEY_IDENTIFIER + " TEXT DEFAULT ''," +
+                PUBLIC_KEY + " BLOB DEFAULT ''," +
+                EXPIRATION_TIME + " INTEGER DEFAULT 0," +
                 LAST_MODIFIED + " INTEGER DEFAULT 0," +
                 "UNIQUE (" + TextUtils.join(", ", CARRIERS_UNIQUE_FIELDS) + "));";
     }
@@ -78,8 +82,20 @@
         db.execSQL(getStringForCarrierKeyTableCreation(CARRIER_KEY_TABLE));
     }
 
+    public void createCarrierTable(SQLiteDatabase db) {
+        db.execSQL(getStringForCarrierKeyTableCreation(CARRIER_KEY_TABLE));
+    }
+
+    public void dropCarrierTable(SQLiteDatabase db) {
+        db.execSQL("DROP TABLE IF EXISTS " + CARRIER_KEY_TABLE + ";");
+    }
+
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-        // do nothing
+        Log.d(TAG, "dbh.onUpgrade:+ db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
+        if (oldVersion < 2) {
+            dropCarrierTable(db);
+            createCarrierTable(db);
+        }
     }
 }
diff --git a/src/com/android/providers/telephony/CarrierProvider.java b/src/com/android/providers/telephony/CarrierProvider.java
index 1c85806..a4b6ea0 100644
--- a/src/com/android/providers/telephony/CarrierProvider.java
+++ b/src/com/android/providers/telephony/CarrierProvider.java
@@ -78,24 +78,34 @@
     @Override
     public Uri insert(Uri uri, ContentValues values) {
         values.put(CarrierDatabaseHelper.LAST_MODIFIED, System.currentTimeMillis());
-        long row = getWritableDatabase().insert(CarrierDatabaseHelper.CARRIER_KEY_TABLE,
+        long row = getWritableDatabase().insertOrThrow(CarrierDatabaseHelper.CARRIER_KEY_TABLE,
                 null, values);
         if (row > 0) {
             Uri newUri = ContentUris.withAppendedId(CONTENT_URI, row);
             getContext().getContentResolver().notifyChange(newUri, null);
             return newUri;
         }
-        throw new SQLException("Fail to add a new record into " + uri);
+        return null;
     }
 
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
-        throw new UnsupportedOperationException("Cannot delete URL: " + uri);
+        if (VDBG) {
+            Log.d(TAG, "delete:"
+                    + " uri=" + uri
+                    + " selection={" + selection + "}"
+                    + " selection=" + selection
+                    + " selectionArgs=" + Arrays.toString(selectionArgs));
+        }
+        final int count = getWritableDatabase().delete(CarrierDatabaseHelper.CARRIER_KEY_TABLE,
+                selection, selectionArgs);
+        Log.d(TAG, "  delete.count=" + count);
+        return count;
     }
 
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-
+        values.put(CarrierDatabaseHelper.LAST_MODIFIED, System.currentTimeMillis());
         if (VDBG) {
             Log.d(TAG, "update:"
                     + " uri=" + uri
diff --git a/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java b/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java
index ceaed4c..2debc57 100644
--- a/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java
+++ b/src/com/android/providers/telephony/HbpcdLookupDatabaseHelper.java
@@ -86,6 +86,7 @@
 
     private static final String DATABASE_NAME = "HbpcdLookup.db";
     private static final int DATABASE_VERSION = 1;
+    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
 
     // Context to access resources with
     private Context mContext;
@@ -99,6 +100,8 @@
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
 
         mContext = context;
+        // Memory optimization - close idle connections after 30s of inactivity
+        setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
     }
 
     @Override
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index c9051d6..4b84fe5 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -39,6 +39,7 @@
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.google.android.mms.pdu.EncodedStringValue;
 import com.google.android.mms.pdu.PduHeaders;
 
@@ -240,14 +241,17 @@
 
     static final String DATABASE_NAME = "mmssms.db";
     static final int DATABASE_VERSION = 66;
+    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
+
     private final Context mContext;
     private LowStorageMonitor mLowStorageMonitor;
 
 
     private MmsSmsDatabaseHelper(Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
-
         mContext = context;
+        // Memory optimization - close idle connections after 30s of inactivity
+        setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
     }
 
     /**
@@ -851,33 +855,44 @@
                    "END;");
     }
 
+    @VisibleForTesting
+    public static String CREATE_SMS_TABLE_STRING =
+            "CREATE TABLE sms (" +
+            "_id INTEGER PRIMARY KEY," +
+            "thread_id INTEGER," +
+            "address TEXT," +
+            "person INTEGER," +
+            "date INTEGER," +
+            "date_sent INTEGER DEFAULT 0," +
+            "protocol INTEGER," +
+            "read INTEGER DEFAULT 0," +
+            "status INTEGER DEFAULT -1," + // a TP-Status value
+            // or -1 if it
+            // status hasn't
+            // been received
+            "type INTEGER," +
+            "reply_path_present INTEGER," +
+            "subject TEXT," +
+            "body TEXT," +
+            "service_center TEXT," +
+            "locked INTEGER DEFAULT 0," +
+            "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
+            "error_code INTEGER DEFAULT 0," +
+            "creator TEXT," +
+            "seen INTEGER DEFAULT 0" +
+            ");";
+
+    @VisibleForTesting
+    public static String CREATE_ATTACHMENTS_TABLE_STRING =
+            "CREATE TABLE attachments (" +
+            "sms_id INTEGER," +
+            "content_url TEXT," +
+            "offset INTEGER);";
+
     private void createSmsTables(SQLiteDatabase db) {
         // N.B.: Whenever the columns here are changed, the columns in
         // {@ref MmsSmsProvider} must be changed to match.
-        db.execSQL("CREATE TABLE sms (" +
-                   "_id INTEGER PRIMARY KEY," +
-                   "thread_id INTEGER," +
-                   "address TEXT," +
-                   "person INTEGER," +
-                   "date INTEGER," +
-                   "date_sent INTEGER DEFAULT 0," +
-                   "protocol INTEGER," +
-                   "read INTEGER DEFAULT 0," +
-                   "status INTEGER DEFAULT -1," + // a TP-Status value
-                                                  // or -1 if it
-                                                  // status hasn't
-                                                  // been received
-                   "type INTEGER," +
-                   "reply_path_present INTEGER," +
-                   "subject TEXT," +
-                   "body TEXT," +
-                   "service_center TEXT," +
-                   "locked INTEGER DEFAULT 0," +
-                   "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
-                   "error_code INTEGER DEFAULT 0," +
-                   "creator TEXT," +
-                   "seen INTEGER DEFAULT 0" +
-                   ");");
+        db.execSQL(CREATE_SMS_TABLE_STRING);
 
         /**
          * This table is used by the SMS dispatcher to hold
@@ -899,10 +914,7 @@
                    // email address if from an email gateway, otherwise same as address
         );
 
-        db.execSQL("CREATE TABLE attachments (" +
-                   "sms_id INTEGER," +
-                   "content_url TEXT," +
-                   "offset INTEGER);");
+        db.execSQL(CREATE_ATTACHMENTS_TABLE_STRING);
 
         /**
          * This table is used by the SMS dispatcher to hold pending
diff --git a/src/com/android/providers/telephony/ProviderUtil.java b/src/com/android/providers/telephony/ProviderUtil.java
index 4234b06..cd0f351 100644
--- a/src/com/android/providers/telephony/ProviderUtil.java
+++ b/src/com/android/providers/telephony/ProviderUtil.java
@@ -109,6 +109,7 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.d(TAG, "notifyIfNotDefaultSmsApp - called from " + callingPackage + ", notifying");
         }
+        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         context.sendBroadcast(intent);
     }
 
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index eca18a6..37a1044 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -120,7 +120,7 @@
 
         // Generate the body of the query.
         int match = sURLMatcher.match(url);
-        SQLiteDatabase db = getDBOpenHelper(match).getReadableDatabase();
+        SQLiteDatabase db = getReadableDatabase(match);
         switch (match) {
             case SMS_ALL:
                 constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL, smsTable);
@@ -521,7 +521,7 @@
                 return null;
         }
 
-        SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+        SQLiteDatabase db = getWritableDatabase(match);
 
         if (table.equals(TABLE_SMS)) {
             boolean addDate = false;
@@ -632,12 +632,7 @@
             db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
         }
         if (rowID > 0) {
-            Uri uri;
-            if (table == TABLE_SMS) {
-                uri = Uri.withAppendedPath(url, "/" + rowID);
-            } else {
-                uri = Uri.withAppendedPath(url, "/" + table + "/" + rowID );
-            }
+            Uri uri = Uri.withAppendedPath(url, String.valueOf(rowID));
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
                 Log.d(TAG, "insert " + uri + " succeeded");
             }
@@ -653,7 +648,7 @@
     public int delete(Uri url, String where, String[] whereArgs) {
         int count;
         int match = sURLMatcher.match(url);
-        SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+        SQLiteDatabase db = getWritableDatabase(match);
         boolean notifyIfNotDefault = true;
         switch (match) {
             case SMS_ALL:
@@ -760,7 +755,7 @@
         String extraWhere = null;
         boolean notifyIfNotDefault = true;
         int match = sURLMatcher.match(url);
-        SQLiteDatabase db = getDBOpenHelper(match).getWritableDatabase();
+        SQLiteDatabase db = getWritableDatabase(match);
 
         switch (match) {
             case SMS_RAW_MESSAGE:
@@ -922,4 +917,16 @@
         sURLMatcher.addURI("sms", "sim", SMS_ALL_ICC);
         sURLMatcher.addURI("sms", "sim/#", SMS_ICC);
     }
+
+    /**
+     * These methods can be overridden in a subclass for testing SmsProvider using an
+     * in-memory database.
+     */
+    SQLiteDatabase getReadableDatabase(int match) {
+        return getDBOpenHelper(match).getReadableDatabase();
+    }
+
+    SQLiteDatabase getWritableDatabase(int match) {
+        return  getDBOpenHelper(match).getWritableDatabase();
+    }
 }
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index 32e0b55..49c3d10 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -115,6 +115,7 @@
 public class TelephonyProvider extends ContentProvider
 {
     private static final String DATABASE_NAME = "telephony.db";
+    private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
     private static final boolean DBG = true;
     private static final boolean VDBG = false; // STOPSHIP if true
 
@@ -143,6 +144,7 @@
 
     private static final String PREF_FILE_APN = "preferred-apn";
     private static final String COLUMN_APN_ID = "apn_id";
+    private static final String EXPLICIT_SET_CALLED = "explicit_set_called";
 
     private static final String PREF_FILE_FULL_APN = "preferred-full-apn";
     private static final String DB_VERSION_KEY = "version";
@@ -337,6 +339,8 @@
         public DatabaseHelper(Context context) {
             super(context, DATABASE_NAME, null, getVersion(context));
             mContext = context;
+            // Memory optimization - close idle connections after 30s of inactivity
+            setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
         }
 
         private static int getVersion(Context context) {
@@ -1815,15 +1819,23 @@
         return true;
     }
 
-    private void setPreferredApnId(Long id, int subId) {
+    private void setPreferredApnId(Long id, int subId, boolean saveApn) {
         SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN,
                 Context.MODE_PRIVATE);
         SharedPreferences.Editor editor = sp.edit();
-        editor.putLong(COLUMN_APN_ID + subId, id != null ? id.longValue() : INVALID_APN_ID);
+        editor.putLong(COLUMN_APN_ID + subId, id != null ? id : INVALID_APN_ID);
+        // This is for debug purposes. It indicates if this APN was set by DcTracker or user (true)
+        // or if this was restored from APN saved in PREF_FILE_FULL_APN (false).
+        editor.putBoolean(EXPLICIT_SET_CALLED + subId, saveApn);
         editor.apply();
-        // remove saved apn if apnId is invalid
         if (id == null || id.longValue() == INVALID_APN_ID) {
             deletePreferredApn(subId);
+        } else {
+            // If id is not invalid, and saveApn is true, save the actual APN in PREF_FILE_FULL_APN
+            // too.
+            if (saveApn) {
+                setPreferredApn(id, subId);
+            }
         }
     }
 
@@ -1834,8 +1846,7 @@
         if (apnId == INVALID_APN_ID && checkApnSp) {
             apnId = getPreferredApnIdFromApn(subId);
             if (apnId != INVALID_APN_ID) {
-                setPreferredApnId(apnId, subId);
-                deletePreferredApn(subId);
+                setPreferredApnId(apnId, subId, false);
             }
         }
         return apnId;
@@ -1844,7 +1855,12 @@
     private void deletePreferredApnId() {
         SharedPreferences sp = getContext().getSharedPreferences(PREF_FILE_APN,
                 Context.MODE_PRIVATE);
-        // before deleting, save actual preferred apns (not the ids) in a separate SP
+
+        // Before deleting, save actual preferred apns (not the ids) in a separate SP.
+        // NOTE: This code to call setPreferredApn() can be removed since the function is now called
+        // from setPreferredApnId(). However older builds (pre oc-mr1) do not have that change, so
+        // when devices upgrade from those builds and this function is called, this code is needed
+        // otherwise the preferred APN will be lost.
         Map<String, ?> allPrefApnId = sp.getAll();
         for (String key : allPrefApnId.keySet()) {
             // extract subId from key by removing COLUMN_APN_ID
@@ -1858,6 +1874,7 @@
                 loge("Skipping over key " + key + " due to exception " + e);
             }
         }
+
         SharedPreferences.Editor editor = sp.edit();
         editor.clear();
         editor.apply();
@@ -1935,7 +1952,6 @@
             for (String key : CARRIERS_UNIQUE_FIELDS) {
                 editor.remove(key + subId);
             }
-            editor.remove(DB_VERSION_KEY + subId);
             editor.apply();
         }
     }
@@ -2254,7 +2270,7 @@
             {
                 if (initialValues != null) {
                     if(initialValues.containsKey(COLUMN_APN_ID)) {
-                        setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId);
+                        setPreferredApnId(initialValues.getAsLong(COLUMN_APN_ID), subId, true);
                     }
                 }
                 break;
@@ -2293,6 +2309,8 @@
         {
             case URL_DELETE:
             {
+                // Delete preferred APN for all subIds
+                deletePreferredApnId();
                 // Delete unedited entries
                 count = db.delete(CARRIERS_TABLE, "(" + where + unedited, whereArgs);
                 break;
@@ -2391,7 +2409,7 @@
             case URL_PREFERAPN:
             case URL_PREFERAPN_NO_UPDATE:
             {
-                setPreferredApnId((long)INVALID_APN_ID, subId);
+                setPreferredApnId((long)INVALID_APN_ID, subId, true);
                 if ((match == URL_PREFERAPN) || (match == URL_PREFERAPN_USING_SUBID)) count = 1;
                 break;
             }
@@ -2524,7 +2542,7 @@
             {
                 if (values != null) {
                     if (values.containsKey(COLUMN_APN_ID)) {
-                        setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId);
+                        setPreferredApnId(values.getAsLong(COLUMN_APN_ID), subId, true);
                         if ((match == URL_PREFERAPN) ||
                                 (match == URL_PREFERAPN_USING_SUBID)) {
                             count = 1;
diff --git a/tests/src/com/android/providers/telephony/CarrierProviderTest.java b/tests/src/com/android/providers/telephony/CarrierProviderTest.java
index 6a56343..5146aa9 100644
--- a/tests/src/com/android/providers/telephony/CarrierProviderTest.java
+++ b/tests/src/com/android/providers/telephony/CarrierProviderTest.java
@@ -54,7 +54,7 @@
     private MockContentResolver mContentResolver;
     private CarrierProviderTestable mCarrierProviderTestable;
 
-    public static final String dummy_type = "TYPE5";
+    public static final int dummy_type = 1;
     public static final String dummy_mnc = "MNC001";
     public static final String dummy_mnc2 = "MNC002";
     public static final String dummy_mcc = "MCC005";
@@ -62,6 +62,8 @@
     public static final String dummy_key2 = "PUBKEY2";
     public static final String dummy_mvno_type = "100";
     public static final String dummy_mvno_match_data = "101";
+    public static final String  dummy_key_identifier_data = "key_identifier1";
+    public static final long  dummy_key_expiration = 1496795015L;
 
 
     /**
@@ -147,7 +149,9 @@
         contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
         contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
         contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_CERTIFICATE, dummy_key1);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, dummy_key_expiration);
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -181,7 +185,9 @@
         contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
         contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
         contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_CERTIFICATE, dummy_key1);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, dummy_key_expiration);
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -191,18 +197,19 @@
 
         try {
             ContentValues updatedValues = new ContentValues();
-            updatedValues.put(CarrierDatabaseHelper.PUBLIC_CERTIFICATE, dummy_key2);
+            updatedValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key2);
             mContentResolver.update(CarrierProvider.CONTENT_URI, updatedValues,
-                    "mcc=? and mnc=? and key_type=?", new String[] { dummy_mcc, dummy_mnc, dummy_type });
+                    "mcc=? and mnc=? and key_type=?", new String[] { dummy_mcc, dummy_mnc,
+                            String.valueOf(dummy_type) });
         } catch (Exception e) {
             Log.d(TAG, "Error updating values:" + e);
         }
 
         try {
-            String[] columns ={CarrierDatabaseHelper.PUBLIC_CERTIFICATE};
+            String[] columns ={CarrierDatabaseHelper.PUBLIC_KEY};
             Cursor findEntry = mContentResolver.query(CarrierProvider.CONTENT_URI, columns,
                     "mcc=? and mnc=? and key_type=?",
-                    new String[] { dummy_mcc, dummy_mnc, dummy_type }, null);
+                    new String[] { dummy_mcc, dummy_mnc, String.valueOf(dummy_type) }, null);
             findEntry.moveToFirst();
             key = findEntry.getString(0);
         } catch (Exception e) {
@@ -224,7 +231,8 @@
         contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
         contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
         contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_CERTIFICATE, dummy_key1);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
 
         ContentValues contentValuesNew = new ContentValues();
         contentValuesNew.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
@@ -232,7 +240,8 @@
         contentValuesNew.put(CarrierDatabaseHelper.MNC, dummy_mnc2);
         contentValuesNew.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
         contentValuesNew.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_CERTIFICATE, dummy_key2);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
+        contentValuesNew.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key2.getBytes());
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -266,7 +275,7 @@
         contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
         contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
         contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
-        contentValues.put(CarrierDatabaseHelper.PUBLIC_CERTIFICATE, dummy_key1);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
 
         try {
             mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
@@ -279,4 +288,38 @@
             Log.d(TAG, "Error inserting certificates:: " + e);
         }
     }
+
+    /**
+     * Test delete.
+     */
+    @Test
+    @SmallTest
+    public void testDelete() {
+        int numRowsDeleted = -1;
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(CarrierDatabaseHelper.KEY_TYPE, dummy_type);
+        contentValues.put(CarrierDatabaseHelper.MCC, dummy_mcc);
+        contentValues.put(CarrierDatabaseHelper.MNC, dummy_mnc);
+        contentValues.put(CarrierDatabaseHelper.MVNO_TYPE, dummy_mvno_type);
+        contentValues.put(CarrierDatabaseHelper.MVNO_MATCH_DATA, dummy_mvno_match_data);
+        contentValues.put(CarrierDatabaseHelper.KEY_IDENTIFIER, dummy_key_identifier_data);
+        contentValues.put(CarrierDatabaseHelper.PUBLIC_KEY, dummy_key1.getBytes());
+        contentValues.put(CarrierDatabaseHelper.EXPIRATION_TIME, dummy_key_expiration);
+
+        try {
+            mContentResolver.insert(CarrierProvider.CONTENT_URI, contentValues);
+        } catch (Exception e) {
+            Log.d(TAG, "Error inserting certificates:" + e);
+        }
+
+        try {
+            String whereClause = "mcc=? and mnc=?";
+            String[] whereArgs = new String[] { dummy_mcc, dummy_mnc };
+            numRowsDeleted = mContentResolver.delete(CarrierProvider.CONTENT_URI, whereClause, whereArgs);
+        } catch (Exception e) {
+            Log.d(TAG, "Error updating values:" + e);
+        }
+        assertEquals(numRowsDeleted, 1);
+    }
+
 }
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTest.java b/tests/src/com/android/providers/telephony/SmsProviderTest.java
new file mode 100644
index 0000000..8fba85b
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/SmsProviderTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.providers.telephony;
+
+import android.app.AppOpsManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Telephony;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+
+/**
+ * Tests for testing CRUD operations of SmsProvider.
+ * Uses a MockContentResolver to test insert
+ * Uses SmsProviderTestable to set up in-memory database
+ *
+ * Build, install and run the tests by running the commands below:
+ *     runtest --path <dir or file>
+ *     runtest --path <dir or file> --test-method <testMethodName>
+ *     e.g.)
+ *         runtest --path tests/src/com/android/providers/telephony/SmsProviderTest.java \
+ *                 --test-method testInsertUri
+ */
+public class SmsProviderTest extends TestCase {
+    private static final String TAG = "TelephonyProviderTest";
+
+    private MockContextWithProvider mContext;
+    private MockContentResolver mContentResolver;
+    private SmsProviderTestable mSmsProviderTestable;
+
+    private int notifyChangeCount;
+
+
+    /**
+     * This is used to give the SmsProviderTest a mocked context which takes a
+     * SmsProvider and attaches it to the ContentResolver with telephony authority.
+     * The mocked context also gives WRITE_APN_SETTINGS permissions
+     */
+    private class MockContextWithProvider extends MockContext {
+        private final MockContentResolver mResolver;
+
+        public MockContextWithProvider(SmsProvider smsProvider) {
+            mResolver = new MockContentResolver() {
+                @Override
+                public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
+                        int userHandle) {
+                    notifyChangeCount++;
+                }
+            };
+
+            // Add authority="sms" to given smsProvider
+            ProviderInfo providerInfo = new ProviderInfo();
+            providerInfo.authority = "sms";
+
+            // Add context to given smsProvider
+            smsProvider.attachInfoForTesting(this, providerInfo);
+            Log.d(TAG, "MockContextWithProvider: smsProvider.getContext(): "
+                    + smsProvider.getContext());
+
+            // Add given SmsProvider to mResolver with authority="sms" so that
+            // mResolver can send queries to mSmsProvider
+            mResolver.addProvider("sms", smsProvider);
+            Log.d(TAG, "MockContextWithProvider: Add SmsProvider to mResolver");
+        }
+
+        @Override
+        public Object getSystemService(String name) {
+            Log.d(TAG, "getSystemService: returning null");
+            switch (name) {
+                case Context.APP_OPS_SERVICE:
+                    return Mockito.mock(AppOpsManager.class);
+                case Context.TELEPHONY_SERVICE:
+                    return Mockito.mock(TelephonyManager.class);
+                default:
+                    return null;
+            }
+        }
+
+        @Override
+        public Resources getResources() {
+            Log.d(TAG, "getResources: returning null");
+            return null;
+        }
+
+        @Override
+        public int getUserId() {
+            return 0;
+        }
+
+        @Override
+        public MockContentResolver getContentResolver() {
+            return mResolver;
+        }
+
+        @Override
+        public int checkCallingOrSelfPermission(String permission) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSmsProviderTestable = new SmsProviderTestable();
+        mContext = new MockContextWithProvider(mSmsProviderTestable);
+        mContentResolver = mContext.getContentResolver();
+        notifyChangeCount = 0;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mSmsProviderTestable.closeDatabase();
+    }
+
+    @Test
+    @SmallTest
+    public void testInsertUri() {
+        // insert test contentValues
+        final ContentValues values = new ContentValues();
+        values.put(Telephony.Sms.SUBSCRIPTION_ID, 1);
+        values.put(Telephony.Sms.ADDRESS, "12345");
+        values.put(Telephony.Sms.BODY, "test");
+        values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds
+        values.put(Telephony.Sms.SEEN, 1);
+        values.put(Telephony.Sms.READ, 1);
+        values.put(Telephony.Sms.THREAD_ID, 1);
+
+        // test for sms table
+        Log.d(TAG, "testInsertSmsTable Inserting contentValues: " + values);
+        assertEquals(Uri.parse("content://sms/1"),
+                mContentResolver.insert(Uri.parse("content://sms"), values));
+        assertEquals(Uri.parse("content://sms/2"),
+                mContentResolver.insert(Uri.parse("content://sms"), values));
+
+        // test for attachments table
+        values.clear();
+        values.put("sms_id", 1);
+        values.put("content_url", "test");
+        values.put("offset", 0);
+        Log.d(TAG, "testInsertAttachmentTable Inserting contentValues: " + values);
+        assertEquals(Uri.parse("content://sms/attachments/1"),
+                mContentResolver.insert(Uri.parse("content://sms/attachments"), values));
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/providers/telephony/SmsProviderTestable.java b/tests/src/com/android/providers/telephony/SmsProviderTestable.java
new file mode 100644
index 0000000..9c3372e
--- /dev/null
+++ b/tests/src/com/android/providers/telephony/SmsProviderTestable.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.providers.telephony;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * A subclass of SmsProvider used for testing on an in-memory database
+ */
+public class SmsProviderTestable extends SmsProvider {
+    private static final String TAG = "SmsProviderTestable";
+
+    private InMemorySmsProviderDbHelper mDbHelper;
+
+    @Override
+    public boolean onCreate() {
+        Log.d(TAG, "onCreate called: mDbHelper = new InMemorySmsProviderDbHelper()");
+        mDbHelper = new InMemorySmsProviderDbHelper();
+        return true;
+    }
+
+    @Override
+    SQLiteDatabase getReadableDatabase(int match) {
+        Log.d(TAG, "getReadableDatabase called");
+        return mDbHelper.getReadableDatabase();
+    }
+
+    @Override
+    SQLiteDatabase getWritableDatabase(int match) {
+        Log.d(TAG, "getWritableDatabase called");
+        return mDbHelper.getWritableDatabase();
+    }
+
+    // close mDbHelper database object
+    protected void closeDatabase() {
+        mDbHelper.close();
+    }
+
+    /**
+     * An in memory DB for SmsProviderTestable to use
+     */
+    public static class InMemorySmsProviderDbHelper extends SQLiteOpenHelper {
+
+
+        public InMemorySmsProviderDbHelper() {
+            super(null,      // no context is needed for in-memory db
+                  null,      // db file name is null for in-memory db
+                  null,      // CursorFactory is null by default
+                  1);        // db version is no-op for tests
+            Log.d(TAG, "InMemorySmsProviderDbHelper creating in-memory database");
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            // Set up the sms tables
+            Log.d(TAG, "InMemorySmsProviderDbHelper onCreate creating the sms tables");
+            db.execSQL(MmsSmsDatabaseHelper.CREATE_SMS_TABLE_STRING);
+            db.execSQL(MmsSmsDatabaseHelper.CREATE_ATTACHMENTS_TABLE_STRING);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.d(TAG, "InMemorySmsProviderDbHelper onUpgrade doing nothing");
+            return;
+        }
+    }
+}
+