merge from open-source master

Change-Id: Ie2616d4bbc9f1f545accbf043176b698e2e7dc3f
diff --git a/Android.mk b/Android.mk
index 97439a2..b0f3906 100644
--- a/Android.mk
+++ b/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := user
+LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_JAVA_LIBRARIES := 
-
 LOCAL_PACKAGE_NAME := TelephonyProvider
 LOCAL_CERTIFICATE := platform
 
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
 include $(BUILD_PACKAGE)
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..b84e1b6
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/res/drawable-mdpi/ic_launcher_phone.png b/res/drawable-mdpi/ic_launcher_phone.png
index 4e613ec..88336a3 100644
--- a/res/drawable-mdpi/ic_launcher_phone.png
+++ b/res/drawable-mdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index dc74a30..f811e9a 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Úložiště vytáčení"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 0888bcf..37eae10 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Opkaldslagring"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 66e94a3..703fe33 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Speicher des Wählprogramms"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 5efe232..cc55573 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Αποθηκευτικός χώρος προγράμματος αποθήκευσης"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 2fee906..2d26cbb 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Almacenamiento del marcador"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index f3744ec..f134392 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Dialer Storage"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b115c14..fb2f0fa 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Stockage téléphone"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 5762831..f4ee7e6 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Archiviazione dialer"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 16ea74d..bbcc59c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"端末情報保存"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 1baa28d..2d90961 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"다이얼러 저장소"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 27e6563..5e2074a 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Dialer-lagring"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index ea5b3ac..ce97015 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Telefoonopslag"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 4680eff..500d2c9 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Magazyn telefonu"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b11fbbf..28b7ca9 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Armazenamento do Marcador"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 1a97e6d..da8b8c7 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Armazenamento do discador"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d0dca6f..1c87b4e 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Хранилище данных телефона"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index eb4d752..5068312 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Samtalslagring"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 2430820..658ddea 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"Çevirici Deposu"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index cc79baa..52fcf21 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"拨号器存储"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 08c3f62..e080644 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!--  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.
@@ -12,7 +12,8 @@
      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" msgid="4846406803456361049">"撥號儲存空間"</string>
diff --git a/src/com/android/providers/telephony/MmsProvider.java b/src/com/android/providers/telephony/MmsProvider.java
index f141cd4..a4e6919 100644
--- a/src/com/android/providers/telephony/MmsProvider.java
+++ b/src/com/android/providers/telephony/MmsProvider.java
@@ -16,8 +16,6 @@
 
 package com.android.providers.telephony;
 
-import com.google.android.mms.pdu.PduHeaders;
-
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -30,6 +28,7 @@
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.provider.BaseColumns;
+import android.provider.Telephony;
 import android.provider.Telephony.CanonicalAddressesColumns;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
@@ -40,6 +39,8 @@
 import android.util.Config;
 import android.util.Log;
 
+import com.google.android.mms.pdu.PduHeaders;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -55,6 +56,7 @@
     static final String TABLE_PART = "part";
     static final String TABLE_RATE = "rate";
     static final String TABLE_DRM  = "drm";
+    static final String TABLE_WORDS = "words";
 
     @Override
     public boolean onCreate() {
@@ -377,10 +379,12 @@
             }
 
             String contentType = values.getAsString("ct");
-            
+
             // text/plain and app application/smil store their "data" inline in the
             // table so there's no need to create the file
-            if (!"text/plain".equals(contentType) && !"application/smil".equals(contentType)) {
+            boolean plainText = "text/plain".equals(contentType);
+            boolean smilText = "application/smil".equals(contentType);
+            if (!plainText && !smilText) {
                 // Generate the '_data' field of the part with default
                 // permission settings.
                 String path = getContext().getDir("parts", 0).getPath()
@@ -409,6 +413,26 @@
             }
 
             res = Uri.parse(res + "/part/" + rowId);
+
+            // Don't use a trigger for updating the words table because of a bug
+            // in FTS3.  The bug is such that the call to get the last inserted
+            // row is incorrect.
+            if (plainText) {
+                // Update the words table with a corresponding row.  The words table
+                // allows us to search for words quickly, without scanning the whole
+                // table;
+                ContentValues cv = new ContentValues();
+
+                // we're using the row id of the part table row but we're also using ids
+                // from the sms table so this divides the space into two large chunks.
+                // The row ids from the part table start at 2 << 32.
+                cv.put(Telephony.MmsSms.WordsTable.ID, (2 << 32) + rowId);
+                cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("text"));
+                cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowId);
+                cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 2);
+                db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
+            }
+
         } else if (table.equals(TABLE_RATE)) {
             long now = values.getAsLong(Rate.SENT_TIME);
             long oneHourAgo = now - 1000 * 60 * 60;
diff --git a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
index b62d4eb..c26e855 100644
--- a/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
+++ b/src/com/android/providers/telephony/MmsSmsDatabaseHelper.java
@@ -16,26 +16,19 @@
 
 package com.android.providers.telephony;
 
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_DELIVERY_IND;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_REC_IND;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF;
-import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_SEND_REQ;
-
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.FileInputStream;
 import java.io.File;
 import java.util.ArrayList;
 
-import com.google.android.mms.pdu.EncodedStringValue;
-
+import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.provider.BaseColumns;
+import android.provider.Telephony;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.Sms;
@@ -46,6 +39,9 @@
 import android.provider.Telephony.MmsSms.PendingMessages;
 import android.util.Log;
 
+import com.google.android.mms.pdu.EncodedStringValue;
+import com.google.android.mms.pdu.PduHeaders;
+
 public class MmsSmsDatabaseHelper extends SQLiteOpenHelper {
     private static final String TAG = "MmsSmsDatabaseHelper";
 
@@ -98,16 +94,27 @@
                         "END;";
 
     private static final String PDU_UPDATE_THREAD_CONSTRAINTS =
-                        "  WHEN new." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_RETRIEVE_CONF +
-                        "    OR new." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_NOTIFICATION_IND +
-                        "    OR new." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_SEND_REQ + " ";
+                        "  WHEN new." + Mms.MESSAGE_TYPE + "=" +
+                        PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF +
+                        "    OR new." + Mms.MESSAGE_TYPE + "=" +
+                        PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND +
+                        "    OR new." + Mms.MESSAGE_TYPE + "=" +
+                        PduHeaders.MESSAGE_TYPE_SEND_REQ + " ";
 
+    // When looking in the pdu table for unread messages, only count messages that
+    // are displayed to the user. The constants are defined in PduHeaders and could be used
+    // here, but the string "(m_type=132 OR m_type=130 OR m_type=128)" is used throughout this
+    // file and so it is used here to be consistent.
+    //     m_type=128   = MESSAGE_TYPE_SEND_REQ
+    //     m_type=130   = MESSAGE_TYPE_NOTIFICATION_IND
+    //     m_type=132   = MESSAGE_TYPE_RETRIEVE_CONF
     private static final String PDU_UPDATE_THREAD_READ_BODY =
                         "  UPDATE threads SET read = " +
                         "    CASE (SELECT COUNT(*)" +
                         "          FROM " + MmsProvider.TABLE_PDU +
                         "          WHERE " + Mms.READ + " = 0" +
-                        "            AND " + Mms.THREAD_ID + " = threads._id)" +
+                        "            AND " + Mms.THREAD_ID + " = threads._id " +
+                        "            AND (m_type=132 OR m_type=130 OR m_type=128)) " +
                         "      WHEN 0 THEN 1" +
                         "      ELSE 0" +
                         "    END" +
@@ -177,9 +184,10 @@
                         " BEGIN " +
                         "  UPDATE threads SET has_attachment = " +
                         "   CASE " +
-                        "    (SELECT COUNT(*) FROM part JOIN pdu ON pdu._id=part.mid " +
+                        "    (SELECT COUNT(*) FROM part JOIN pdu " +
                         "     WHERE pdu.thread_id = threads._id " +
-                        "     AND part.ct != 'text/plain' AND part.ct != 'application/smil')" +
+                        "     AND part.ct != 'text/plain' AND part.ct != 'application/smil' " +
+                        "     AND part.mid = pdu._id)" +
                         "   WHEN 0 THEN 0 " +
                         "   ELSE 1 " +
                         "   END; " +
@@ -192,14 +200,15 @@
                         " AFTER UPDATE of thread_id ON pdu " +
                         " BEGIN " +
                         "  UPDATE threads SET has_attachment=1 WHERE _id IN " +
-                        "   (SELECT pdu.thread_id FROM part JOIN pdu ON new._id=part.mid " +
-                        "     WHERE part.ct != 'text/plain' AND part.ct != 'application/smil'); " +
+                        "   (SELECT pdu.thread_id FROM part JOIN pdu " +
+                        "     WHERE part.ct != 'text/plain' AND part.ct != 'application/smil' " +
+                        "     AND part.mid = pdu._id);" +
                         " END";
 
     private static MmsSmsDatabaseHelper mInstance = null;
 
     static final String DATABASE_NAME = "mmssms.db";
-    static final int DATABASE_VERSION = 47;
+    static final int DATABASE_VERSION = 53;
 
     private MmsSmsDatabaseHelper(Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -274,11 +283,37 @@
 
         // Update the error column of the thread to indicate if there
         // are any messages in it that have failed to send.
-        db.execSQL(
-            "UPDATE threads SET error =" +
-            "       (SELECT COUNT(*) FROM sms WHERE type=5" +
-            "        AND thread_id = " + thread_id + " LIMIT 1)" +
-            "   WHERE threads._id = " + thread_id + ";");
+        // First check to see if there are any messages with errors in this thread.
+        String query = "SELECT thread_id FROM sms WHERE type=" +
+            Telephony.TextBasedSmsColumns.MESSAGE_TYPE_FAILED +
+            " AND thread_id = " + thread_id +
+                                " LIMIT 1";
+        int setError = 0;
+        Cursor c = db.rawQuery(query, null);
+        if (c != null) {
+            try {
+                setError = c.getCount();    // Because of the LIMIT 1, count will be 1 or 0.
+            } finally {
+                c.close();
+            }
+        }
+        // What's the current state of the error flag in the threads table?
+        String errorQuery = "SELECT error FROM threads WHERE _id = " + thread_id;
+        c = db.rawQuery(errorQuery, null);
+        if (c != null) {
+            try {
+                if (c.moveToNext()) {
+                    int curError = c.getInt(0);
+                    if (curError != setError) {
+                        // The current thread error column differs, update it.
+                        db.execSQL("UPDATE threads SET error=" + setError +
+                                " WHERE _id = " + thread_id);
+                    }
+                }
+            } finally {
+                c.close();
+            }
+        }
     }
 
     public static void updateAllThreads(SQLiteDatabase db, String where, String[] whereArgs) {
@@ -291,10 +326,13 @@
                        "(SELECT DISTINCT thread_id FROM sms " + where + ")";
         Cursor c = db.rawQuery(query, whereArgs);
         if (c != null) {
-            while (c.moveToNext()) {
-                updateThread(db, c.getInt(0));
+            try {
+                while (c.moveToNext()) {
+                    updateThread(db, c.getInt(0));
+                }
+            } finally {
+                c.close();
             }
-            c.close();
         }
         // remove orphaned threads
         db.delete("threads",
@@ -330,6 +368,120 @@
         createCommonTables(db);
         createCommonTriggers(db);
         createMmsTriggers(db);
+        createWordsTables(db);
+        createIndices(db);
+    }
+
+    // When upgrading the database we need to populate the words
+    // table with the rows out of sms and part.
+    private void populateWordsTable(SQLiteDatabase db) {
+        final String TABLE_WORDS = "words";
+        {
+            Cursor smsRows = db.query(
+                    "sms",
+                    new String[] { Sms._ID, Sms.BODY },
+                    null,
+                    null,
+                    null,
+                    null,
+                    null);
+            try {
+                if (smsRows != null) {
+                    smsRows.moveToPosition(-1);
+                    ContentValues cv = new ContentValues();
+                    while (smsRows.moveToNext()) {
+                        cv.clear();
+
+                        long id = smsRows.getLong(0);        // 0 for Sms._ID
+                        String body = smsRows.getString(1);  // 1 for Sms.BODY
+
+                        cv.put(Telephony.MmsSms.WordsTable.ID, id);
+                        cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, body);
+                        cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, id);
+                        cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 1);
+                        db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
+                    }
+                }
+            } finally {
+                if (smsRows != null) {
+                    smsRows.close();
+                }
+            }
+        }
+
+        {
+            Cursor mmsRows = db.query(
+                    "part",
+                    new String[] { Part._ID, Part.TEXT },
+                    "ct = 'text/plain'",
+                    null,
+                    null,
+                    null,
+                    null);
+            try {
+                if (mmsRows != null) {
+                    mmsRows.moveToPosition(-1);
+                    ContentValues cv = new ContentValues();
+                    while (mmsRows.moveToNext()) {
+                        cv.clear();
+
+                        long id = mmsRows.getLong(0);         // 0 for Part._ID
+                        String body = mmsRows.getString(1);   // 1 for Part.TEXT
+
+                        cv.put(Telephony.MmsSms.WordsTable.ID, id);
+                        cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, body);
+                        cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, id);
+                        cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 1);
+                        db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
+                    }
+                }
+            } finally {
+                if (mmsRows != null) {
+                    mmsRows.close();
+                }
+            }
+        }
+    }
+
+    private void createWordsTables(SQLiteDatabase db) {
+        try {
+            db.execSQL("CREATE VIRTUAL TABLE words USING FTS3 (_id INTEGER PRIMARY KEY, index_text TEXT, source_id INTEGER, table_to_use INTEGER);");
+
+            // monitor the sms table
+            // NOTE don't handle inserts using a trigger because it has an unwanted
+            // side effect:  the value returned for the last row ends up being the
+            // id of one of the trigger insert not the original row insert.
+            // Handle inserts manually in the provider.
+            db.execSQL("CREATE TRIGGER sms_words_update AFTER UPDATE ON sms BEGIN UPDATE words " +
+                    " SET index_text = NEW.body WHERE (source_id=NEW._id AND table_to_use=1); " +
+                    " END;");
+            db.execSQL("CREATE TRIGGER sms_words_delete AFTER DELETE ON sms BEGIN DELETE FROM " +
+                    "  words WHERE source_id = OLD._id AND table_to_use = 1; END;");
+
+            // monitor the mms table
+            db.execSQL("CREATE TRIGGER mms_words_update AFTER UPDATE ON part BEGIN UPDATE words " +
+                    " SET index_text = NEW.text WHERE (source_id=NEW._id AND table_to_use=2); " +
+                    " END;");
+            db.execSQL("CREATE TRIGGER mms_words_delete AFTER DELETE ON part BEGIN DELETE FROM " +
+                    " words WHERE source_id = OLD._id AND table_to_use = 2; END;");
+
+            populateWordsTable(db);
+        } catch (Exception ex) {
+            Log.e(TAG, "got exception creating words table: " + ex.toString());
+        }
+    }
+
+    private void createIndices(SQLiteDatabase db) {
+        createThreadIdIndex(db);
+    }
+
+    private void createThreadIdIndex(SQLiteDatabase db) {
+        try {
+            db.execSQL("CREATE INDEX IF NOT EXISTS typeThreadIdIndex ON sms" +
+            " (type, thread_id);");
+        } catch (Exception ex) {
+            Log.e(TAG, "got exception creating indices: " + ex.toString());
+        }
     }
 
     private void createMmsTables(SQLiteDatabase db) {
@@ -365,7 +517,8 @@
                    Mms.RESPONSE_TEXT + " TEXT," +
                    Mms.DELIVERY_TIME + " INTEGER," +
                    Mms.DELIVERY_REPORT + " INTEGER," +
-                   Mms.LOCKED + " INTEGER DEFAULT 0" +
+                   Mms.LOCKED + " INTEGER DEFAULT 0," +
+                   Mms.SEEN + " INTEGER DEFAULT 0" +
                    ");");
 
         db.execSQL("CREATE TABLE " + MmsProvider.TABLE_ADDR + " (" +
@@ -419,11 +572,12 @@
         // associated Send.req.
         db.execSQL("CREATE TRIGGER cleanup_delivery_and_read_report " +
                    "AFTER DELETE ON " + MmsProvider.TABLE_PDU + " " +
-                   "WHEN old." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_SEND_REQ + " " +
+                   "WHEN old." + Mms.MESSAGE_TYPE + "=" + PduHeaders.MESSAGE_TYPE_SEND_REQ + " " +
                    "BEGIN " +
                    "  DELETE FROM " + MmsProvider.TABLE_PDU +
-                   "  WHERE (" + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_DELIVERY_IND +
-                   "    OR " + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_READ_ORIG_IND + ")" +
+                   "  WHERE (" + Mms.MESSAGE_TYPE + "=" + PduHeaders.MESSAGE_TYPE_DELIVERY_IND +
+                   "    OR " + Mms.MESSAGE_TYPE + "=" + PduHeaders.MESSAGE_TYPE_READ_ORIG_IND +
+                   ")" +
                    "    AND " + Mms.MESSAGE_ID + "=old." + Mms.MESSAGE_ID + "; " +
                    "END;");
 
@@ -455,7 +609,9 @@
                    "subject TEXT," +
                    "body TEXT," +
                    "service_center TEXT," +
-                   "locked INTEGER DEFAULT 0" +
+                   "locked INTEGER DEFAULT 0," +
+                   "error_code INTEGER DEFAULT 0," +
+                   "seen INTEGER DEFAULT 0" +
                    ");");
 
         /**
@@ -621,8 +777,9 @@
         // when they are inserted into Inbox/Outbox.
         db.execSQL("CREATE TRIGGER insert_mms_pending_on_insert " +
                    "AFTER INSERT ON pdu " +
-                   "WHEN new." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_NOTIFICATION_IND +
-                   "  OR new." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_READ_REC_IND + " " +
+                   "WHEN new." + Mms.MESSAGE_TYPE + "=" + PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND +
+                   "  OR new." + Mms.MESSAGE_TYPE + "=" + PduHeaders.MESSAGE_TYPE_READ_REC_IND +
+                   " " +
                    "BEGIN " +
                    "  INSERT INTO " + MmsSmsProvider.TABLE_PENDING_MSG +
                    "    (" + PendingMessages.PROTO_TYPE + "," +
@@ -641,7 +798,7 @@
         // Insert pending status for M-Send.req when it is moved into Outbox.
         db.execSQL("CREATE TRIGGER insert_mms_pending_on_update " +
                    "AFTER UPDATE ON pdu " +
-                   "WHEN new." + Mms.MESSAGE_TYPE + "=" + MESSAGE_TYPE_SEND_REQ +
+                   "WHEN new." + Mms.MESSAGE_TYPE + "=" + PduHeaders.MESSAGE_TYPE_SEND_REQ +
                    "  AND new." + Mms.MESSAGE_BOX + "=" + Mms.MESSAGE_BOX_OUTBOX +
                    "  AND old." + Mms.MESSAGE_BOX + "!=" + Mms.MESSAGE_BOX_OUTBOX + " " +
                    "BEGIN " +
@@ -806,7 +963,7 @@
             } finally {
                 db.endTransaction();
             }
-            // fall through 
+            // fall through
         case 44:
             if (currentVersion <= 44) {
                 return;
@@ -822,7 +979,7 @@
             } finally {
                 db.endTransaction();
             }
-            // fall through 
+            // fall through
         case 45:
             if (currentVersion <= 45) {
                 return;
@@ -853,10 +1010,94 @@
             } finally {
                 db.endTransaction();
             }
+            // fall through
+        case 47:
+            if (currentVersion <= 47) {
+                return;
+            }
+
+            db.beginTransaction();
+            try {
+                upgradeDatabaseToVersion48(db);
+                db.setTransactionSuccessful();
+            } catch (Throwable ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+                break;
+            } finally {
+                db.endTransaction();
+            }
+            // fall through
+        case 48:
+            if (currentVersion <= 48) {
+                return;
+            }
+
+            db.beginTransaction();
+            try {
+                createWordsTables(db);
+                db.setTransactionSuccessful();
+            } catch (Throwable ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+                break;
+            } finally {
+                db.endTransaction();
+            }
+            // fall through
+        case 49:
+            if (currentVersion <= 49) {
+                return;
+            }
+            db.beginTransaction();
+            try {
+                createThreadIdIndex(db);
+                db.setTransactionSuccessful();
+            } catch (Throwable ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+                break; // force to destroy all old data;
+            } finally {
+                db.endTransaction();
+            }
+            // fall through
+        case 50:
+            if (currentVersion <= 50) {
+                return;
+            }
+
+            db.beginTransaction();
+            try {
+                upgradeDatabaseToVersion51(db);
+                db.setTransactionSuccessful();
+            } catch (Throwable ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+                break;
+            } finally {
+                db.endTransaction();
+            }
+            // fall through
+        case 51:
+            if (currentVersion <= 51) {
+                return;
+            }
+            // 52 was adding a new meta_data column, but that was removed.
+            // fall through
+        case 52:
+            if (currentVersion <= 52) {
+                return;
+            }
+
+            db.beginTransaction();
+            try {
+                upgradeDatabaseToVersion53(db);
+                db.setTransactionSuccessful();
+            } catch (Throwable ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+                break;
+            } finally {
+                db.endTransaction();
+            }
             return;
         }
 
-
         Log.e(TAG, "Destroying all old data.");
         dropAll(db);
         onCreate(db);
@@ -921,12 +1162,25 @@
 
     private void upgradeDatabaseToVersion45(SQLiteDatabase db) {
         // Add 'locked' column to sms table.
-        db.execSQL("ALTER TABLE sms ADD COLUMN locked INTEGER DEFAULT 0");
+        db.execSQL("ALTER TABLE sms ADD COLUMN " + Sms.LOCKED + " INTEGER DEFAULT 0");
 
         // Add 'locked' column to pdu table.
         db.execSQL("ALTER TABLE pdu ADD COLUMN " + Mms.LOCKED + " INTEGER DEFAULT 0");
     }
 
+    private void upgradeDatabaseToVersion53(SQLiteDatabase db) {
+        db.execSQL("DROP TRIGGER IF EXISTS pdu_update_thread_read_on_update");
+
+        // Updates threads table whenever a message in pdu is updated.
+        db.execSQL("CREATE TRIGGER pdu_update_thread_read_on_update AFTER" +
+                   "  UPDATE OF " + Mms.READ +
+                   "  ON " + MmsProvider.TABLE_PDU + " " +
+                   PDU_UPDATE_THREAD_CONSTRAINTS +
+                   "BEGIN " +
+                   PDU_UPDATE_THREAD_READ_BODY +
+                   "END;");
+    }
+
     private void upgradeDatabaseToVersion46(SQLiteDatabase db) {
         // add the "text" column for caching inline text (e.g. strings) instead of
         // putting them in an external file
@@ -992,6 +1246,31 @@
         db.execSQL(PDU_UPDATE_THREADS_ON_UPDATE_TRIGGER);
     }
 
+    private void upgradeDatabaseToVersion48(SQLiteDatabase db) {
+        // Add 'error_code' column to sms table.
+        db.execSQL("ALTER TABLE sms ADD COLUMN error_code INTEGER DEFAULT 0");
+    }
+
+    private void upgradeDatabaseToVersion51(SQLiteDatabase db) {
+        db.execSQL("ALTER TABLE sms add COLUMN seen INTEGER DEFAULT 0");
+        db.execSQL("ALTER TABLE pdu add COLUMN seen INTEGER DEFAULT 0");
+
+        try {
+            // update the existing sms and pdu tables so the new "seen" column is the same as
+            // the "read" column for each row.
+            ContentValues contentValues = new ContentValues();
+            contentValues.put("seen", 1);
+            int count = db.update("sms", contentValues, "read=1", null);
+            Log.d(TAG, "[MmsSmsDb] upgradeDatabaseToVersion51: updated " + count +
+                    " rows in sms table to have READ=1");
+            count = db.update("pdu", contentValues, "read=1", null);
+            Log.d(TAG, "[MmsSmsDb] upgradeDatabaseToVersion51: updated " + count +
+                    " rows in pdu table to have READ=1");
+        } catch (Exception ex) {
+            Log.e(TAG, "[MmsSmsDb] upgradeDatabaseToVersion51 caught ", ex);
+        }
+    }
+
     private void updateThreadsAttachmentColumn(SQLiteDatabase db) {
         // Set the values of that column correctly based on the current
         // contents of the database.
diff --git a/src/com/android/providers/telephony/MmsSmsProvider.java b/src/com/android/providers/telephony/MmsSmsProvider.java
index 8b21e8e..bfe41ab 100644
--- a/src/com/android/providers/telephony/MmsSmsProvider.java
+++ b/src/com/android/providers/telephony/MmsSmsProvider.java
@@ -22,6 +22,7 @@
 import java.util.List;
 import java.util.Set;
 
+import android.app.SearchManager;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -90,8 +91,9 @@
     private static final int URI_DRAFT                             = 12;
     private static final int URI_CANONICAL_ADDRESSES               = 13;
     private static final int URI_SEARCH                            = 14;
-    private static final int URI_FIRST_LOCKED_MESSAGE_ALL          = 15;
-    private static final int URI_FIRST_LOCKED_MESSAGE_BY_THREAD_ID = 16;
+    private static final int URI_SEARCH_SUGGEST                    = 15;
+    private static final int URI_FIRST_LOCKED_MESSAGE_ALL          = 16;
+    private static final int URI_FIRST_LOCKED_MESSAGE_BY_THREAD_ID = 17;
 
     /**
      * the name of the table that is used to store the queue of
@@ -127,7 +129,7 @@
     // table.
     private static final String[] SMS_ONLY_COLUMNS =
             { "address", "body", "person", "reply_path_present",
-              "service_center", "status", "subject", "type" };
+              "service_center", "status", "subject", "type", "error_code" };
 
     // These are all the columns that appear in the "threads" table.
     private static final String[] THREADS_COLUMNS = {
@@ -209,6 +211,7 @@
         URI_MATCHER.addURI(AUTHORITY, "canonical-addresses", URI_CANONICAL_ADDRESSES);
 
         URI_MATCHER.addURI(AUTHORITY, "search", URI_SEARCH);
+        URI_MATCHER.addURI(AUTHORITY, "searchSuggest", URI_SEARCH_SUGGEST);
 
         // In this pattern, two query parameters may be supplied:
         // "protocol" and "message." For example:
@@ -321,7 +324,22 @@
                         null, null,
                         sortOrder);
                 break;
-            case URI_SEARCH:
+            case URI_SEARCH_SUGGEST: {
+                String searchString = uri.getQueryParameter("pattern");
+                String query = String.format("SELECT _id, index_text, source_id, table_to_use, offsets(words) FROM words WHERE words MATCH '%s*' LIMIT 50;", searchString);
+                if (       sortOrder != null
+                        || selection != null
+                        || selectionArgs != null
+                        || projection != null) {
+                    throw new IllegalArgumentException(
+                            "do not specify sortOrder, selection, selectionArgs, or projection" +
+                            "with this query");
+                }
+
+                cursor = db.rawQuery(query, null);
+                break;
+            }
+            case URI_SEARCH: {
                 if (       sortOrder != null
                         || selection != null
                         || selectionArgs != null
@@ -337,35 +355,45 @@
                 // using a UNION so we have to have the same number of result columns from
                 // both queries.
 
-                String searchString = "%" + uri.getQueryParameter("pattern") + "%";
-                String smsProjection = "_id,thread_id,address,body,date";
-                String mmsProjection = "pdu._id,thread_id,addr.address,part.text as body,pdu.date";
+                String searchString = uri.getQueryParameter("pattern") + "*";
 
+                String smsProjection = "sms._id as _id,thread_id,address,body,date," +
+                "index_text,words._id";
+                String mmsProjection = "pdu._id,thread_id,addr.address,part.text as " + "" +
+                		"body,pdu.date,index_text,words._id";
+
+                // search on the words table but return the rows from the corresponding sms table
                 String smsQuery = String.format(
-                        "SELECT %s FROM sms WHERE (address NOTNULL AND body LIKE ?) ",
+                        "SELECT %s FROM sms,words WHERE (words MATCH ? " +
+                        " AND sms._id=words.source_id AND words.table_to_use=1) ",
                         smsProjection);
 
-                // TODO consider whether we're really getting the right addr here (for example, if
-                // I send a message to a given phone number do I want the search result to
-                // show a match on "me" or on that phone number.  I suspect the latter.
+                // search on the words table but return the rows from the corresponding parts table
                 String mmsQuery = String.format(
-                        "SELECT %s FROM pdu,part,addr WHERE ((part.mid=pdu._id) AND " +
+                        "SELECT %s FROM pdu,part,addr,words WHERE ((part.mid=pdu._id) AND " +
                         "(addr.msg_id=pdu._id) AND " +
                         "(addr.type=%d) AND " +
                         "(part.ct='text/plain') AND " +
-                        "(body like ?))",
+                        "(words MATCH ?) AND " +
+                        "(part._id = words.source_id) AND " +
+                        "(words.table_to_use=2))",
                         mmsProjection,
                         PduHeaders.TO);
 
+                // join the results from sms and part (mms)
                 String rawQuery = String.format(
                         "%s UNION %s GROUP BY %s ORDER BY %s",
                         smsQuery,
                         mmsQuery,
                         "thread_id",
                         "thread_id ASC, date DESC");
-
-                cursor = db.rawQuery(rawQuery, new String[] { searchString, searchString });
+                try {
+                    cursor = db.rawQuery(rawQuery, new String[] { searchString, searchString });
+                } catch (Exception ex) {
+                    Log.e(LOG_TAG, "got exception: " + ex.toString());
+                }
                 break;
+            }
             case URI_PENDING_MSG: {
                 String protoName = uri.getQueryParameter("protocol");
                 String msgId = uri.getQueryParameter("message");
diff --git a/src/com/android/providers/telephony/SmsProvider.java b/src/com/android/providers/telephony/SmsProvider.java
index 069ee6a..57ac256 100644
--- a/src/com/android/providers/telephony/SmsProvider.java
+++ b/src/com/android/providers/telephony/SmsProvider.java
@@ -28,6 +28,7 @@
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.provider.Contacts;
+import android.provider.Telephony;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.Sms;
@@ -39,7 +40,7 @@
 import android.util.Config;
 import android.util.Log;
 
-import com.android.internal.database.ArrayListCursor;
+import com.android.common.ArrayListCursor;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -50,6 +51,7 @@
     static final String TABLE_SMS = "sms";
     private static final String TABLE_RAW = "raw";
     private static final String TABLE_SR_PENDING = "sr_pending";
+    private static final String TABLE_WORDS = "words";
 
     private static final Integer ONE = Integer.valueOf(1);
 
@@ -75,7 +77,8 @@
         "is_status_report",             // isStatusReportMessage
         "transport_type",               // Always "sms".
         "type",                         // Always MESSAGE_TYPE_ALL.
-        "locked"                        // Always 0 (false).
+        "locked",                       // Always 0 (false).
+        "error_code"                    // Always 0
     };
 
     @Override
@@ -243,6 +246,7 @@
         result.add("sms");
         result.add(TextBasedSmsColumns.MESSAGE_TYPE_ALL);
         result.add(0);      // locked
+        result.add(0);      // error_code
         return result;
     }
 
@@ -484,6 +488,21 @@
         }
 
         rowID = db.insert(table, "body", values);
+
+        // Don't use a trigger for updating the words table because of a bug
+        // in FTS3.  The bug is such that the call to get the last inserted
+        // row is incorrect.
+        if (table == TABLE_SMS) {
+            // Update the words table with a corresponding row.  The words table
+            // allows us to search for words quickly, without scanning the whole
+            // table;
+            ContentValues cv = new ContentValues();
+            cv.put(Telephony.MmsSms.WordsTable.ID, rowID);
+            cv.put(Telephony.MmsSms.WordsTable.INDEXED_TEXT, values.getAsString("body"));
+            cv.put(Telephony.MmsSms.WordsTable.SOURCE_ROW_ID, rowID);
+            cv.put(Telephony.MmsSms.WordsTable.TABLE_ID, 1);
+            db.insert(TABLE_WORDS, Telephony.MmsSms.WordsTable.INDEXED_TEXT, cv);
+        }
         if (rowID > 0) {
             Uri uri = Uri.parse("content://" + table + "/" + rowID);