30481342: Security Vulnerability - TOCTOU in MmsProvider allows access to files as phone (radio) uid - DO NOT MERGE am: fd9be3377f am: a8ae137579 am: b028c61f44  -s ours
am: 7153a8830c  -s ours

Change-Id: I0d8d7cf58297e49273dc05cd2fde9bd45547deb2
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c1c2795..f1cd72b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -32,6 +32,7 @@
         <provider android:name="TelephonyProvider"
                   android:authorities="telephony"
                   android:exported="true"
+                  android:singleUser="true"
                   android:multiprocess="false" />
 
         <!-- This is a singleton provider that is used by all users.
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 4eedae0..f018658 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/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="4282451239358791628">"फ़ोन/संदेश मेमोरी"</string>
+    <string name="app_label" product="default" msgid="4282451239358791628">"फ़ोन/संदेश संग्रहण"</string>
 </resources>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index fe6f377..8227c76 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/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="4282451239358791628">"ဖုန်း/စာပို့ခြင်း သိုလှောင်မှု"</string>
 </resources>
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index 19b4efa..329f505 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.telephony;
 
+import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.ContentProvider;
 import android.content.ContentValues;
@@ -28,6 +29,7 @@
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
@@ -35,10 +37,11 @@
 import android.provider.Telephony;
 import android.provider.Telephony.CanonicalAddressesColumns;
 import android.provider.Telephony.Mms;
-import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.Mms.Addr;
 import android.provider.Telephony.Mms.Part;
 import android.provider.Telephony.Mms.Rate;
+import android.provider.Telephony.MmsSms;
+import android.provider.Telephony.Threads;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -49,8 +52,6 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
 
-import android.provider.Telephony.Threads;
-
 /**
  * The class to provide base facility to access MMS related content,
  * which is stored in a SQLite database and in the file system.
@@ -63,6 +64,8 @@
     static final String TABLE_DRM  = "drm";
     static final String TABLE_WORDS = "words";
 
+    // The name of parts directory. The full dir is "app_parts".
+    private static final String PARTS_DIR_NAME = "parts";
 
     @Override
     public boolean onCreate() {
@@ -283,6 +286,7 @@
         if (values != null && values.containsKey(Part._DATA)) {
             return null;
         }
+        final int callerUid = Binder.getCallingUid();
         int msgBox = Mms.MESSAGE_BOX_ALL;
         boolean notify = true;
 
@@ -375,19 +379,27 @@
                 finalValues.put(Mms.THREAD_ID, Threads.getOrCreateThreadId(getContext(), address));
             }
 
+            if (ProviderUtil.shouldSetCreator(finalValues, callerUid)) {
+                // Only SYSTEM or PHONE can set CREATOR
+                // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
+                // set CREATOR using the truth on caller.
+                // Note: Inferring package name from UID may include unrelated package names
+                finalValues.put(Telephony.Mms.CREATOR,
+                        ProviderUtil.getPackageNamesByUid(getContext(), callerUid));
+            }
+
             if ((rowId = db.insert(table, null, finalValues)) <= 0) {
-                Log.e(TAG, "MmsProvider.insert: failed! " + finalValues);
+                Log.e(TAG, "MmsProvider.insert: failed!");
                 return null;
             }
 
             res = Uri.parse(res + "/" + rowId);
-
         } else if (table.equals(TABLE_ADDR)) {
             finalValues = new ContentValues(values);
             finalValues.put(Addr.MSG_ID, uri.getPathSegments().get(0));
 
             if ((rowId = db.insert(table, null, finalValues)) <= 0) {
-                Log.e(TAG, "Failed to insert address: " + finalValues);
+                Log.e(TAG, "Failed to insert address");
                 return null;
             }
 
@@ -422,7 +434,7 @@
 
                 // Generate the '_data' field of the part with default
                 // permission settings.
-                String path = getContext().getDir("parts", 0).getPath()
+                String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath()
                         + "/PART_" + System.currentTimeMillis() + contentLocation;
 
                 if (DownloadDrmHelper.isDrmConvertNeeded(contentType)) {
@@ -456,7 +468,7 @@
             }
 
             if ((rowId = db.insert(table, null, finalValues)) <= 0) {
-                Log.e(TAG, "MmsProvider.insert: failed! " + finalValues);
+                Log.e(TAG, "MmsProvider.insert: failed!");
                 return null;
             }
 
@@ -488,7 +500,7 @@
             db.delete(table, Rate.SENT_TIME + "<=" + oneHourAgo, null);
             db.insert(table, null, values);
         } else if (table.equals(TABLE_DRM)) {
-            String path = getContext().getDir("parts", 0).getPath()
+            String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath()
                     + "/PART_" + System.currentTimeMillis();
             finalValues = new ContentValues(1);
             finalValues.put("_data", path);
@@ -508,7 +520,7 @@
             }
 
             if ((rowId = db.insert(table, null, finalValues)) <= 0) {
-                Log.e(TAG, "MmsProvider.insert: failed! " + finalValues);
+                Log.e(TAG, "MmsProvider.insert: failed!");
                 return null;
             }
             res = Uri.parse(res + "/drm/" + rowId);
@@ -695,8 +707,7 @@
     }
 
     @Override
-    public int update(Uri uri, ContentValues values,
-            String selection, String[] selectionArgs) {
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         // The _data column is filled internally in MmsProvider, so this check is just to avoid
         // it from being inadvertently set. This is not supposed to be a protection against
         // malicious attack, since sql injection could still be attempted to bypass the check. On
@@ -705,6 +716,7 @@
         if (values != null && values.containsKey(Part._DATA)) {
             return 0;
         }
+        final int callerUid = Binder.getCallingUid();
         int match = sURLMatcher.match(uri);
         if (LOCAL_LOGV) {
             Log.v(TAG, "Update uri=" + uri + ", match=" + match);
@@ -737,7 +749,7 @@
                 break;
 
             case MMS_PART_RESET_FILE_PERMISSION:
-                String path = getContext().getDir("parts", 0).getPath() + '/' +
+                String path = getContext().getDir(PARTS_DIR_NAME, 0).getPath() + '/' +
                         uri.getPathSegments().get(1);
                 // Reset the file permission back to read for everyone but me.
                 int result = FileUtils.setPermissions(path, 0644, -1, -1);
@@ -757,6 +769,12 @@
         if (table.equals(TABLE_PDU)) {
             // Filter keys that we don't support yet.
             filterUnsupportedKeys(values);
+            if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
+                // CREATOR should not be changed by non-SYSTEM/PHONE apps
+                Log.w(TAG, ProviderUtil.getPackageNamesByUid(getContext(), callerUid) +
+                        " tries to update CREATOR");
+                values.remove(Mms.CREATOR);
+            }
             finalValues = new ContentValues(values);
 
             if (msgId != null) {
@@ -803,8 +821,9 @@
         return safeOpenFileHelper(uri, mode);
     }
 
+    @NonNull
     private ParcelFileDescriptor safeOpenFileHelper(
-            Uri uri, String mode) throws FileNotFoundException {
+            @NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
         Cursor c = query(uri, new String[]{"_data"}, null, null, null);
         int count = (c != null) ? c.getCount() : 0;
         if (count != 1) {
@@ -836,11 +855,11 @@
             // TODO(afurtado): provide a more robust mechanism to avoid disallowed _data paths to
             // be inserted/updated in the first place, including via SQL injection.
             if (!filePath.getCanonicalPath()
-                    .startsWith(getContext().getApplicationInfo().dataDir + "/app_parts/")) {
+                    .startsWith(getContext().getDir(PARTS_DIR_NAME, 0).getPath())) {
                 Log.e(TAG, "openFile: path "
                         + filePath.getCanonicalPath()
                         + " does not start with "
-                        + getContext().getApplicationInfo().dataDir + "/app_parts/");
+                        + getContext().getDir(PARTS_DIR_NAME, 0).getPath());
                 // Don't care return value
                 return null;
             }
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index a470bb1..6325905 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -42,6 +42,7 @@
 import android.provider.Telephony.Mms.Part;
 import android.provider.Telephony.Mms.Rate;
 import android.provider.Telephony.MmsSms.PendingMessages;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
 
 import com.google.android.mms.pdu.EncodedStringValue;
@@ -592,7 +593,8 @@
                    Mms.DELIVERY_TIME + " INTEGER," +
                    Mms.DELIVERY_REPORT + " INTEGER," +
                    Mms.LOCKED + " INTEGER DEFAULT 0," +
-                   Mms.SUB_ID + " INTEGER DEFAULT -1, " +
+                   Mms.SUBSCRIPTION_ID + " INTEGER DEFAULT "
+                           + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
                    Mms.SEEN + " INTEGER DEFAULT 0," +
                    Mms.CREATOR + " TEXT," +
                    Mms.TEXT_ONLY + " INTEGER DEFAULT 0" +
@@ -837,7 +839,7 @@
                    "body TEXT," +
                    "service_center TEXT," +
                    "locked INTEGER DEFAULT 0," +
-                   "sub_id INTEGER DEFAULT -1, " +
+                   "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
                    "error_code INTEGER DEFAULT 0," +
                    "creator TEXT," +
                    "seen INTEGER DEFAULT 0" +
@@ -855,7 +857,7 @@
                    "sequence INTEGER," + // the part number of this message
                    "destination_port INTEGER," +
                    "address TEXT," +
-                   "sub_id INTEGER DEFAULT -1, " +
+                   "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
                    "pdu TEXT);"); // the raw PDU for this part
 
         db.execSQL("CREATE TABLE attachments (" +
@@ -923,7 +925,8 @@
                    PendingMessages.ERROR_CODE + " INTEGER," +
                    PendingMessages.RETRY_INDEX + " INTEGER NOT NULL DEFAULT 0," +
                    PendingMessages.DUE_TIME + " INTEGER," +
-                   PendingMessages.SUB_ID + " INTEGER DEFAULT 0, " +
+                   PendingMessages.SUBSCRIPTION_ID + " INTEGER DEFAULT " +
+                           SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
                    PendingMessages.LAST_TRY + " INTEGER);");
 
     }
@@ -1530,14 +1533,18 @@
     }
 
     private void upgradeDatabaseToVersion58(SQLiteDatabase db) {
-        db.execSQL("ALTER TABLE " + MmsProvider.TABLE_PDU +" ADD COLUMN "
-                + Mms.SUB_ID + " INTEGER DEFAULT -1");
-        db.execSQL("ALTER TABLE " + MmsSmsProvider.TABLE_PENDING_MSG +" ADD COLUMN "
-                + "pending_sub_id" + " INTEGER DEFAULT 0");
-        db.execSQL("ALTER TABLE " + SmsProvider.TABLE_SMS +" ADD COLUMN "
-                + Sms.SUB_ID + " INTEGER DEFAULT -1");
-        db.execSQL("ALTER TABLE " + SmsProvider.TABLE_RAW +" ADD COLUMN "
-                + Sms.SUB_ID + " INTEGER DEFAULT -1");
+        db.execSQL("ALTER TABLE " + MmsProvider.TABLE_PDU +
+                " ADD COLUMN " + Mms.SUBSCRIPTION_ID
+                + " INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        db.execSQL("ALTER TABLE " + MmsSmsProvider.TABLE_PENDING_MSG
+                +" ADD COLUMN " + "pending_sub_id"
+                + " INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        db.execSQL("ALTER TABLE " + SmsProvider.TABLE_SMS
+                + " ADD COLUMN " + Sms.SUBSCRIPTION_ID
+                + " INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        db.execSQL("ALTER TABLE " + SmsProvider.TABLE_RAW
+                +" ADD COLUMN " + Sms.SUBSCRIPTION_ID
+                + " INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID);
     }
 
     private void upgradeDatabaseToVersion59(SQLiteDatabase db) {
@@ -1829,7 +1836,8 @@
                 Mms.DELIVERY_TIME + " INTEGER," +
                 Mms.DELIVERY_REPORT + " INTEGER," +
                 Mms.LOCKED + " INTEGER DEFAULT 0," +
-                Mms.SUB_ID + " INTEGER DEFAULT -1," +
+                Mms.SUBSCRIPTION_ID + " INTEGER DEFAULT "
+                        + SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
                 Mms.SEEN + " INTEGER DEFAULT 0," +
                 Mms.TEXT_ONLY + " INTEGER DEFAULT 0" +
                 ");");
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 65b377c..457e472 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -16,11 +16,6 @@
 
 package com.android.providers.telephony;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
 import android.app.AppOpsManager;
 import android.content.ContentProvider;
 import android.content.ContentValues;
@@ -32,22 +27,27 @@
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.UserHandle;
 import android.provider.BaseColumns;
-import android.provider.Telephony;
 import android.provider.Telephony.CanonicalAddressesColumns;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
+import android.provider.Telephony.MmsSms.PendingMessages;
 import android.provider.Telephony.Sms;
+import android.provider.Telephony.Sms.Conversations;
 import android.provider.Telephony.Threads;
 import android.provider.Telephony.ThreadsColumns;
-import android.provider.Telephony.MmsSms.PendingMessages;
-import android.provider.Telephony.Sms.Conversations;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.google.android.mms.pdu.PduHeaders;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 /**
  * This class provides the ability to query the MMS and SMS databases
  * at the same time, mixing messages from both in a single thread
@@ -120,7 +120,7 @@
     // SMS ("sms") message tables.
     private static final String[] MMS_SMS_COLUMNS =
             { BaseColumns._ID, Mms.DATE, Mms.DATE_SENT, Mms.READ, Mms.THREAD_ID, Mms.LOCKED,
-                    Mms.SUB_ID };
+                    Mms.SUBSCRIPTION_ID };
 
     // These are the columns that appear only in the MMS message
     // table.
@@ -1240,13 +1240,14 @@
     @Override
     public int update(Uri uri, ContentValues values,
             String selection, String[] selectionArgs) {
+        final int callerUid = Binder.getCallingUid();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         int affectedRows = 0;
         switch(URI_MATCHER.match(uri)) {
             case URI_CONVERSATIONS_MESSAGES:
                 String threadIdString = uri.getPathSegments().get(1);
                 affectedRows = updateConversation(threadIdString, values,
-                        selection, selectionArgs);
+                        selection, selectionArgs, callerUid);
                 break;
 
             case URI_PENDING_MSG:
@@ -1286,12 +1287,22 @@
 
     private int updateConversation(
             String threadIdString, ContentValues values, String selection,
-            String[] selectionArgs) {
+            String[] selectionArgs, int callerUid) {
         try {
             Long.parseLong(threadIdString);
         } catch (NumberFormatException exception) {
             Log.e(LOG_TAG, "Thread ID must be a Long.");
             return 0;
+
+        }
+        if (ProviderUtil.shouldRemoveCreator(values, callerUid)) {
+            // CREATOR should not be changed by non-SYSTEM/PHONE apps
+            Log.w(LOG_TAG, ProviderUtil.getPackageNamesByUid(getContext(), callerUid) +
+                    " tries to update CREATOR");
+            // Sms.CREATOR and Mms.CREATOR are same. But let's do this
+            // twice in case the names may differ in the future
+            values.remove(Sms.CREATOR);
+            values.remove(Mms.CREATOR);
         }
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
diff --git a/src/com/android/providers/telephony/ProviderUtil.java b/src/com/android/providers/telephony/ProviderUtil.java
new file mode 100644
index 0000000..8abe934
--- /dev/null
+++ b/src/com/android/providers/telephony/ProviderUtil.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 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.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.provider.Telephony;
+import android.text.TextUtils;
+
+/**
+ * Helpers
+ */
+public class ProviderUtil {
+
+    /**
+     * Get space separated package names associated with a UID
+     *
+     * @param context The context to use
+     * @param uid The UID to look up
+     * @return The space separated list of package names for UID
+     */
+    public static String getPackageNamesByUid(Context context, int uid) {
+        final PackageManager pm = context.getPackageManager();
+        final String[] packageNames = pm.getPackagesForUid(uid);
+        if (packageNames != null) {
+            final StringBuilder sb = new StringBuilder();
+            for (String name : packageNames) {
+                if (!TextUtils.isEmpty(name)) {
+                    if (sb.length() > 0) {
+                        sb.append(' ');
+                    }
+                    sb.append(name);
+                }
+            }
+            return sb.toString();
+        }
+        return null;
+    }
+
+    /**
+     * Whether should set CREATOR for an insertion
+     *
+     * @param values The content of the message
+     * @param uid The caller UID of the insertion
+     * @return true if we should set CREATOR, false otherwise
+     */
+    public static boolean shouldSetCreator(ContentValues values, int uid) {
+        return (uid != Process.SYSTEM_UID && uid != Process.PHONE_UID) ||
+                (!values.containsKey(Telephony.Sms.CREATOR) &&
+                        !values.containsKey(Telephony.Mms.CREATOR));
+    }
+
+    /**
+     * Whether should remove CREATOR for an update
+     *
+     * @param values The content of the message
+     * @param uid The caller UID of the update
+     * @return true if we should remove CREATOR, false otherwise
+     */
+    public static boolean shouldRemoveCreator(ContentValues values, int uid) {
+        return (uid != Process.SYSTEM_UID && uid != Process.PHONE_UID) &&
+                (values.containsKey(Telephony.Sms.CREATOR) ||
+                        values.containsKey(Telephony.Mms.CREATOR));
+    }
+}
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 5f0a8af..03d2aa6 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -20,7 +20,6 @@
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
-import android.content.Context;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
@@ -33,7 +32,6 @@
 import android.os.UserHandle;
 import android.provider.Contacts;
 import android.provider.Telephony;
-import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.Sms;
 import android.provider.Telephony.TextBasedSmsColumns;
@@ -357,15 +355,16 @@
 
     @Override
     public Uri insert(Uri url, ContentValues initialValues) {
+        final int callerUid = Binder.getCallingUid();
         long token = Binder.clearCallingIdentity();
         try {
-            return insertInner(url, initialValues);
+            return insertInner(url, initialValues, callerUid);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    private Uri insertInner(Uri url, ContentValues initialValues) {
+    private Uri insertInner(Uri url, ContentValues initialValues, int callerUid) {
         ContentValues values;
         long rowID;
         int type = Sms.MESSAGE_TYPE_ALL;
@@ -508,6 +507,13 @@
                 // Mark all non-inbox messages read.
                 values.put(Sms.READ, ONE);
             }
+            if (ProviderUtil.shouldSetCreator(values, callerUid)) {
+                // Only SYSTEM or PHONE can set CREATOR
+                // If caller is not SYSTEM or PHONE, or SYSTEM or PHONE does not set CREATOR
+                // set CREATOR using the truth on caller.
+                // Note: Inferring package name from UID may include unrelated package names
+                values.put(Sms.CREATOR, ProviderUtil.getPackageNamesByUid(getContext(), callerUid));
+            }
         } else {
             if (initialValues == null) {
                 values = new ContentValues(1);
@@ -541,7 +547,7 @@
             notifyChange(uri);
             return uri;
         } else {
-            Log.e(TAG,"insert: failed! " + values.toString());
+            Log.e(TAG,"insert: failed!");
         }
 
         return null;
@@ -636,6 +642,7 @@
 
     @Override
     public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+        final int callerUid = Binder.getCallingUid();
         int count = 0;
         String table = TABLE_SMS;
         String extraWhere = null;
@@ -695,6 +702,13 @@
                         "URI " + url + " not supported");
         }
 
+        if (table.equals(TABLE_SMS) && ProviderUtil.shouldRemoveCreator(values, callerUid)) {
+            // CREATOR should not be changed by non-SYSTEM/PHONE apps
+            Log.w(TAG, ProviderUtil.getPackageNamesByUid(getContext(), callerUid) +
+                    " tries to update CREATOR");
+            values.remove(Sms.CREATOR);
+        }
+
         where = DatabaseUtils.concatenateWhere(where, extraWhere);
         count = db.update(table, values, where, whereArgs);
 
diff --git a/src/com/android/providers/telephony/TelephonyProvider.java b/src/com/android/providers/telephony/TelephonyProvider.java
index ead4887..64ab430 100644
--- a/src/com/android/providers/telephony/TelephonyProvider.java
+++ b/src/com/android/providers/telephony/TelephonyProvider.java
@@ -35,6 +35,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.UserHandle;
 import android.provider.Telephony;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -61,7 +62,7 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
-    private static final int DATABASE_VERSION = 12 << 16;
+    private static final int DATABASE_VERSION = 13 << 16;
     private static final int URL_UNKNOWN = 0;
     private static final int URL_TELEPHONY = 1;
     private static final int URL_CURRENT = 2;
@@ -85,6 +86,7 @@
     private static final String COLUMN_APN_ID = "apn_id";
 
     private static final String PARTNER_APNS_PATH = "etc/apns-conf.xml";
+    private static final String OEM_APNS_PATH = "telephony/apns-conf.xml";
 
     private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
@@ -187,14 +189,15 @@
         private void createSimInfoTable(SQLiteDatabase db) {
             if (DBG) log("dbh.createSimInfoTable:+");
             db.execSQL("CREATE TABLE " + SIMINFO_TABLE + "("
-                    + "_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+                    + SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
                     + SubscriptionManager.ICC_ID + " TEXT NOT NULL,"
-                    + SubscriptionManager.SIM_ID + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","
+                    + SubscriptionManager.SIM_SLOT_INDEX + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","
                     + SubscriptionManager.DISPLAY_NAME + " TEXT,"
+                    + SubscriptionManager.CARRIER_NAME + " TEXT,"
                     + SubscriptionManager.NAME_SOURCE + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + ","
                     + SubscriptionManager.COLOR + " INTEGER DEFAULT " + SubscriptionManager.COLOR_DEFAULT + ","
                     + SubscriptionManager.NUMBER + " TEXT,"
-                    + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL DEFAULT " + SubscriptionManager.DISLPAY_NUMBER_DEFAULT + ","
+                    + SubscriptionManager.DISPLAY_NUMBER_FORMAT + " INTEGER NOT NULL DEFAULT " + SubscriptionManager.DISPLAY_NUMBER_DEFAULT + ","
                     + SubscriptionManager.DATA_ROAMING + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
                     + SubscriptionManager.MCC + " INTEGER DEFAULT 0,"
                     + SubscriptionManager.MNC + " INTEGER DEFAULT 0"
@@ -229,17 +232,16 @@
                     "bearer INTEGER," +
                     "mvno_type TEXT," +
                     "mvno_match_data TEXT," +
-                    "sub_id LONG DEFAULT -1," +
+                    "sub_id INTEGER DEFAULT " + SubscriptionManager.INVALID_SUBSCRIPTION_ID + "," +
                     "profile_id INTEGER default 0," +
                     "modem_cognitive BOOLEAN default 0," +
                     "max_conns INTEGER default 0," +
                     "wait_time INTEGER default 0," +
                     "max_conns_time INTEGER default 0," +
                     "mtu INTEGER);");
-             /* FIXME Currenlty sub_id is column is not used for query purpose.
-             This would be modified to more appropriate default value later. */
             if (DBG) log("dbh.createCarriersTable:-");
         }
+
         private void initDatabase(SQLiteDatabase db) {
             if (VDBG) log("dbh.initDatabase:+ db=" + db);
             // Read internal APNS data
@@ -260,7 +262,27 @@
             XmlPullParser confparser = null;
             // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
             File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
+            File oemConfFile =  new File(Environment.getOemDirectory(), OEM_APNS_PATH);
+            if (oemConfFile.exists()) {
+                // OEM image exist APN xml, get the timestamp from OEM & System image for comparison
+                long oemApnTime = oemConfFile.lastModified();
+                long sysApnTime = confFile.lastModified();
+                if (DBG) log("APNs Timestamp: oemTime = " + oemApnTime + " sysTime = "
+                        + sysApnTime);
+
+                // To get the latest version from OEM or System image
+                if (oemApnTime > sysApnTime) {
+                    if (DBG) log("APNs Timestamp: OEM image is greater than System image");
+                    confFile = oemConfFile;
+                }
+            } else {
+                // No Apn in OEM image, so load it from system image.
+                if (DBG) log("No APNs in OEM image = " + oemConfFile.getPath() +
+                        " Load APNs from system image");
+            }
+
             FileReader confreader = null;
+            if (DBG) log("confFile = " + confFile);
             try {
                 confreader = new FileReader(confFile);
                 confparser = Xml.newPullParser();
@@ -338,7 +360,8 @@
             }
             if (oldVersion < (9 << 16 | 6)) {
                 db.execSQL("ALTER TABLE " + CARRIERS_TABLE +
-                        " ADD COLUMN sub_id LONG DEFAULT -1;");
+                        " ADD COLUMN sub_id INTEGER DEFAULT " +
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID + ";");
                 oldVersion = 9 << 16 | 6;
             }
             if (oldVersion < (10 << 16 | 6)) {
@@ -374,6 +397,19 @@
                 }
                 oldVersion = 12 << 16 | 6;
             }
+            if (oldVersion < (13 << 16 | 6)) {
+                try {
+                    // Try to update the siminfo table. It might not be there.
+                    db.execSQL("ALTER TABLE " + SIMINFO_TABLE +
+                            " ADD COLUMN " + SubscriptionManager.CARRIER_NAME + " TEXT DEFAULT '';");
+                } catch (SQLiteException e) {
+                    if (DBG) {
+                        log("onUpgrade skipping " + SIMINFO_TABLE + " upgrade. " +
+                                " The table will get created in onOpen.");
+                    }
+                }
+                oldVersion = 13 << 16 | 6;
+            }
             if (DBG) {
                 log("dbh.onUpgrade:- db=" + db + " oldV=" + oldVersion + " newV=" + newVersion);
             }
@@ -578,9 +614,9 @@
                 values.put(Telephony.Carriers.MVNO_MATCH_DATA, "");
             }
 
-            long subId = SubscriptionManager.getDefaultSubId();
-            if (!values.containsKey(Telephony.Carriers.SUB_ID)) {
-                values.put(Telephony.Carriers.SUB_ID, subId);
+            int subId = SubscriptionManager.getDefaultSubId();
+            if (!values.containsKey(Telephony.Carriers.SUBSCRIPTION_ID)) {
+                values.put(Telephony.Carriers.SUBSCRIPTION_ID, subId);
             }
 
             if (!values.containsKey(Telephony.Carriers.PROFILE_ID)) {
@@ -616,7 +652,7 @@
         return true;
     }
 
-    private void setPreferredApnId(Long id, long subId) {
+    private void setPreferredApnId(Long id, int subId) {
         SharedPreferences sp = getContext().getSharedPreferences(
                 PREF_FILE + subId, Context.MODE_PRIVATE);
         SharedPreferences.Editor editor = sp.edit();
@@ -624,7 +660,7 @@
         editor.apply();
     }
 
-    private long getPreferredApnId(long subId) {
+    private long getPreferredApnId(int subId) {
         SharedPreferences sp = getContext().getSharedPreferences(
                 PREF_FILE + subId, Context.MODE_PRIVATE);
         return sp.getLong(COLUMN_APN_ID, -1);
@@ -635,7 +671,7 @@
             String[] selectionArgs, String sort) {
         TelephonyManager mTelephonyManager =
                 (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        long subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubId();
         String subIdString;
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
         qb.setStrict(true); // a little protection from injection attacks
@@ -646,7 +682,7 @@
             case URL_TELEPHONY_USING_SUBID: {
                 subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     return null;
@@ -665,7 +701,7 @@
             case URL_CURRENT_USING_SUBID: {
                 subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     return null;
@@ -691,7 +727,7 @@
             case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {
                 subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     return null;
@@ -773,7 +809,7 @@
     public Uri insert(Uri url, ContentValues initialValues)
     {
         Uri result = null;
-        long subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubId();
 
         checkPermission();
 
@@ -786,7 +822,7 @@
             {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     return result;
@@ -821,7 +857,7 @@
             {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     return result;
@@ -856,7 +892,7 @@
             {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     return result;
@@ -884,7 +920,8 @@
         }
 
         if (notify) {
-            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
+            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,
+                    true, UserHandle.USER_ALL);
         }
 
         return result;
@@ -894,7 +931,7 @@
     public int delete(Uri url, String where, String[] whereArgs)
     {
         int count = 0;
-        long subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubId();
 
         checkPermission();
 
@@ -906,7 +943,7 @@
             {
                  String subIdString = url.getLastPathSegment();
                  try {
-                     subId = Long.parseLong(subIdString);
+                     subId = Integer.parseInt(subIdString);
                  } catch (NumberFormatException e) {
                      loge("NumberFormatException" + e);
                      throw new IllegalArgumentException("Invalid subId " + url);
@@ -925,7 +962,7 @@
             case URL_CURRENT_USING_SUBID: {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     throw new IllegalArgumentException("Invalid subId " + url);
@@ -951,7 +988,7 @@
             case URL_RESTOREAPN_USING_SUBID: {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     throw new IllegalArgumentException("Invalid subId " + url);
@@ -969,7 +1006,7 @@
             case URL_PREFERAPN_NO_UPDATE_USING_SUBID: {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     throw new IllegalArgumentException("Invalid subId " + url);
@@ -997,7 +1034,8 @@
         }
 
         if (count > 0) {
-            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null);
+            getContext().getContentResolver().notifyChange(Telephony.Carriers.CONTENT_URI, null,
+                    true, UserHandle.USER_ALL);
         }
 
         return count;
@@ -1008,7 +1046,7 @@
     {
         int count = 0;
         int uriType = URL_UNKNOWN;
-        long subId = SubscriptionManager.getDefaultSubId();
+        int subId = SubscriptionManager.getDefaultSubId();
 
         checkPermission();
 
@@ -1020,7 +1058,7 @@
             {
                  String subIdString = url.getLastPathSegment();
                  try {
-                     subId = Long.parseLong(subIdString);
+                     subId = Integer.parseInt(subIdString);
                  } catch (NumberFormatException e) {
                      loge("NumberFormatException" + e);
                      throw new IllegalArgumentException("Invalid subId " + url);
@@ -1040,7 +1078,7 @@
             {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     throw new IllegalArgumentException("Invalid subId " + url);
@@ -1072,7 +1110,7 @@
             {
                 String subIdString = url.getLastPathSegment();
                 try {
-                    subId = Long.parseLong(subIdString);
+                    subId = Integer.parseInt(subIdString);
                 } catch (NumberFormatException e) {
                     loge("NumberFormatException" + e);
                     throw new IllegalArgumentException("Invalid subId " + url);
@@ -1110,11 +1148,11 @@
             switch (uriType) {
                 case URL_SIMINFO:
                     getContext().getContentResolver().notifyChange(
-                            SubscriptionManager.CONTENT_URI, null);
+                            SubscriptionManager.CONTENT_URI, null, true, UserHandle.USER_ALL);
                     break;
                 default:
                     getContext().getContentResolver().notifyChange(
-                            Telephony.Carriers.CONTENT_URI, null);
+                            Telephony.Carriers.CONTENT_URI, null, true, UserHandle.USER_ALL);
             }
         }
 
@@ -1144,7 +1182,7 @@
 
     private DatabaseHelper mOpenHelper;
 
-    private void restoreDefaultAPN(long subId) {
+    private void restoreDefaultAPN(int subId) {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
 
         try {