Snap for 8564071 from 682ab534f6f49f8a8b3fcc74ba172e0e2d9de97d to mainline-conscrypt-release

Change-Id: I77f1854609d177a4c9ba6960a0a711d9ce225cde
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..fd0ed55
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,104 @@
+//  Copyright (C) 2015 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+    ],
+}
+
+soong_config_module_type {
+    name: "messaging_java_defaults",
+    module_type: "java_defaults",
+    config_namespace: "messaging",
+    bool_variables: ["build_variant_eng"],
+    properties: ["optimize"],
+}
+
+messaging_java_defaults {
+    name: "messaging_defaults",
+    soong_config_variables: {
+        build_variant_eng: {
+            optimize: {
+                proguard_flags_files: [
+                    "proguard.flags",
+                    "proguard-test.flags",
+                ],
+            },
+            conditions_default: {
+                optimize: {
+                    proguard_flags_files: [
+                        "proguard.flags",
+                        "proguard-release.flags",
+                    ],
+                }
+            },
+        },
+    },
+}
+
+
+android_app {
+    name: "messaging",
+
+    srcs: ["src/**/*.java"],
+
+    defaults: ["messaging_defaults"],
+
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.collection_collection",
+        "androidx.core_core",
+        "androidx.fragment_fragment",
+        "androidx.media_media",
+        "androidx.legacy_legacy-support-core-utils",
+        "androidx.legacy_legacy-support-core-ui",
+        "androidx.palette_palette",
+        "androidx.recyclerview_recyclerview",
+        "androidx.viewpager_viewpager",
+        "androidx.legacy_legacy-support-v13",
+        "colorpicker",
+        "libchips",
+        "libphotoviewer",
+        "androidx.annotation_annotation",
+        "android-common",
+        "android-common-framesequence",
+        "com.android.vcard",
+        "guava",
+        "libphonenumber",
+    ],
+
+    aaptflags: [
+        "--version-name",
+        "1.0.001",
+        "--version-code",
+        "10001040",
+    ],
+    required: [
+        "libframesequence",
+        "libgiftranscode",
+    ],
+    optimize: {
+        obfuscate: true,
+        optimize: true,
+        enabled: true,
+    },
+
+    certificate: "platform",
+
+    sdk_version: "current",
+
+    product_specific: true,
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index b6c2bb5..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,81 +0,0 @@
-#  Copyright (C) 2015 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.
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_USE_AAPT2 := true
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.core_core \
-    androidx.media_media \
-    androidx.legacy_legacy-support-core-utils \
-    androidx.legacy_legacy-support-core-ui \
-    androidx.fragment_fragment \
-    androidx.appcompat_appcompat \
-    androidx.palette_palette \
-    androidx.recyclerview_recyclerview \
-    androidx.legacy_legacy-support-v13 \
-    colorpicker \
-    libchips \
-    libphotoviewer
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.annotation_annotation \
-    android-common \
-    android-common-framesequence \
-    com.android.vcard \
-    guava \
-    libphonenumber
-
-include $(LOCAL_PATH)/version.mk
-
-LOCAL_AAPT_FLAGS += --version-name "$(version_name_package)"
-LOCAL_AAPT_FLAGS += --version-code $(version_code_package)
-
-ifdef TARGET_BUILD_APPS
-    LOCAL_JNI_SHARED_LIBRARIES := libframesequence libgiftranscode
-else
-    LOCAL_REQUIRED_MODULES:= libframesequence libgiftranscode
-endif
-
-LOCAL_PROGUARD_ENABLED := obfuscation optimization
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-ifeq (eng,$(TARGET_BUILD_VARIANT))
-    LOCAL_PROGUARD_FLAG_FILES += proguard-test.flags
-else
-    LOCAL_PROGUARD_FLAG_FILES += proguard-release.flags
-endif
-
-LOCAL_PACKAGE_NAME := messaging
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PRODUCT_MODULE := true
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_APPS)
-
-LOCAL_COMPATIBILITY_SUITE := general-tests
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3468ac3..fffcdab 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -47,6 +47,7 @@
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
     <!--  Optional features -->
     <uses-feature android:name="android.hardware.camera" android:required="false" />
@@ -69,6 +70,7 @@
             android:configChanges="orientation|screenSize|keyboardHidden"
             android:screenOrientation="user"
             android:label="@string/app_name"
+            android:exported="true"
             android:theme="@style/BugleTheme.ConversationListActivity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -90,6 +92,7 @@
             android:screenOrientation="user"
             android:theme="@style/Invisible"
             android:noHistory="true"
+            android:exported="true"
             android:documentLaunchMode="always">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
@@ -98,12 +101,6 @@
                 <category android:name="android.intent.category.BROWSABLE" />
                 <data android:scheme="sms" />
                 <data android:scheme="smsto" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-                <action android:name="android.intent.action.SENDTO" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
                 <data android:scheme="mms" />
                 <data android:scheme="mmsto" />
             </intent-filter>
@@ -191,6 +188,7 @@
             android:parentActivityName="com.android.messaging.ui.appsettings.SettingsActivity">
             <meta-data
                 android:name="android.support.PARENT_ACTIVITY"
+                android:exported="true"
                 android:value="com.android.messaging.ui.appsettings.SettingsActivity" />
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -206,6 +204,7 @@
             android:screenOrientation="user"
             android:theme="@style/BugleTheme.DialogActivity"
             android:excludeFromRecents="true"
+            android:exported="true"
             android:documentLaunchMode="always">
             <intent-filter
                 android:label="@string/share_intent_label">
@@ -336,6 +335,7 @@
         <!-- Registered with the highest possible priority (max_int) -->
         <receiver android:name=".receiver.MmsWapPushReceiver"
                   android:enabled="false"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_WAP_PUSH">
             <intent-filter android:priority="2147483647">
                 <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
@@ -344,6 +344,7 @@
         </receiver>
         <receiver android:name=".receiver.SmsReceiver"
                   android:enabled="false"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_SMS">
             <intent-filter android:priority="2147483647">
                 <action android:name="android.provider.Telephony.SMS_RECEIVED" />
@@ -357,6 +358,7 @@
         <!-- Registered for a priority just ahead of inbox Messaging apps (2) -->
         <receiver android:name=".receiver.AbortMmsWapPushReceiver"
                   android:enabled="false"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_WAP_PUSH">
             <intent-filter android:priority="3">
                 <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
@@ -365,6 +367,7 @@
         </receiver>
         <receiver android:name=".receiver.AbortSmsReceiver"
                   android:enabled="false"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_SMS">
             <intent-filter android:priority="3">
                 <action android:name="android.provider.Telephony.SMS_RECEIVED" />
@@ -373,6 +376,7 @@
 
         <!-- Intents for KLP+ Delivery -->
         <receiver android:name=".receiver.MmsWapPushDeliverReceiver"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_WAP_PUSH">
             <intent-filter>
                 <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
@@ -380,6 +384,7 @@
             </intent-filter>
         </receiver>
         <receiver android:name=".receiver.SmsDeliverReceiver"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_SMS">
             <intent-filter>
                 <action android:name="android.provider.Telephony.SMS_DELIVER" />
@@ -413,6 +418,8 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:scheme="sms" />
                 <data android:scheme="smsto" />
+                <data android:scheme="mms" />
+                <data android:scheme="mmsto" />
             </intent-filter>
         </service>
 
@@ -441,7 +448,8 @@
                   android:theme="@style/BugleTheme"
                   android:parentActivityName="com.android.messaging.ui.appsettings.ApnSettingsActivity"/>
 
-        <receiver android:name=".receiver.StorageStatusReceiver">
+        <receiver android:name=".receiver.StorageStatusReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
             </intent-filter>
@@ -450,11 +458,12 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name=".receiver.BootAndPackageReplacedReceiver">
+        <receiver android:name=".receiver.BootAndPackageReplacedReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
                 <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
-                </intent-filter>
+            </intent-filter>
         </receiver>
 
         <!-- Broadcast receiver that will be notified to reset notifications -->
@@ -472,7 +481,8 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name=".receiver.DefaultSmsSubscriptionChangeReceiver">
+        <receiver android:name=".receiver.DefaultSmsSubscriptionChangeReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"/>
             </intent-filter>
@@ -480,6 +490,7 @@
 
         <!-- Widget that displays the conversation list -->
         <receiver android:name=".widget.BugleWidgetProvider"
+                android:exported="true"
                 android:label="@string/widget_conversation_name">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -490,6 +501,7 @@
 
         <!-- Widget that displays the messages of a single conversation -->
         <receiver android:name=".widget.WidgetConversationProvider"
+                android:exported="true"
                 android:label="@string/widget_conversation_name">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -508,6 +520,7 @@
 
         <activity android:name=".ui.WidgetPickConversationActivity"
             android:theme="@style/BugleTheme"
+                android:exported="true"
             android:label="@string/app_name" >
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
diff --git a/jni/Android.bp b/jni/Android.bp
new file mode 100644
index 0000000..22840ea
--- /dev/null
+++ b/jni/Android.bp
@@ -0,0 +1,40 @@
+//  Copyright (C) 2015 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_shared {
+    name: "libgiftranscode",
+    static_libs: ["libgif"],
+    // Link to Android logging (liblog.so) and dynamic linker (libdl.so) libraries
+    shared_libs: [
+        "liblog",
+        "libdl",
+    ],
+    // for including the jni.h file
+    header_libs: ["jni_headers"],
+    include_dirs: ["external/giflib"],
+    srcs: ["GifTranscoder.cpp"],
+    cflags: [
+        "-Wno-unused-parameter",
+        "-Wno-switch",
+    ],
+    sdk_version: "19",
+    // LLVM libc++
+    stl: "c++_static",
+    product_specific: true,
+}
diff --git a/jni/Android.mk b/jni/Android.mk
deleted file mode 100644
index 6b349a8..0000000
--- a/jni/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-#  Copyright (C) 2015 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_LIBRARIES += libgif
-
-# Link to Android logging (liblog.so) and dynamic linker (libdl.so) libraries
-LOCAL_LDFLAGS := -llog -ldl
-
-LOCAL_C_INCLUDES := \
-	external/giflib
-
-LOCAL_MODULE    := libgiftranscode
-LOCAL_SRC_FILES := GifTranscoder.cpp
-
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wno-switch
-
-LOCAL_SDK_VERSION := 19
-LOCAL_NDK_STL_VARIANT := c++_static # LLVM libc++
-
-LOCAL_PRODUCT_MODULE := true
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/jni/GifTranscoder.cpp b/jni/GifTranscoder.cpp
index 9169f1b..324d315 100644
--- a/jni/GifTranscoder.cpp
+++ b/jni/GifTranscoder.cpp
@@ -173,7 +173,7 @@
                     return false;
                 }
 
-                // Sanity-check the current image position.
+                // Check the current image position.
                 if (gifIn->Image.Left < 0 ||
                         gifIn->Image.Top < 0 ||
                         gifIn->Image.Left + gifIn->Image.Width > gifIn->SWidth ||
diff --git a/proguard-test.flags b/proguard-test.flags
index 353970a..50f69a2 100755
--- a/proguard-test.flags
+++ b/proguard-test.flags
@@ -18,8 +18,6 @@
 -dontobfuscate
 -dontoptimize
 
-
-
 # FLAG(dnotario): Until we rationalize how to handle tests (extensive unit tests will want similar
 # settings to these, but maybe we want to require VisibleForTesting attribute), just expose all
 # non-private methods. This means we cannot run tests on user builds for the moment.
@@ -35,7 +33,7 @@
   !private *;
 }
 
--keep class com.android.messaging.ui.contact.* { *; }
+-keep class com.android.messaging.ui.*.* { *; }
 
 # Keep the classes needed by emma
 -keep class com.vladium.** { *; }
diff --git a/proguard.flags b/proguard.flags
index cb04900..c5bbb42 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -21,7 +21,9 @@
 -keepclassmembers class * {
   @com.google.common.annotations.VisibleForTesting *;
 }
--keep class com.android.messaging.ui.contact.* { *; }
+-keep class com.android.messaging.* { *; }
+-keep class com.android.messaging.*.* { *; }
+-keep class com.android.messaging.*.*.* { *; }
 
 # Keep methods that have the @VisibleForAnimation annotation
 -keep @interface com.android.messaging.annotation.VisibleForAnimation
@@ -39,10 +41,13 @@
 -keep public class * extends androidx.fragment.app.Fragment
 -keep public class com.android.vcard.* { *; }
 
+-keep class androidx.collection.* { *; }
 -keep class androidx.core.* { *; }
 -keep class androidx.core.*.* { *; }
 -keep class androidx.appcompat.* { *; }
 -keep class androidx.appcompat.*.* { *; }
+-keep class androidx.recyclerview.widget.* { *; }
+-keep class androidx.viewpager.widget.* { *; }
 
 # Keep rastermill classes that need to be accessed from native code (JNI)
 -keep class android.support.rastermill.** { *; }
diff --git a/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index e5f8942..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index 11ef458..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index eb948c1..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index b90188e..0000000
--- a/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cab_bg.9.png b/res/drawable-hdpi/cab_bg.9.png
deleted file mode 100644
index 1adfbc6..0000000
--- a/res/drawable-hdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_add_gray.png b/res/drawable-hdpi/ic_add_gray.png
deleted file mode 100644
index dbbd78d..0000000
--- a/res/drawable-hdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_archive_small_light.png b/res/drawable-hdpi/ic_archive_small_light.png
deleted file mode 100644
index 783126c..0000000
--- a/res/drawable-hdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_archive_undo_small_light.png b/res/drawable-hdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index 2c91e44..0000000
--- a/res/drawable-hdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_arrow_back_dark.png b/res/drawable-hdpi/ic_arrow_back_dark.png
deleted file mode 100644
index 4e5dc89..0000000
--- a/res/drawable-hdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_checkmark_small_blue.png b/res/drawable-hdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index d84fd20..0000000
--- a/res/drawable-hdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_color_lens.png b/res/drawable-hdpi/ic_color_lens.png
deleted file mode 100644
index 69a4287..0000000
--- a/res/drawable-hdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_crying_hero.png b/res/drawable-hdpi/ic_crying_hero.png
deleted file mode 100644
index 804b4e6..0000000
--- a/res/drawable-hdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_delete_small_light.png b/res/drawable-hdpi/ic_delete_small_light.png
deleted file mode 100644
index d2ed6d3..0000000
--- a/res/drawable-hdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_dnd_on_light.png b/res/drawable-hdpi/ic_dnd_on_light.png
deleted file mode 100644
index cadf5b5..0000000
--- a/res/drawable-hdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_history_light.png b/res/drawable-hdpi/ic_history_light.png
deleted file mode 100644
index 4b512fe..0000000
--- a/res/drawable-hdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_ime_dark.png b/res/drawable-hdpi/ic_ime_dark.png
deleted file mode 100644
index a539d28..0000000
--- a/res/drawable-hdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_keyboard_arrow_left_light.png b/res/drawable-hdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 50f280e..0000000
--- a/res/drawable-hdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_keyboard_arrow_right_light.png b/res/drawable-hdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 3e31c08..0000000
--- a/res/drawable-hdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_mp_camera_large_light.png b/res/drawable-hdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 721b760..0000000
--- a/res/drawable-hdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_music.png b/res/drawable-hdpi/ic_music.png
new file mode 100644
index 0000000..033b402
--- /dev/null
+++ b/res/drawable-hdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_open_in_new.png b/res/drawable-hdpi/ic_open_in_new.png
deleted file mode 100644
index 06e5f71..0000000
--- a/res/drawable-hdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_person_add_blue.png b/res/drawable-hdpi/ic_person_add_blue.png
deleted file mode 100644
index b7b85bd..0000000
--- a/res/drawable-hdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_photo_library_light.png b/res/drawable-hdpi/ic_photo_library_light.png
index 1f71a62..55b956a 100644
--- a/res/drawable-hdpi/ic_photo_library_light.png
+++ b/res/drawable-hdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_light.png b/res/drawable-hdpi/ic_search_light.png
deleted file mode 100644
index c35418e..0000000
--- a/res/drawable-hdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_sms_failed_light.png b/res/drawable-hdpi/ic_sms_failed_light.png
deleted file mode 100644
index 4973427..0000000
--- a/res/drawable-hdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_tooltip_arrow.png b/res/drawable-hdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 870d502..0000000
--- a/res/drawable-hdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/sym_keyboard_delete_holo.png b/res/drawable-hdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 9e74a64..0000000
--- a/res/drawable-hdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 3e31c08..0000000
--- a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 50f280e..0000000
--- a/res/drawable-ldrtl-hdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-hdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-hdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index f0e1b08..0000000
--- a/res/drawable-ldrtl-hdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 9bd625d..0000000
--- a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 6004f37..0000000
--- a/res/drawable-ldrtl-mdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-mdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-mdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index 346e754..0000000
--- a/res/drawable-ldrtl-mdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 51a1903..0000000
--- a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 464a0bc..0000000
--- a/res/drawable-ldrtl-xhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xhdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-xhdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index a662fc2..0000000
--- a/res/drawable-ldrtl-xhdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 62ca382..0000000
--- a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 09536ed..0000000
--- a/res/drawable-ldrtl-xxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxhdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-xxhdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index 273efd6..0000000
--- a/res/drawable-ldrtl-xxhdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 85d005b..0000000
--- a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 3737fec..0000000
--- a/res/drawable-ldrtl-xxxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl-xxxhdpi/sym_keyboard_delete_holo_dark.png b/res/drawable-ldrtl-xxxhdpi/sym_keyboard_delete_holo_dark.png
deleted file mode 100644
index 9203260..0000000
--- a/res/drawable-ldrtl-xxxhdpi/sym_keyboard_delete_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index 577ab08..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index d8525e4..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index 1b029e1..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index 4fa69f8..0000000
--- a/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cab_bg.9.png b/res/drawable-mdpi/cab_bg.9.png
deleted file mode 100644
index f43a85b..0000000
--- a/res/drawable-mdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_add_gray.png b/res/drawable-mdpi/ic_add_gray.png
deleted file mode 100644
index 806b6e1..0000000
--- a/res/drawable-mdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_archive_small_light.png b/res/drawable-mdpi/ic_archive_small_light.png
deleted file mode 100644
index fa3ffa9..0000000
--- a/res/drawable-mdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_archive_undo_small_light.png b/res/drawable-mdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index 08b2332..0000000
--- a/res/drawable-mdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_dark.png b/res/drawable-mdpi/ic_arrow_back_dark.png
deleted file mode 100644
index 75ae1b1..0000000
--- a/res/drawable-mdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_checkmark_small_blue.png b/res/drawable-mdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index df6ea14..0000000
--- a/res/drawable-mdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_color_lens.png b/res/drawable-mdpi/ic_color_lens.png
deleted file mode 100644
index b33c8b6..0000000
--- a/res/drawable-mdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_crying_hero.png b/res/drawable-mdpi/ic_crying_hero.png
deleted file mode 100644
index d2f5eef..0000000
--- a/res/drawable-mdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_delete_small_light.png b/res/drawable-mdpi/ic_delete_small_light.png
deleted file mode 100644
index 641adc4..0000000
--- a/res/drawable-mdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_dnd_on_light.png b/res/drawable-mdpi/ic_dnd_on_light.png
deleted file mode 100644
index 4936c2f..0000000
--- a/res/drawable-mdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_history_light.png b/res/drawable-mdpi/ic_history_light.png
deleted file mode 100644
index 234b2ce..0000000
--- a/res/drawable-mdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_ime_dark.png b/res/drawable-mdpi/ic_ime_dark.png
deleted file mode 100644
index 8c0a156..0000000
--- a/res/drawable-mdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_keyboard_arrow_left_light.png b/res/drawable-mdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 6004f37..0000000
--- a/res/drawable-mdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_keyboard_arrow_right_light.png b/res/drawable-mdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 9bd625d..0000000
--- a/res/drawable-mdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_mp_camera_large_light.png b/res/drawable-mdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 3ec72f9..0000000
--- a/res/drawable-mdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_music.png b/res/drawable-mdpi/ic_music.png
new file mode 100644
index 0000000..106f912
--- /dev/null
+++ b/res/drawable-mdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_open_in_new.png b/res/drawable-mdpi/ic_open_in_new.png
deleted file mode 100644
index 44ef9d5..0000000
--- a/res/drawable-mdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_person_add_blue.png b/res/drawable-mdpi/ic_person_add_blue.png
deleted file mode 100644
index 0f63871..0000000
--- a/res/drawable-mdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_photo_library_light.png b/res/drawable-mdpi/ic_photo_library_light.png
index 322d486..26c58f3 100644
--- a/res/drawable-mdpi/ic_photo_library_light.png
+++ b/res/drawable-mdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_search_light.png b/res/drawable-mdpi/ic_search_light.png
deleted file mode 100644
index b7e1de0..0000000
--- a/res/drawable-mdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_sms_failed_light.png b/res/drawable-mdpi/ic_sms_failed_light.png
deleted file mode 100644
index 9914e3f..0000000
--- a/res/drawable-mdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_tooltip_arrow.png b/res/drawable-mdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 51824f5..0000000
--- a/res/drawable-mdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/sym_keyboard_delete_holo.png b/res/drawable-mdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 7b3103b..0000000
--- a/res/drawable-mdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index 96eb10e..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index 10986ee..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index 12f9a82..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index e7590b0..0000000
--- a/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cab_bg.9.png b/res/drawable-xhdpi/cab_bg.9.png
deleted file mode 100644
index ba8c446..0000000
--- a/res/drawable-xhdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_add_gray.png b/res/drawable-xhdpi/ic_add_gray.png
deleted file mode 100644
index 8ce295d..0000000
--- a/res/drawable-xhdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_archive_small_light.png b/res/drawable-xhdpi/ic_archive_small_light.png
deleted file mode 100644
index b3e628f..0000000
--- a/res/drawable-xhdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_archive_undo_small_light.png b/res/drawable-xhdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index b7038d8..0000000
--- a/res/drawable-xhdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_dark.png b/res/drawable-xhdpi/ic_arrow_back_dark.png
deleted file mode 100644
index c40b832..0000000
--- a/res/drawable-xhdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_checkmark_small_blue.png b/res/drawable-xhdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index d6354df..0000000
--- a/res/drawable-xhdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_color_lens.png b/res/drawable-xhdpi/ic_color_lens.png
deleted file mode 100644
index 8887429..0000000
--- a/res/drawable-xhdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_crying_hero.png b/res/drawable-xhdpi/ic_crying_hero.png
deleted file mode 100644
index ccc40ab..0000000
--- a/res/drawable-xhdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_delete_small_light.png b/res/drawable-xhdpi/ic_delete_small_light.png
deleted file mode 100644
index 7c45f70..0000000
--- a/res/drawable-xhdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dnd_on_light.png b/res/drawable-xhdpi/ic_dnd_on_light.png
deleted file mode 100644
index e0527f5..0000000
--- a/res/drawable-xhdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_history_light.png b/res/drawable-xhdpi/ic_history_light.png
deleted file mode 100644
index 23585a2..0000000
--- a/res/drawable-xhdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_ime_dark.png b/res/drawable-xhdpi/ic_ime_dark.png
deleted file mode 100644
index cd5c5c6..0000000
--- a/res/drawable-xhdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-xhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 464a0bc..0000000
--- a/res/drawable-xhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-xhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 51a1903..0000000
--- a/res/drawable-xhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_mp_camera_large_light.png b/res/drawable-xhdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 5552d92..0000000
--- a/res/drawable-xhdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_music.png b/res/drawable-xhdpi/ic_music.png
new file mode 100644
index 0000000..1de4c3b
--- /dev/null
+++ b/res/drawable-xhdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_open_in_new.png b/res/drawable-xhdpi/ic_open_in_new.png
deleted file mode 100644
index 363a35d..0000000
--- a/res/drawable-xhdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_person_add_blue.png b/res/drawable-xhdpi/ic_person_add_blue.png
deleted file mode 100644
index 0cdaeee..0000000
--- a/res/drawable-xhdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_photo_library_light.png b/res/drawable-xhdpi/ic_photo_library_light.png
index 1f5a69c..627ff48 100644
--- a/res/drawable-xhdpi/ic_photo_library_light.png
+++ b/res/drawable-xhdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_light.png b/res/drawable-xhdpi/ic_search_light.png
deleted file mode 100644
index b83d895..0000000
--- a/res/drawable-xhdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_sms_failed_light.png b/res/drawable-xhdpi/ic_sms_failed_light.png
deleted file mode 100644
index 90c899a..0000000
--- a/res/drawable-xhdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_tooltip_arrow.png b/res/drawable-xhdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 4a59931..0000000
--- a/res/drawable-xhdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/sym_keyboard_delete_holo.png b/res/drawable-xhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 16b58a0..0000000
--- a/res/drawable-xhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected.9.png b/res/drawable-xhdpi/tab_selected.9.png
deleted file mode 100644
index 95e5f43..0000000
--- a/res/drawable-xhdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_focused_holo.9.png b/res/drawable-xhdpi/tab_selected_focused_holo.9.png
deleted file mode 100644
index f98b4d9..0000000
--- a/res/drawable-xhdpi/tab_selected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png b/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png
deleted file mode 100644
index e8f2cb6..0000000
--- a/res/drawable-xhdpi/tab_selected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xhdpi/tab_selected_pressed_holo.9.png
deleted file mode 100644
index 1882927..0000000
--- a/res/drawable-xhdpi/tab_selected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected.9.png b/res/drawable-xhdpi/tab_unselected.9.png
deleted file mode 100644
index 8cede8d..0000000
--- a/res/drawable-xhdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xhdpi/tab_unselected_focused_holo.9.png
deleted file mode 100644
index 28a7ec5..0000000
--- a/res/drawable-xhdpi/tab_unselected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_pressed_focused_holo.9.png b/res/drawable-xhdpi/tab_unselected_pressed_focused_holo.9.png
deleted file mode 100644
index b9962b1..0000000
--- a/res/drawable-xhdpi/tab_unselected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png
deleted file mode 100644
index dadc471..0000000
--- a/res/drawable-xhdpi/tab_unselected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index cc880c2..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index 60ccf97..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index 0358907..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index adcddc9..0000000
--- a/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cab_bg.9.png b/res/drawable-xxhdpi/cab_bg.9.png
deleted file mode 100644
index b75401e..0000000
--- a/res/drawable-xxhdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_add_gray.png b/res/drawable-xxhdpi/ic_add_gray.png
deleted file mode 100644
index 8273830..0000000
--- a/res/drawable-xxhdpi/ic_add_gray.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_archive_small_light.png b/res/drawable-xxhdpi/ic_archive_small_light.png
deleted file mode 100644
index 042ed27..0000000
--- a/res/drawable-xxhdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_archive_undo_small_light.png b/res/drawable-xxhdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index cd9d0d5..0000000
--- a/res/drawable-xxhdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_dark.png b/res/drawable-xxhdpi/ic_arrow_back_dark.png
deleted file mode 100644
index 1c71cb0..0000000
--- a/res/drawable-xxhdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_checkmark_small_blue.png b/res/drawable-xxhdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index 154c66e..0000000
--- a/res/drawable-xxhdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_color_lens.png b/res/drawable-xxhdpi/ic_color_lens.png
deleted file mode 100644
index 6fd56fd..0000000
--- a/res/drawable-xxhdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_crying_hero.png b/res/drawable-xxhdpi/ic_crying_hero.png
deleted file mode 100644
index 46538b5..0000000
--- a/res/drawable-xxhdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_delete_small_light.png b/res/drawable-xxhdpi/ic_delete_small_light.png
deleted file mode 100644
index b739ac9..0000000
--- a/res/drawable-xxhdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dnd_on_light.png b/res/drawable-xxhdpi/ic_dnd_on_light.png
deleted file mode 100644
index 3ddaa13..0000000
--- a/res/drawable-xxhdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_history_light.png b/res/drawable-xxhdpi/ic_history_light.png
deleted file mode 100644
index bd430f8..0000000
--- a/res/drawable-xxhdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_ime_dark.png b/res/drawable-xxhdpi/ic_ime_dark.png
deleted file mode 100644
index a32bd46..0000000
--- a/res/drawable-xxhdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-xxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 09536ed..0000000
--- a/res/drawable-xxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-xxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 62ca382..0000000
--- a/res/drawable-xxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_mp_camera_large_light.png b/res/drawable-xxhdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index 37fa935..0000000
--- a/res/drawable-xxhdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_music.png b/res/drawable-xxhdpi/ic_music.png
new file mode 100644
index 0000000..f8016cc
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_open_in_new.png b/res/drawable-xxhdpi/ic_open_in_new.png
deleted file mode 100644
index 9109fa4..0000000
--- a/res/drawable-xxhdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_person_add_blue.png b/res/drawable-xxhdpi/ic_person_add_blue.png
deleted file mode 100644
index d9b6a77..0000000
--- a/res/drawable-xxhdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_photo_library_light.png b/res/drawable-xxhdpi/ic_photo_library_light.png
index f61bb58..cd72bdf 100644
--- a/res/drawable-xxhdpi/ic_photo_library_light.png
+++ b/res/drawable-xxhdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_search_light.png b/res/drawable-xxhdpi/ic_search_light.png
deleted file mode 100644
index 97d77d6..0000000
--- a/res/drawable-xxhdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_sms_failed_light.png b/res/drawable-xxhdpi/ic_sms_failed_light.png
deleted file mode 100644
index 5da454b..0000000
--- a/res/drawable-xxhdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_tooltip_arrow.png b/res/drawable-xxhdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 72f1c5b..0000000
--- a/res/drawable-xxhdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/sym_keyboard_delete_holo.png b/res/drawable-xxhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index f2adbec..0000000
--- a/res/drawable-xxhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected.9.png b/res/drawable-xxhdpi/tab_selected.9.png
deleted file mode 100644
index e5efc58..0000000
--- a/res/drawable-xxhdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_focused_holo.9.png b/res/drawable-xxhdpi/tab_selected_focused_holo.9.png
deleted file mode 100644
index 8762b9e..0000000
--- a/res/drawable-xxhdpi/tab_selected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_pressed_focused_holo.9.png b/res/drawable-xxhdpi/tab_selected_pressed_focused_holo.9.png
deleted file mode 100644
index 8faa7ff..0000000
--- a/res/drawable-xxhdpi/tab_selected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png
deleted file mode 100644
index c7a539b..0000000
--- a/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected.9.png b/res/drawable-xxhdpi/tab_unselected.9.png
deleted file mode 100644
index 3891886..0000000
--- a/res/drawable-xxhdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png
deleted file mode 100644
index 462ab15..0000000
--- a/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_focused_holo.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_focused_holo.9.png
deleted file mode 100644
index 58ddbe8..0000000
--- a/res/drawable-xxhdpi/tab_unselected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png
deleted file mode 100644
index de61933..0000000
--- a/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_dark_normal_holo.9.png
deleted file mode 100644
index 65cd55e..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
deleted file mode 100644
index b6cac56..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_light_normal_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_light_normal_holo.9.png
deleted file mode 100644
index f017e03..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/res/drawable-xxxhdpi/btn_keyboard_key_light_pressed_holo.9.png
deleted file mode 100644
index 025e694..0000000
--- a/res/drawable-xxxhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/cab_bg.9.png b/res/drawable-xxxhdpi/cab_bg.9.png
deleted file mode 100644
index 978cfad..0000000
--- a/res/drawable-xxxhdpi/cab_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_archive_small_light.png b/res/drawable-xxxhdpi/ic_archive_small_light.png
deleted file mode 100644
index 59ffef9..0000000
--- a/res/drawable-xxxhdpi/ic_archive_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_archive_undo_small_light.png b/res/drawable-xxxhdpi/ic_archive_undo_small_light.png
deleted file mode 100644
index 46fd7d8..0000000
--- a/res/drawable-xxxhdpi/ic_archive_undo_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_arrow_back_dark.png b/res/drawable-xxxhdpi/ic_arrow_back_dark.png
deleted file mode 100644
index e7a9c81..0000000
--- a/res/drawable-xxxhdpi/ic_arrow_back_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_checkmark_small_blue.png b/res/drawable-xxxhdpi/ic_checkmark_small_blue.png
deleted file mode 100644
index 855cfc5..0000000
--- a/res/drawable-xxxhdpi/ic_checkmark_small_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_color_lens.png b/res/drawable-xxxhdpi/ic_color_lens.png
deleted file mode 100644
index 5c71071..0000000
--- a/res/drawable-xxxhdpi/ic_color_lens.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_crying_hero.png b/res/drawable-xxxhdpi/ic_crying_hero.png
deleted file mode 100644
index 0a428c9..0000000
--- a/res/drawable-xxxhdpi/ic_crying_hero.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_delete_small_light.png b/res/drawable-xxxhdpi/ic_delete_small_light.png
deleted file mode 100644
index d8a661f..0000000
--- a/res/drawable-xxxhdpi/ic_delete_small_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_dnd_on_light.png b/res/drawable-xxxhdpi/ic_dnd_on_light.png
deleted file mode 100644
index 7851985..0000000
--- a/res/drawable-xxxhdpi/ic_dnd_on_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_history_light.png b/res/drawable-xxxhdpi/ic_history_light.png
deleted file mode 100644
index 7ff42b7..0000000
--- a/res/drawable-xxxhdpi/ic_history_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_ime_dark.png b/res/drawable-xxxhdpi/ic_ime_dark.png
deleted file mode 100644
index 7c051df..0000000
--- a/res/drawable-xxxhdpi/ic_ime_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_keyboard_arrow_left_light.png b/res/drawable-xxxhdpi/ic_keyboard_arrow_left_light.png
deleted file mode 100644
index 3737fec..0000000
--- a/res/drawable-xxxhdpi/ic_keyboard_arrow_left_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_keyboard_arrow_right_light.png b/res/drawable-xxxhdpi/ic_keyboard_arrow_right_light.png
deleted file mode 100644
index 85d005b..0000000
--- a/res/drawable-xxxhdpi/ic_keyboard_arrow_right_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_mp_camera_large_light.png b/res/drawable-xxxhdpi/ic_mp_camera_large_light.png
deleted file mode 100644
index f915989..0000000
--- a/res/drawable-xxxhdpi/ic_mp_camera_large_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_music.png b/res/drawable-xxxhdpi/ic_music.png
new file mode 100644
index 0000000..11d67ae
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_music.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_open_in_new.png b/res/drawable-xxxhdpi/ic_open_in_new.png
deleted file mode 100644
index 6d6b59f..0000000
--- a/res/drawable-xxxhdpi/ic_open_in_new.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_person_add_blue.png b/res/drawable-xxxhdpi/ic_person_add_blue.png
deleted file mode 100644
index 13eb372..0000000
--- a/res/drawable-xxxhdpi/ic_person_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_photo_library_light.png b/res/drawable-xxxhdpi/ic_photo_library_light.png
index 8627f42..9fe62f4 100644
--- a/res/drawable-xxxhdpi/ic_photo_library_light.png
+++ b/res/drawable-xxxhdpi/ic_photo_library_light.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_search_light.png b/res/drawable-xxxhdpi/ic_search_light.png
deleted file mode 100644
index b38785a..0000000
--- a/res/drawable-xxxhdpi/ic_search_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_sms_failed_light.png b/res/drawable-xxxhdpi/ic_sms_failed_light.png
deleted file mode 100644
index 2daf2ae..0000000
--- a/res/drawable-xxxhdpi/ic_sms_failed_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_tooltip_arrow.png b/res/drawable-xxxhdpi/ic_tooltip_arrow.png
deleted file mode 100644
index 84b03ad..0000000
--- a/res/drawable-xxxhdpi/ic_tooltip_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/sym_keyboard_delete_holo.png b/res/drawable-xxxhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 09af002..0000000
--- a/res/drawable-xxxhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected.9.png b/res/drawable-xxxhdpi/tab_selected.9.png
deleted file mode 100644
index 5c20d4d..0000000
--- a/res/drawable-xxxhdpi/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected_focused_holo.9.png b/res/drawable-xxxhdpi/tab_selected_focused_holo.9.png
deleted file mode 100644
index 33be972..0000000
--- a/res/drawable-xxxhdpi/tab_selected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected_pressed_focused_holo.9.png b/res/drawable-xxxhdpi/tab_selected_pressed_focused_holo.9.png
deleted file mode 100644
index dc38a52..0000000
--- a/res/drawable-xxxhdpi/tab_selected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xxxhdpi/tab_selected_pressed_holo.9.png
deleted file mode 100644
index e4518a8..0000000
--- a/res/drawable-xxxhdpi/tab_selected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected.9.png b/res/drawable-xxxhdpi/tab_unselected.9.png
deleted file mode 100644
index 9df735b..0000000
--- a/res/drawable-xxxhdpi/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xxxhdpi/tab_unselected_focused_holo.9.png
deleted file mode 100644
index f01cfaf..0000000
--- a/res/drawable-xxxhdpi/tab_unselected_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected_pressed_focused_holo.9.png b/res/drawable-xxxhdpi/tab_unselected_pressed_focused_holo.9.png
deleted file mode 100644
index 7d85b20..0000000
--- a/res/drawable-xxxhdpi/tab_unselected_pressed_focused_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xxxhdpi/tab_unselected_pressed_holo.9.png
deleted file mode 100644
index 0ace170..0000000
--- a/res/drawable-xxxhdpi/tab_unselected_pressed_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/exit_button_bg.xml b/res/drawable/exit_button_bg.xml
deleted file mode 100644
index 9fa0fb2..0000000
--- a/res/drawable/exit_button_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2015 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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="@android:color/white">
-</ripple>
diff --git a/res/drawable/gallery_document_picker_item_background.xml b/res/drawable/gallery_document_picker_item_background.xml
deleted file mode 100644
index 4bbd7fe..0000000
--- a/res/drawable/gallery_document_picker_item_background.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2015 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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <stroke android:color="@color/gallery_image_default_background" android:width="1dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/stat_notify_chat.png b/res/drawable/stat_notify_chat.png
deleted file mode 100644
index cc08e21..0000000
--- a/res/drawable/stat_notify_chat.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/tab_indicator.xml b/res/drawable/tab_indicator.xml
deleted file mode 100644
index 28ecda3..0000000
--- a/res/drawable/tab_indicator.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2015 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Non focused states -->
-    <item
-        android:state_focused="false"
-        android:state_selected="false"
-        android:state_pressed="false"
-        android:drawable="@drawable/tab_unselected" />
-    <item
-        android:state_focused="false"
-        android:state_selected="true"
-        android:state_pressed="false"
-        android:drawable="@drawable/tab_selected" />
-
-    <!-- Focused states -->
-    <item
-        android:state_focused="true"
-        android:state_selected="false"
-        android:state_pressed="false"
-        android:drawable="@drawable/tab_unselected_focused_holo" />
-    <item
-        android:state_focused="true"
-        android:state_selected="true"
-        android:state_pressed="false"
-        android:drawable="@drawable/tab_selected_focused_holo" />
-
-    <!-- Pressed -->
-    <!-- Non focused states -->
-    <item
-        android:state_focused="false"
-        android:state_selected="false"
-        android:state_pressed="true"
-        android:drawable="@drawable/tab_unselected_pressed_holo" />
-    <item
-        android:state_focused="false"
-        android:state_selected="true"
-        android:state_pressed="true"
-        android:drawable="@drawable/tab_selected_pressed_holo" />
-
-    <!-- Focused states -->
-    <item
-        android:state_focused="true"
-        android:state_selected="false"
-        android:state_pressed="true"
-        android:drawable="@drawable/tab_unselected_pressed_focused_holo" />
-    <item
-        android:state_focused="true"
-        android:state_selected="true"
-        android:state_pressed="true"
-        android:drawable="@drawable/tab_selected_pressed_focused_holo" />
-</selector>
diff --git a/res/layout/apn_preference_layout.xml b/res/layout/apn_preference_layout.xml
index 8b864f6..12693d4 100644
--- a/res/layout/apn_preference_layout.xml
+++ b/res/layout/apn_preference_layout.xml
@@ -38,7 +38,7 @@
         android:background="?android:attr/selectableItemBackground">
 
         <TextView
-            android:id="@+id/title"
+            android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:focusable="true"
@@ -46,7 +46,7 @@
             android:textAppearance="?android:attr/textAppearanceListItem" />
 
         <TextView
-            android:id="@+id/summary"
+            android:id="@android:id/summary"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@android:id/title"
diff --git a/res/layout/gallery_grid_item_view.xml b/res/layout/gallery_grid_item_view.xml
index fc857ca..f8f3920 100644
--- a/res/layout/gallery_grid_item_view.xml
+++ b/res/layout/gallery_grid_item_view.xml
@@ -85,6 +85,7 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:focusableInTouchMode="true"
         android:background="@drawable/gallery_image_background_selector" />
 
     <CheckBox
diff --git a/res/layout/mediapicker_contact_chooser.xml b/res/layout/mediapicker_contact_chooser.xml
new file mode 100644
index 0000000..dcace4f
--- /dev/null
+++ b/res/layout/mediapicker_contact_chooser.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/white"
+    android:importantForAccessibility="no" >
+
+    <FrameLayout
+        android:id="@+id/mediapicker_enabled"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <TextView
+            style="@style/ContactPickerHintText"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top|center"
+            android:layout_marginTop="16dp"
+            android:text="@string/contact_picker_hint_text"
+            android:importantForAccessibility="no" />
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:scaleType="center"
+            android:src="@drawable/ic_person_light_large"
+            android:tint="@color/primary_color"
+            android:importantForAccessibility="no"
+            android:contentDescription="@null" />
+    </FrameLayout>
+
+    <!-- This view will hide all other views if the required permission is not granted -->
+    <TextView
+        android:id="@+id/missing_permission_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/enable_permission_procedure"
+        android:contentDescription="@string/enable_permission_procedure_description"
+        android:background="@android:color/white"
+        android:gravity="center"
+        android:visibility="gone" />
+
+</FrameLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 035ed81..0ecd2c2 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -111,6 +111,8 @@
     <color name="audio_attachment_timer_text_color">#323232</color>
     <color name="audio_progress_bar_color">@color/primary_color</color>
 
+    <color name="contact_picker_hint_text_color">#40000000</color>
+
     <color name="notification_sender_text">#9A9A9A</color>
     <color name="notification_secondary_text">#FFFFFF</color>
     <color name="notification_tertiary_text">#FFFFFF</color>
diff --git a/res/values/constants.xml b/res/values/constants.xml
index 8985fd1..e494e95 100644
--- a/res/values/constants.xml
+++ b/res/values/constants.xml
@@ -33,7 +33,7 @@
     <string name="advanced_pref_key" translatable="false">advanced_prefs</string>
 
     <!-- Subscription-specific settings. The values of these pref keys must be prefixed with
-         "buglesub_" to allow for runtime sanity checks -->
+         "buglesub_" to allow for runtime checks -->
     <string name="delivery_reports_pref_key" translatable="false">buglesub_delivery_reports</string>
     <bool name="delivery_reports_pref_default" translatable="false">false</bool>
     <string name="auto_retrieve_mms_pref_key" translatable="false">buglesub_auto_retrieve_mms</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d775c7e..0795ebc 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -100,6 +100,9 @@
     <dimen name="audio_picker_text_size">16sp</dimen>
     <dimen name="audio_attachment_text_size">14sp</dimen>
     <dimen name="audio_progress_bar_height">6dp</dimen>
+
+    <dimen name="contact_picker_text_size">16sp</dimen>
+
     <!-- Videos in the message list view should at least be this big in the smallest dimension -->
     <dimen name="video_message_min_size">320dp</dimen>
     <dimen name="attachment_rounded_corner_radius">3dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 521b1fa..e5c442d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -53,6 +53,7 @@
     <string name="mediapicker_cameraChooserDescription">Capture pictures or video</string>
     <string name="mediapicker_galleryChooserDescription">Choose images from this device</string>
     <string name="mediapicker_audioChooserDescription">Record audio</string>
+    <string name="mediapicker_contactChooserDescription">Choose a contact from this device</string>
     <string name="mediapicker_gallery_title">Choose media</string>
     <string name="mediapicker_gallery_item_selected_content_description">The media is selected.</string>
     <string name="mediapicker_gallery_item_unselected_content_description">The media is unselected.</string>
@@ -61,6 +62,9 @@
     <string name="mediapicker_gallery_image_item_description">image <xliff:g id="date">%1$tB %1$te %1$tY %1$tl %1$tM %1$tp</xliff:g></string>
     <string name="mediapicker_gallery_image_item_description_no_date">image</string>
     <string name="mediapicker_audio_title">Record audio</string>
+    <string name="mediapicker_contact_title">Choose a contact</string>
+    <!-- Hint text on the contact picker -->
+    <string name="contact_picker_hint_text">Click to open contacts list on this device</string>
 
     <string name="action_share">Share</string>
 
@@ -975,4 +979,7 @@
     <string name="selected_sim_content_message"><xliff:g id="selected_sim">%s</xliff:g> selected</string>
 
     <string name="work_directory_display_name">Work Profile contacts</string>
+
+    <!-- When making a call to emergency numbers, show this toast -->
+    <string name="disallow_emergency_call">Can\'t make a voice call to emergency services here.</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 46533af..6f9be47 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -217,6 +217,11 @@
         <item name="android:paddingLeft">16dp</item>
     </style>
 
+    <style name="ContactPickerHintText">
+        <item name="android:textSize">@dimen/contact_picker_text_size</item>
+        <item name="android:textColor">@color/contact_picker_hint_text_color</item>
+    </style>
+
     <style name="VcardAttachmentSingleStyle">
         <item name="android:paddingRight">@dimen/message_text_left_right_padding</item>
         <item name="android:paddingLeft">@dimen/message_text_left_right_padding</item>
diff --git a/res/xml-mcc440-mnc11/mms_config.xml b/res/xml-mcc440-mnc11/mms_config.xml
new file mode 100644
index 0000000..2464a13
--- /dev/null
+++ b/res/xml-mcc440-mnc11/mms_config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2013 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.
+-->
+
+<!-- Version History
+        version 1 - initial version.
+        version 2 - added recipientLimit.
+        version 3 - added min/max recycler values.
+        version 4 - added sms to mms text threshold.
+-->
+
+<!-- Rakuten Mobile J.P. mcc=440, mnc=11 -->
+
+<mms_config version="4">
+    <!-- Maximum message size in bytes for a MMS message -->
+    <int name="maxMessageSize">2097152</int>
+
+    <!-- Maximum height for an attached image -->
+    <int name="maxImageHeight">1944</int>
+
+    <!-- Maximum width for an attached image -->
+    <int name="maxImageWidth">2592</int>
+</mms_config>
\ No newline at end of file
diff --git a/res/xml/apns.xml b/res/xml/apns.xml
index 57e1163..20de298 100644
--- a/res/xml/apns.xml
+++ b/res/xml/apns.xml
@@ -836,10 +836,10 @@
       <apn mcc="310" mnc="120" carrier="Sprint" apn="sprint" mmsc="http://mms.sprintpcs.com" mmsproxy="68.28.31.7" mmsport="80" type="default,supl,mms,ims,cbs,dun" bearer="8"></apn>
       <apn mcc="310" mnc="120" carrier="Sprint CdmaNai" apn="CdmaNai" mmsc="http://mms.sprintpcs.com" mmsproxy="68.28.31.7" mmsport="80" type="mms" authtype="0" protocol="IPV4V6" bearer="6" roaming_protocol="IPV4V6" carrier_enabled="FALSE"></apn>
       <apn mcc="310" mnc="120" carrier="Sprint CdmaNai" apn="CdmaNai" mmsc="http://mms.sprintpcs.com" mmsproxy="68.28.31.7" mmsport="80" type="mms" authtype="0" protocol="IPV4V6" bearer="6" roaming_protocol="IPV4V6" carrier_enabled="TRUE"></apn>
-      <apn mcc="310" mnc="130" carrier="Carolina west" apn="VZWINTERNET" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="204.117.091.161" mmsport="80" type="default,mms,dun,supl" mvno_match_data="carolinawest" mvno_type="spn" protocol="IPV4V6" bearer="13" roaming_protocol="IPV4V6"></apn>
-      <apn mcc="310" mnc="130" carrier="Carolina west" apn="VZWINTERNET" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="204.117.091.161" mmsport="80" type="default,mms,dun,supl" mvno_match_data="carolinawest" mvno_type="spn" protocol="IPV4V6" bearer="14" roaming_protocol="IPV4V6"></apn>
-      <apn mcc="310" mnc="130" carrier="My Multi Media" apn="mms.c1.ama" mmsc="http://mms.iot1.com/amarillo/mms.php" type="mms" user="cell1mms" password="cell1"></apn>
-      <apn mcc="310" mnc="130" carrier="carolinawest" apn="CdmaNai" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="204.117.091.161" mmsport="80" type="mms" mvno_match_data="carolinawest" mvno_type="spn" protocol="IPV4V6" bearer="6" roaming_protocol="IPV4V6" carrier_enabled="false"></apn>
+      <apn mcc="310" mnc="130" carrier="Carolina West Wireless" apn="home.cww.com" mmsc="" mmsproxy="" mmsport="" type="default,supl,fota"></apn>
+      <apn mcc="310" mnc="130" carrier="Carolina West Wireless IMS" apn="ims" mmsc="" mmsproxy="" mmsport="" type="ims"></apn>
+      <apn mcc="310" mnc="130" carrier="Carolina West Wireless MMS" apn="mms" mmsc="http://mms.cwwmms.com/cww/mms.php" mmsproxy="" mmsport="" type="mms"></apn>
+      <apn mcc="310" mnc="130" carrier="Carolina West Wireless Tethering" apn="tethering.cww.com" mmsc="" mmsproxy="" mmsport="80" type="dun"></apn>
       <apn mcc="310" mnc="150" carrier="internet" apn="ndo" mmsc="http://mmsc.aiowireless.net" mmsproxy="proxy.aiowireless.net" mmsport="80" type="default,mms,fota,hipri,supl"></apn>
       <apn mcc="310" mnc="160" carrier="MetroPCS 160" apn="fast.metropcs.com" mmsc="http://metropcs.mmsmvno.com/mms/wapenc" type="default,supl,mms" mvno_match_data="6D" mvno_type="gid" protocol="IPV6" roaming_protocol="IP"></apn>
       <apn mcc="310" mnc="160" carrier="T-Mobile US 160" apn="epc.tmobile.com" mmsc="http://mms.msg.eng.t-mobile.com/mms/wapenc"></apn>
diff --git a/src/android/support/v7/mms/MmsManager.java b/src/android/support/v7/mms/MmsManager.java
index 4a72a32..642a141 100644
--- a/src/android/support/v7/mms/MmsManager.java
+++ b/src/android/support/v7/mms/MmsManager.java
@@ -135,13 +135,13 @@
      */
     public static void sendMultimediaMessage(int subId, Context context, Uri contentUri,
             String locationUrl, PendingIntent sentIntent) {
-        if (Utils.hasMmsApi() && !sForceLegacyMms) {
+        if (shouldUseLegacyMms()) {
+            MmsService.startRequest(context, new SendRequest(locationUrl, contentUri, sentIntent));
+        } else {
             subId = Utils.getEffectiveSubscriptionId(subId);
             final SmsManager smsManager = Utils.getSmsManager(subId);
             smsManager.sendMultimediaMessage(context, contentUri, locationUrl,
                     getConfigOverrides(subId), sentIntent);
-        } else {
-            MmsService.startRequest(context, new SendRequest(locationUrl, contentUri, sentIntent));
         }
     }
 
@@ -157,18 +157,27 @@
      */
     public static void downloadMultimediaMessage(int subId, Context context, String locationUrl,
             Uri contentUri, PendingIntent downloadedIntent) {
-        if (Utils.hasMmsApi() && !sForceLegacyMms) {
+        if (shouldUseLegacyMms()) {
+            MmsService.startRequest(context,
+                    new DownloadRequest(locationUrl, contentUri, downloadedIntent));
+        } else {
             subId = Utils.getEffectiveSubscriptionId(subId);
             final SmsManager smsManager = Utils.getSmsManager(subId);
             smsManager.downloadMultimediaMessage(context, locationUrl, contentUri,
                     getConfigOverrides(subId), downloadedIntent);
-        } else {
-            MmsService.startRequest(context,
-                    new DownloadRequest(locationUrl, contentUri, downloadedIntent));
         }
     }
 
     /**
+     * Checks if we should use legacy APIs for MMS.
+     *
+     * @return true if forced to use legacy APIs or platform doesn't supports MMS APIs.
+     */
+    public static boolean shouldUseLegacyMms() {
+        return sForceLegacyMms || !Utils.hasMmsApi();
+    }
+
+    /**
      * Get carrier configuration values overrides when platform MMS API is called.
      * We only need to compute this if customized carrier config values loader or
      * user agent info loader are set
diff --git a/src/com/android/messaging/datamodel/BugleDatabaseOperations.java b/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
index 281a8dd..c4e5fb8 100644
--- a/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
+++ b/src/com/android/messaging/datamodel/BugleDatabaseOperations.java
@@ -965,7 +965,7 @@
         return participant;
     }
 
-    static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper,
+    public static int getSelfSubscriptionId(final DatabaseWrapper dbWrapper,
             final String selfParticipantId) {
         final ParticipantData selfParticipant = BugleDatabaseOperations.getExistingParticipant(
                 dbWrapper, selfParticipantId);
diff --git a/src/com/android/messaging/datamodel/DataModel.java b/src/com/android/messaging/datamodel/DataModel.java
index 936b51c..b606c72 100644
--- a/src/com/android/messaging/datamodel/DataModel.java
+++ b/src/com/android/messaging/datamodel/DataModel.java
@@ -50,7 +50,6 @@
 import com.android.messaging.datamodel.data.SubscriptionListData;
 import com.android.messaging.datamodel.data.VCardContactItemData;
 import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
-import com.android.messaging.util.ConnectivityUtil;
 
 public abstract class DataModel {
     private String mFocusedConversation;
@@ -152,7 +151,5 @@
 
     public abstract void onApplicationCreated();
 
-    public abstract ConnectivityUtil getConnectivityUtil();
-
     public abstract SyncManager getSyncManager();
 }
diff --git a/src/com/android/messaging/datamodel/DataModelImpl.java b/src/com/android/messaging/datamodel/DataModelImpl.java
index 6ab3f00..ddb17ff 100644
--- a/src/com/android/messaging/datamodel/DataModelImpl.java
+++ b/src/com/android/messaging/datamodel/DataModelImpl.java
@@ -57,21 +57,27 @@
 import com.android.messaging.util.OsUtil;
 import com.android.messaging.util.PhoneUtils;
 
+import java.util.concurrent.ConcurrentHashMap;
+
 public class DataModelImpl extends DataModel {
     private final Context mContext;
     private final ActionService mActionService;
     private final BackgroundWorker mDataModelWorker;
     private final DatabaseHelper mDatabaseHelper;
-    private final ConnectivityUtil mConnectivityUtil;
     private final SyncManager mSyncManager;
 
+    // Cached ConnectivityUtil instance for Pre-N.
+    private static ConnectivityUtil sConnectivityUtilInstanceCachePreN = null;
+    // Cached ConnectivityUtil subId->instance for N and beyond
+    private static final ConcurrentHashMap<Integer, ConnectivityUtil>
+            sConnectivityUtilInstanceCacheN = new ConcurrentHashMap<>();
+
     public DataModelImpl(final Context context) {
         super();
         mContext = context;
         mActionService = new ActionService();
         mDataModelWorker = new BackgroundWorker();
         mDatabaseHelper = DatabaseHelper.getInstance(context);
-        mConnectivityUtil = new ConnectivityUtil(context);
         mSyncManager = new SyncManager();
     }
 
@@ -185,11 +191,6 @@
     }
 
     @Override
-    public ConnectivityUtil getConnectivityUtil() {
-        return mConnectivityUtil;
-    }
-
-    @Override
     public SyncManager getSyncManager() {
         return mSyncManager;
     }
@@ -213,12 +214,19 @@
 
     @Override
     public void onApplicationCreated() {
+        if (OsUtil.isAtLeastN()) {
+            createConnectivityUtilForEachActiveSubscription();
+        } else {
+            sConnectivityUtilInstanceCachePreN = new ConnectivityUtil(mContext);
+        }
+
         FixupMessageStatusOnStartupAction.fixupMessageStatus();
         ProcessPendingMessagesAction.processFirstPendingMessage();
         SyncManager.immediateSync();
 
         if (OsUtil.isAtLeastL_MR1()) {
-            // Start listening for subscription change events for refreshing self participants.
+            // Start listening for subscription change events for refreshing any data associated
+            // with subscriptions.
             PhoneUtils.getDefault().toLMr1().registerOnSubscriptionsChangedListener(
                     new SubscriptionManager.OnSubscriptionsChangedListener() {
                         @Override
@@ -229,8 +237,35 @@
                             // gracefully
                             MmsConfig.loadAsync();
                             ParticipantRefresh.refreshSelfParticipants();
+                            if (OsUtil.isAtLeastN()) {
+                                createConnectivityUtilForEachActiveSubscription();
+                            }
                         }
                     });
         }
     }
+
+    private void createConnectivityUtilForEachActiveSubscription() {
+        PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
+            @Override
+            public void runForSubscription(int subId) {
+                // Create the ConnectivityUtil instance for given subId if absent.
+                if (subId <= ParticipantData.DEFAULT_SELF_SUB_ID) {
+                    subId = PhoneUtils.getDefault().getDefaultSmsSubscriptionId();
+                }
+                if (!sConnectivityUtilInstanceCacheN.containsKey(subId)) {
+                    sConnectivityUtilInstanceCacheN.put(
+                            subId, new ConnectivityUtil(mContext, subId));
+                }
+            }
+        });
+    }
+
+    public static ConnectivityUtil getConnectivityUtil(final int subId) {
+        if (OsUtil.isAtLeastN()) {
+            return sConnectivityUtilInstanceCacheN.get(subId);
+        } else {
+            return sConnectivityUtilInstanceCachePreN;
+        }
+    }
 }
diff --git a/src/com/android/messaging/datamodel/DatabaseHelper.java b/src/com/android/messaging/datamodel/DatabaseHelper.java
index 2025e2c..486973e 100644
--- a/src/com/android/messaging/datamodel/DatabaseHelper.java
+++ b/src/com/android/messaging/datamodel/DatabaseHelper.java
@@ -49,10 +49,13 @@
         return Integer.parseInt(context.getResources().getString(R.string.database_version));
     }
 
-    /** Table containing names of all other tables and views */
-    private static final String MASTER_TABLE = "sqlite_master";
+    /**
+     * Table containing names of all other tables and views.
+     * TODO(rtenneti): Fix the following special SQLLite table name when SQLLite changes.
+     */
+    private static final String PRIMARY_TABLE = "sqlite_master";
     /** Column containing the name of the tables and views */
-    private static final String[] MASTER_COLUMNS = new String[] { "name", };
+    private static final String[] PRIMARY_COLUMNS = new String[] { "name", };
 
     // Table names
     public static final String CONVERSATIONS_TABLE = "conversations";
@@ -603,7 +606,6 @@
      */
     @VisibleForTesting
     static DatabaseHelper getNewInstanceForTest(final Context context) {
-        Assert.isEngBuild();
         Assert.isTrue(BugleApplication.isRunningTests());
         return new DatabaseHelper(context);
     }
@@ -683,7 +685,7 @@
      */
     private static void dropAllTables(final SQLiteDatabase db) {
         final Cursor tableCursor =
-                db.query(MASTER_TABLE, MASTER_COLUMNS, "type='table'", null, null, null, null);
+                db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='table'", null, null, null, null);
         if (tableCursor != null) {
             try {
                 final String dropPrefix = "DROP TABLE IF EXISTS ";
@@ -714,7 +716,7 @@
      */
     private static void dropAllTriggers(final SQLiteDatabase db) {
         final Cursor triggerCursor =
-                db.query(MASTER_TABLE, MASTER_COLUMNS, "type='trigger'", null, null, null, null);
+                db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='trigger'", null, null, null, null);
         if (triggerCursor != null) {
             try {
                 final String dropPrefix = "DROP TRIGGER IF EXISTS ";
@@ -745,7 +747,7 @@
      */
     public static void dropAllViews(final SQLiteDatabase db) {
         final Cursor viewCursor =
-                db.query(MASTER_TABLE, MASTER_COLUMNS, "type='view'", null, null, null, null);
+                db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='view'", null, null, null, null);
         if (viewCursor != null) {
             try {
                 while (viewCursor.moveToNext()) {
@@ -763,7 +765,7 @@
      */
     private static void dropAllIndexes(final SQLiteDatabase db) {
         final Cursor indexCursor =
-                db.query(MASTER_TABLE, MASTER_COLUMNS, "type='index'", null, null, null, null);
+                db.query(PRIMARY_TABLE, PRIMARY_COLUMNS, "type='index'", null, null, null, null);
         if (indexCursor != null) {
             try {
                 final String dropPrefix = "DROP INDEX IF EXISTS ";
diff --git a/src/com/android/messaging/datamodel/MessageNotificationState.java b/src/com/android/messaging/datamodel/MessageNotificationState.java
index 1c66f89..6b858fa 100644
--- a/src/com/android/messaging/datamodel/MessageNotificationState.java
+++ b/src/com/android/messaging/datamodel/MessageNotificationState.java
@@ -100,7 +100,6 @@
     protected CharSequence mContent = null;
     protected Uri mAttachmentUri = null;
     protected String mAttachmentType = null;
-    protected boolean mTickerNoContent;
 
     @Override
     protected Uri getAttachmentUri() {
@@ -1080,8 +1079,10 @@
         }
         if (state != null && LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
             LogUtil.v(TAG, "MessageNotificationState: Notification state created"
-                    + ", title = " + LogUtil.sanitizePII(state.mTitle)
-                    + ", content = " + LogUtil.sanitizePII(state.mContent.toString()));
+                    + ", title = "
+                    + (state.mTickerSender != null ? state.mTickerSender : state.mTitle)
+                    + ", content = "
+                    + (state.mTickerText != null ? state.mTickerText : state.mContent));
         }
         return state;
     }
@@ -1121,8 +1122,9 @@
     protected CharSequence getTicker() {
         return BugleNotifications.buildColonSeparatedMessage(
                 mTickerSender != null ? mTickerSender : mTitle,
-                mTickerText != null ? mTickerText : (mTickerNoContent ? null : mContent), null,
-                        null);
+                mTickerText != null ? mTickerText : mContent,
+                null,
+                null);
     }
 
     private static CharSequence convertHtmlAndStripUrls(final String s) {
diff --git a/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java b/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java
index b5d2185..bea4961 100644
--- a/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java
+++ b/src/com/android/messaging/datamodel/NoConfirmationSmsSendService.java
@@ -28,10 +28,10 @@
 import com.android.messaging.datamodel.action.UpdateMessageNotificationAction;
 import com.android.messaging.datamodel.data.MessageData;
 import com.android.messaging.datamodel.data.ParticipantData;
-import com.android.messaging.sms.MmsUtils;
 import com.android.messaging.ui.UIIntents;
 import com.android.messaging.ui.conversationlist.ConversationListActivity;
 import com.android.messaging.util.LogUtil;
+import com.android.messaging.util.UriUtil;
 
 /**
  * Respond to a special intent and send an SMS message without the user's intervention, unless
@@ -83,8 +83,8 @@
         final String subject = getText(intent, Intent.EXTRA_SUBJECT);
         final int subId = extras.getInt(EXTRA_SUBSCRIPTION, ParticipantData.DEFAULT_SELF_SUB_ID);
 
-        final Uri intentUri = intent.getData();
-        final String recipients = intentUri != null ? MmsUtils.getSmsRecipients(intentUri) : null;
+        // Get a comma-separated list of recipients
+        final String recipients = UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
 
         if (TextUtils.isEmpty(recipients) && TextUtils.isEmpty(conversationId)) {
             if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
diff --git a/src/com/android/messaging/datamodel/action/ActionServiceImpl.java b/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
index 6725dde..d78bdc0 100644
--- a/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
+++ b/src/com/android/messaging/datamodel/action/ActionServiceImpl.java
@@ -28,7 +28,6 @@
 
 import com.android.messaging.Factory;
 import com.android.messaging.datamodel.DataModel;
-import com.android.messaging.util.ConnectivityUtil;
 import com.android.messaging.util.LogUtil;
 import com.android.messaging.util.LoggingTimer;
 import com.google.common.annotations.VisibleForTesting;
@@ -134,7 +133,6 @@
     protected static final String BUNDLE_ACTION = "bundle_action";
 
     private BackgroundWorker mBackgroundWorker;
-    private ConnectivityUtil mConnectivityUtil;
 
     /**
      * Allocate an intent with a specific opcode.
@@ -213,14 +211,11 @@
     public void onCreate() {
         super.onCreate();
         mBackgroundWorker = DataModel.get().getBackgroundWorkerForActionService();
-        mConnectivityUtil = DataModel.get().getConnectivityUtil();
-        mConnectivityUtil.registerForSignalStrength();
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mConnectivityUtil.unregisterForSignalStrength();
     }
 
     /**
diff --git a/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java b/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
index e3d131d..45e2474 100644
--- a/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
+++ b/src/com/android/messaging/datamodel/action/FixupMessageStatusOnStartupAction.java
@@ -60,13 +60,12 @@
             final ContentValues values = new ContentValues();
             values.put(DatabaseHelper.MessageColumns.STATUS,
                     MessageData.BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED);
-            downloadFailedCnt += db.update(DatabaseHelper.MESSAGES_TABLE, values,
+            downloadFailedCnt = db.update(DatabaseHelper.MESSAGES_TABLE, values,
                     DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)",
                     new String[]{
                             Integer.toString(MessageData.BUGLE_STATUS_INCOMING_AUTO_DOWNLOADING),
                             Integer.toString(MessageData.BUGLE_STATUS_INCOMING_MANUAL_DOWNLOADING)
                     });
-            values.clear();
 
             values.clear();
             values.put(DatabaseHelper.MessageColumns.STATUS,
diff --git a/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java b/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
index 2567ca9..8bf6d5e 100644
--- a/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/InsertNewMessageAction.java
@@ -118,7 +118,6 @@
      */
     @Override
     protected Object executeAction() {
-        LogUtil.i(TAG, "InsertNewMessageAction: inserting new message");
         MessageData message = actionParameters.getParcelable(KEY_MESSAGE);
         if (message == null) {
             LogUtil.i(TAG, "InsertNewMessageAction: Creating MessageData with provided data");
@@ -151,6 +150,8 @@
             return null;
         }
         final int subId = self.getSubId();
+        LogUtil.i(TAG, "InsertNewMessageAction: inserting new message for subId " + subId);
+        actionParameters.putInt(KEY_SUB_ID, subId);
 
         // TODO: Work out whether to send with SMS or MMS (taking into account recipients)?
         final boolean isSms = (message.getProtocol() == MessageData.PROTOCOL_SMS);
diff --git a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java
index 07e2cfb..60d3e35 100644
--- a/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessDownloadedMmsAction.java
@@ -83,7 +83,7 @@
     // Set when message downloaded by us (legacy)
     private static final String KEY_STATUS = "status";
     private static final String KEY_RAW_STATUS = "raw_status";
-    private static final String KEY_MMS_URI =  "mms_uri";
+    private static final String KEY_MMS_URI = "mms_uri";
 
     // Used to send a deferred response in response to auto-download failure
     private static final String KEY_SEND_DEFERRED_RESP_STATUS = "send_deferred_resp_status";
@@ -329,6 +329,8 @@
         if (response == null) {
             // No message download to process; doBackgroundWork sent a notify deferred response
             Assert.isTrue(actionParameters.getBoolean(KEY_SEND_DEFERRED_RESP_STATUS));
+            ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(
+                    true /* failed */, this);
             return null;
         }
 
@@ -343,7 +345,9 @@
 
         final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
         // If we were trying to auto-download but have failed need to send the deferred response
-        if (autoDownload && message == null && status == MmsUtils.MMS_REQUEST_MANUAL_RETRY) {
+        final boolean needToSendDeferredResp =
+                autoDownload && (status == MmsUtils.MMS_REQUEST_MANUAL_RETRY);
+        if (needToSendDeferredResp) {
             final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID);
             final String contentLocation = actionParameters.getString(KEY_CONTENT_LOCATION);
             sendDeferredRespStatus(messageId, transactionId, contentLocation, subId);
@@ -373,7 +377,11 @@
         }
 
         final boolean failed = (messageUri == null);
-        ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(failed, this);
+        // Scheduling pending messages. If auto downloading is failed and it needs to send the
+        // deferred response, Skip it here and it will be scheduled after sending the response.
+        if (!needToSendDeferredResp) {
+            ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(failed, this);
+        }
         if (failed) {
             BugleNotifications.update(false, BugleNotifications.UPDATE_ERRORS);
         }
diff --git a/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java b/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
index ecbec10..a897ce0 100644
--- a/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessPendingMessagesAction.java
@@ -17,76 +17,83 @@
 package com.android.messaging.datamodel.action;
 
 import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
 import android.database.Cursor;
-import android.net.ConnectivityManager;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
 
 import com.android.messaging.Factory;
 import com.android.messaging.datamodel.BugleDatabaseOperations;
 import com.android.messaging.datamodel.DataModel;
+import com.android.messaging.datamodel.DataModelImpl;
 import com.android.messaging.datamodel.DatabaseHelper;
 import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
-import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
 import com.android.messaging.datamodel.DatabaseWrapper;
 import com.android.messaging.datamodel.MessagingContentProvider;
 import com.android.messaging.datamodel.data.MessageData;
 import com.android.messaging.datamodel.data.ParticipantData;
-import com.android.messaging.sms.MmsUtils;
 import com.android.messaging.util.BugleGservices;
 import com.android.messaging.util.BugleGservicesKeys;
 import com.android.messaging.util.BuglePrefs;
 import com.android.messaging.util.BuglePrefsKeys;
+import com.android.messaging.util.ConnectivityUtil;
 import com.android.messaging.util.ConnectivityUtil.ConnectivityListener;
 import com.android.messaging.util.LogUtil;
 import com.android.messaging.util.OsUtil;
 import com.android.messaging.util.PhoneUtils;
 
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 /**
  * Action used to lookup any messages in the pending send/download state and either fail them or
- * retry their action. This action only initiates one retry at a time - further retries should be
- * triggered by successful sending of a message, network status change or exponential backoff timer.
+ * retry their action based on subscriptions. This action only initiates one retry at a time for
+ * both sending/downloading. Further retries should be triggered by successful sending/downloading
+ * of a message, network status change or exponential backoff timer.
  */
 public class ProcessPendingMessagesAction extends Action implements Parcelable {
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
-    private static final int PENDING_INTENT_REQUEST_CODE = 101;
+    // PENDING_INTENT_BASE_REQUEST_CODE + subId(-1 for pre-L_MR1) is used per subscription uniquely.
+    private static final int PENDING_INTENT_BASE_REQUEST_CODE = 103;
+
+    private static final String KEY_SUB_ID = "sub_id";
 
     public static void processFirstPendingMessage() {
-        // Clear any pending alarms or connectivity events
-        unregister();
-        // Clear retry count
-        setRetry(0);
-
-        // Start action
-        final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
-        action.start();
+        PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() {
+            @Override
+            public void runForSubscription(final int subId) {
+                // Clear any pending alarms or connectivity events
+                unregister(subId);
+                // Clear retry count
+                setRetry(0, subId);
+                // Start action
+                final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
+                action.actionParameters.putInt(KEY_SUB_ID, subId);
+                action.start();
+            }
+        });
     }
 
     public static void scheduleProcessPendingMessagesAction(final boolean failed,
             final Action processingAction) {
+        final int subId = processingAction.actionParameters
+                .getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
         LogUtil.i(TAG, "ProcessPendingMessagesAction: Scheduling pending messages"
-                + (failed ? "(message failed)" : ""));
+                + (failed ? "(message failed)" : "") + " for subId " + subId);
         // Can safely clear any pending alarms or connectivity events as either an action
         // is currently running or we will run now or register if pending actions possible.
-        unregister();
+        unregister(subId);
 
         final boolean isDefaultSmsApp = PhoneUtils.getDefault().isDefaultSmsApp();
         boolean scheduleAlarm = false;
         // If message succeeded and if Bugle is default SMS app just carry on with next message
         if (!failed && isDefaultSmsApp) {
             // Clear retry attempt count as something just succeeded
-            setRetry(0);
+            setRetry(0, subId);
 
-            // Lookup and queue next message for immediate processing by background worker
-            //  iff there are no pending messages this will do nothing and return true.
+            // Lookup and queue next message for each sending/downloading for immediate processing
+            // by background worker. If there are no pending messages, this will do nothing and
+            // return true.
             final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
             if (action.queueActions(processingAction)) {
                 if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
@@ -103,66 +110,53 @@
             scheduleAlarm = true;
             LogUtil.w(TAG, "ProcessPendingMessagesAction: Action failed to queue; retrying");
         }
-        if (getHavePendingMessages() || scheduleAlarm) {
+        if (getHavePendingMessages(subId) || scheduleAlarm) {
             // Still have a pending message that needs to be queued for processing
             final ConnectivityListener listener = new ConnectivityListener() {
                 @Override
-                public void onConnectivityStateChanged(final Context context, final Intent intent) {
-                    final int networkType =
-                            MmsUtils.getConnectivityEventNetworkType(context, intent);
-                    if (networkType != ConnectivityManager.TYPE_MOBILE) {
-                        return;
-                    }
-                    final boolean isConnected = !intent.getBooleanExtra(
-                            ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
-                    // TODO: Should we check in more detail?
-                    if (isConnected) {
-                        onConnected();
-                    }
-                }
-
-                @Override
-                public void onPhoneStateChanged(final Context context, final int serviceState) {
+                public void onPhoneStateChanged(final int serviceState) {
                     if (serviceState == ServiceState.STATE_IN_SERVICE) {
-                        onConnected();
+                        LogUtil.i(TAG, "ProcessPendingMessagesAction: Now connected for subId "
+                                + subId + ", starting action");
+
+                        // Clear any pending alarms or connectivity events but leave attempt count
+                        // alone
+                        unregister(subId);
+
+                        // Start action
+                        final ProcessPendingMessagesAction action =
+                                new ProcessPendingMessagesAction();
+                        action.actionParameters.putInt(KEY_SUB_ID, subId);
+                        action.start();
                     }
                 }
-
-                private void onConnected() {
-                    LogUtil.i(TAG, "ProcessPendingMessagesAction: Now connected; starting action");
-
-                    // Clear any pending alarms or connectivity events but leave attempt count alone
-                    unregister();
-
-                    // Start action
-                    final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
-                    action.start();
-                }
             };
             // Read and increment attempt number from shared prefs
-            final int retryAttempt = getNextRetry();
-            register(listener, retryAttempt);
+            final int retryAttempt = getNextRetry(subId);
+            register(listener, retryAttempt, subId);
         } else {
             // No more pending messages (presumably the message that failed has expired) or it
             // may be possible that a send and a download are already in process.
             // Clear retry attempt count.
             // TODO Might be premature if send and download in process...
-            //  but worst case means we try to send a bit more often.
-            setRetry(0);
-
-            if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
-                LogUtil.v(TAG, "ProcessPendingMessagesAction: No more pending messages");
-            }
+            // but worst case means we try to send a bit more often.
+            setRetry(0, subId);
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: No more pending messages");
         }
     }
 
-    private static void register(final ConnectivityListener listener, final int retryAttempt) {
+    private static void register(final ConnectivityListener listener, final int retryAttempt,
+            int subId) {
         int retryNumber = retryAttempt;
 
         // Register to be notified about connectivity changes
-        DataModel.get().getConnectivityUtil().register(listener);
+        ConnectivityUtil connectivityUtil = DataModelImpl.getConnectivityUtil(subId);
+        if (connectivityUtil != null) {
+            connectivityUtil.register(listener);
+        }
 
         final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
+        action.actionParameters.putInt(KEY_SUB_ID, subId);
         final long initialBackoffMs = BugleGservices.get().getLong(
                 BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS,
                 BugleGservicesKeys.INITIAL_MESSAGE_RESEND_DELAY_MS_DEFAULT);
@@ -179,31 +173,34 @@
         while (retryNumber > 0 && nextDelayMs < maxDelayMs);
 
         LogUtil.i(TAG, "ProcessPendingMessagesAction: Registering for retry #" + retryAttempt
-                + " in " + delayMs + " ms");
+                + " in " + delayMs + " ms for subId " + subId);
 
-        action.schedule(PENDING_INTENT_REQUEST_CODE, delayMs);
+        action.schedule(PENDING_INTENT_BASE_REQUEST_CODE + subId, delayMs);
     }
 
-    private static void unregister() {
+    private static void unregister(final int subId) {
         // Clear any pending alarms or connectivity events
-        DataModel.get().getConnectivityUtil().unregister();
+        ConnectivityUtil connectivityUtil = DataModelImpl.getConnectivityUtil(subId);
+        if (connectivityUtil != null) {
+            connectivityUtil.unregister();
+        }
 
         final ProcessPendingMessagesAction action = new ProcessPendingMessagesAction();
-        action.schedule(PENDING_INTENT_REQUEST_CODE, Long.MAX_VALUE);
+        action.schedule(PENDING_INTENT_BASE_REQUEST_CODE + subId, Long.MAX_VALUE);
 
         if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
             LogUtil.v(TAG, "ProcessPendingMessagesAction: Unregistering for connectivity changed "
-                    + "events and clearing scheduled alarm");
+                    + "events and clearing scheduled alarm for subId " + subId);
         }
     }
 
-    private static void setRetry(final int retryAttempt) {
-        final BuglePrefs prefs = Factory.get().getApplicationPrefs();
+    private static void setRetry(final int retryAttempt, int subId) {
+        final BuglePrefs prefs = Factory.get().getSubscriptionPrefs(subId);
         prefs.putInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, retryAttempt);
     }
 
-    private static int getNextRetry() {
-        final BuglePrefs prefs = Factory.get().getApplicationPrefs();
+    private static int getNextRetry(int subId) {
+        final BuglePrefs prefs = Factory.get().getSubscriptionPrefs(subId);
         final int retryAttempt =
                 prefs.getInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, 0) + 1;
         prefs.putInt(BuglePrefsKeys.PROCESS_PENDING_MESSAGES_RETRY_COUNT, retryAttempt);
@@ -215,21 +212,27 @@
 
     /**
      * Read from the DB and determine if there are any messages we should process
+     *
+     * @param subId the subId
      * @return true if we have pending messages
      */
-    private static boolean getHavePendingMessages() {
+    private static boolean getHavePendingMessages(final int subId) {
         final DatabaseWrapper db = DataModel.get().getDatabase();
         final long now = System.currentTimeMillis();
+        final String selfId = ParticipantData.getParticipantId(db, subId);
+        if (selfId == null) {
+            // This could be happened before refreshing participant.
+            LogUtil.w(TAG, "ProcessPendingMessagesAction: selfId is null for subId " + subId);
+            return false;
+        }
 
-        for (int subId : getActiveSubscriptionIds()) {
-            final String toSendMessageId = findNextMessageToSend(db, now, subId);
-            if (toSendMessageId != null) {
+        final String toSendMessageId = findNextMessageToSend(db, now, selfId);
+        if (toSendMessageId != null) {
+            return true;
+        } else {
+            final String toDownloadMessageId = findNextMessageToDownload(db, now, selfId);
+            if (toDownloadMessageId != null) {
                 return true;
-            } else {
-                final String toDownloadMessageId = findNextMessageToDownload(db, now, subId);
-                if (toDownloadMessageId != null) {
-                    return true;
-                }
             }
         }
         // Messages may be in the process of sending/downloading even when there are no pending
@@ -237,190 +240,148 @@
         return false;
     }
 
-    private static int[] getActiveSubscriptionIds() {
-        if (!OsUtil.isAtLeastL_MR1()) {
-            return new int[] { ParticipantData.DEFAULT_SELF_SUB_ID };
-        }
-        List<SubscriptionInfo> subscriptions = PhoneUtils.getDefault().toLMr1()
-            .getActiveSubscriptionInfoList();
-
-        int numSubs = subscriptions.size();
-        int[] result = new int[numSubs];
-        for (int i = 0; i < numSubs; i++) {
-            result[i] = subscriptions.get(i).getSubscriptionId();
-        }
-        return result;
-    }
-
     /**
      * Queue any pending actions
+     *
      * @param actionState
      * @return true if action queued (or no actions to queue) else false
      */
     private boolean queueActions(final Action processingAction) {
         final DatabaseWrapper db = DataModel.get().getDatabase();
         final long now = System.currentTimeMillis();
-        boolean succeeded = false;
+        boolean succeeded = true;
+        final int subId = processingAction.actionParameters
+                .getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
 
-        // Will queue no more than one message per subscription to send plus one message to download
+        LogUtil.i(TAG, "ProcessPendingMessagesAction: Start queueing for subId " + subId);
+
+        final String selfId = ParticipantData.getParticipantId(db, subId);
+        if (selfId == null) {
+            // This could be happened before refreshing participant.
+            LogUtil.w(TAG, "ProcessPendingMessagesAction: selfId is null");
+            return false;
+        }
+
+        // Will queue no more than one message to send plus one message to download
         // This keeps outgoing messages "in order" but allow downloads to happen even if sending
-        //  gets blocked until messages time out.  Manual resend bumps messages to head of queue.
-        for (int subId : getActiveSubscriptionIds()) {
-            final String toSendMessageId = findNextMessageToSend(db, now, subId);
-            final String toDownloadMessageId = findNextMessageToDownload(db, now, subId);
-            if (toSendMessageId != null) {
-                LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toSendMessageId
-                        + " for sending");
-                // This could queue nothing
-                if (!SendMessageAction.queueForSendInBackground(toSendMessageId,
-                            processingAction)) {
-                    LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
-                            + toSendMessageId + " for sending");
-                } else {
-                    succeeded = true;
-                }
+        // gets blocked until messages time out. Manual resend bumps messages to head of queue.
+        final String toSendMessageId = findNextMessageToSend(db, now, selfId);
+        final String toDownloadMessageId = findNextMessageToDownload(db, now, selfId);
+        if (toSendMessageId != null) {
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toSendMessageId
+                    + " for sending");
+            // This could queue nothing
+            if (!SendMessageAction.queueForSendInBackground(toSendMessageId, processingAction)) {
+                LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
+                        + toSendMessageId + " for sending");
+                succeeded = false;
             }
-            if (toDownloadMessageId != null) {
-                LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message "
+        }
+        if (toDownloadMessageId != null) {
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: Queueing message " + toDownloadMessageId
+                    + " for download");
+            // This could queue nothing
+            if (!DownloadMmsAction.queueMmsForDownloadInBackground(toDownloadMessageId,
+                    processingAction)) {
+                LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
                         + toDownloadMessageId + " for download");
-                // This could queue nothing
-                if (!DownloadMmsAction.queueMmsForDownloadInBackground(toDownloadMessageId,
-                            processingAction)) {
-                    LogUtil.w(TAG, "ProcessPendingMessagesAction: Failed to queue message "
-                            + toDownloadMessageId + " for download");
-                } else {
-                    succeeded = true;
-                }
-
+                succeeded = false;
             }
-            if (toSendMessageId == null && toDownloadMessageId == null) {
-                if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
-                    LogUtil.d(TAG, "ProcessPendingMessagesAction: No messages to send or download");
-                }
-                succeeded = true;
-            }
+        }
+        if (toSendMessageId == null && toDownloadMessageId == null) {
+            LogUtil.i(TAG, "ProcessPendingMessagesAction: No messages to send or download");
         }
         return succeeded;
     }
 
     @Override
     protected Object executeAction() {
+        final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
         // If triggered by alarm will not have unregistered yet
-        unregister();
+        unregister(subId);
 
         if (PhoneUtils.getDefault().isDefaultSmsApp()) {
-            queueActions(this);
+            if (!queueActions(this)) {
+                LogUtil.v(TAG, "ProcessPendingMessagesAction: rescheduling");
+                // TODO: Need to clear retry count here?
+                scheduleProcessPendingMessagesAction(true /* failed */, this);
+            }
         } else {
             if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
                 LogUtil.v(TAG, "ProcessPendingMessagesAction: Not default SMS app; rescheduling");
             }
-            scheduleProcessPendingMessagesAction(true, this);
+            scheduleProcessPendingMessagesAction(true /* failed */, this);
         }
 
         return null;
     }
 
-    private static String prefixColumnWithTable(final  String tableName, final String column) {
-        return tableName + "." + column;
-    }
-
-    private static String[] prefixProjectionWithTable(final String tableName,
-            final String[] projection) {
-        String[] result = new String[projection.length];
-        for (int i = 0; i < projection.length; i++) {
-            result[i] = prefixColumnWithTable(tableName, projection[i]);
-        }
-        return result;
-    }
-
     private static String findNextMessageToSend(final DatabaseWrapper db, final long now,
-            final int subId) {
+            final String selfId) {
         String toSendMessageId = null;
-        db.beginTransaction();
-        Cursor sending = null;
         Cursor cursor = null;
         int sendingCnt = 0;
         int pendingCnt = 0;
         int failedCnt = 0;
+        db.beginTransaction();
         try {
-            String[] projection = prefixProjectionWithTable(DatabaseHelper.MESSAGES_TABLE,
-                    MessageData.getProjection());
-            String subIdClause =
-                    prefixColumnWithTable(DatabaseHelper.MESSAGES_TABLE,
-                            MessageColumns.SELF_PARTICIPANT_ID)
-                    + " = "
-                    + prefixColumnWithTable(DatabaseHelper.PARTICIPANTS_TABLE,
-                            ParticipantColumns._ID)
-                    + " AND " + ParticipantColumns.SUB_ID + " =?";
-
             // First check to see if we have any messages already sending
-            sending = db.query(DatabaseHelper.MESSAGES_TABLE + ","
-                            + DatabaseHelper.PARTICIPANTS_TABLE,
-                            projection,
-                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)"
-                            + " AND " + subIdClause,
-                    new String[]{Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_SENDING),
-                           Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_RESENDING),
-                           Integer.toString(subId)},
-                    null,
-                    null,
-                    DatabaseHelper.MessageColumns.RECEIVED_TIMESTAMP + " ASC");
-            final boolean messageCurrentlySending = sending.moveToNext();
-            sendingCnt = sending.getCount();
+            sendingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE,
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
+                    new String[] {
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_SENDING),
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_RESENDING),
+                        selfId}
+                    );
+
             // Look for messages we could send
-            final ContentValues values = new ContentValues();
-            values.put(DatabaseHelper.MessageColumns.STATUS,
-                    MessageData.BUGLE_STATUS_OUTGOING_FAILED);
-            cursor = db.query(DatabaseHelper.MESSAGES_TABLE + ","
-                            + DatabaseHelper.PARTICIPANTS_TABLE,
-                    projection,
-                    DatabaseHelper.MessageColumns.STATUS + " IN ("
-                            + MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND + ","
-                            + MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY + ")"
-                            + " AND " + subIdClause,
-                    new String[]{Integer.toString(subId)},
+            cursor = db.query(DatabaseHelper.MESSAGES_TABLE,
+                    MessageData.getProjection(),
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
+                    new String[] {
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND),
+                        Integer.toString(MessageData.BUGLE_STATUS_OUTGOING_AWAITING_RETRY),
+                        selfId
+                    },
                     null,
                     null,
                     DatabaseHelper.MessageColumns.RECEIVED_TIMESTAMP + " ASC");
             pendingCnt = cursor.getCount();
 
+            final ContentValues values = new ContentValues();
+            values.put(DatabaseHelper.MessageColumns.STATUS,
+                    MessageData.BUGLE_STATUS_OUTGOING_FAILED);
+
+            // Prior to L_MR1, isActiveSubscription is true always
+            boolean isActiveSubscription = true;
+            if (OsUtil.isAtLeastL_MR1()) {
+                final ParticipantData messageSelf =
+                        BugleDatabaseOperations.getExistingParticipant(db, selfId);
+                if (messageSelf == null || !messageSelf.isActiveSubscription()) {
+                    isActiveSubscription = false;
+                }
+            }
             while (cursor.moveToNext()) {
                 final MessageData message = new MessageData();
                 message.bind(cursor);
-                if (message.getInResendWindow(now)) {
-                    // If no messages currently sending
-                    if (!messageCurrentlySending) {
-                        // Resend this message
-                        toSendMessageId = message.getMessageId();
-                        // Before queuing the message for resending, check if the message's self is
-                        // active. If not, switch back to the system's default subscription.
-                        if (OsUtil.isAtLeastL_MR1()) {
-                            final ParticipantData messageSelf = BugleDatabaseOperations
-                                    .getExistingParticipant(db, message.getSelfId());
-                            if (messageSelf == null || !messageSelf.isActiveSubscription()) {
-                                final ParticipantData defaultSelf = BugleDatabaseOperations
-                                        .getOrCreateSelf(db, PhoneUtils.getDefault()
-                                                .getDefaultSmsSubscriptionId());
-                                if (defaultSelf != null) {
-                                    message.bindSelfId(defaultSelf.getId());
-                                    final ContentValues selfValues = new ContentValues();
-                                    selfValues.put(MessageColumns.SELF_PARTICIPANT_ID,
-                                            defaultSelf.getId());
-                                    BugleDatabaseOperations.updateMessageRow(db,
-                                            message.getMessageId(), selfValues);
-                                    MessagingContentProvider.notifyMessagesChanged(
-                                            message.getConversationId());
-                                }
-                            }
-                        }
-                    }
-                    break;
-                } else {
+
+                // Mark this message as failed if the message's self is inactive or the message is
+                // outside of resend window
+                if (!isActiveSubscription || !message.getInResendWindow(now)) {
                     failedCnt++;
 
                     // Mark message as failed
                     BugleDatabaseOperations.updateMessageRow(db, message.getMessageId(), values);
                     MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
+                } else {
+                    // If no messages currently sending
+                    if (sendingCnt == 0) {
+                        // Send this message
+                        toSendMessageId = message.getMessageId();
+                    }
+                    break;
                 }
             }
             db.setTransactionSuccessful();
@@ -429,9 +390,6 @@
             if (cursor != null) {
                 cursor.close();
             }
-            if (sending != null) {
-                sending.close();
-            }
         }
 
         if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
@@ -445,48 +403,33 @@
     }
 
     private static String findNextMessageToDownload(final DatabaseWrapper db, final long now,
-            final int subId) {
+            final String selfId) {
         String toDownloadMessageId = null;
-        db.beginTransaction();
         Cursor cursor = null;
         int downloadingCnt = 0;
         int pendingCnt = 0;
+        db.beginTransaction();
         try {
-            String[] projection = prefixProjectionWithTable(DatabaseHelper.MESSAGES_TABLE,
-                    MessageData.getProjection());
-            String subIdClause =
-                    prefixColumnWithTable(DatabaseHelper.MESSAGES_TABLE,
-                            MessageColumns.SELF_PARTICIPANT_ID)
-                    + " = "
-                    + prefixColumnWithTable(DatabaseHelper.PARTICIPANTS_TABLE,
-                            ParticipantColumns._ID)
-                    + " AND " + ParticipantColumns.SUB_ID + " =?";
-
             // First check if we have any messages already downloading
-            downloadingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE
-                    + "," + DatabaseHelper.PARTICIPANTS_TABLE,
-                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?)"
-                            + " AND " + subIdClause,
+            downloadingCnt = (int) db.queryNumEntries(DatabaseHelper.MESSAGES_TABLE,
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =?",
                     new String[] {
                         Integer.toString(MessageData.BUGLE_STATUS_INCOMING_AUTO_DOWNLOADING),
                         Integer.toString(MessageData.BUGLE_STATUS_INCOMING_MANUAL_DOWNLOADING),
-                        Integer.toString(subId)
+                        selfId
                     });
 
             // TODO: This query is not actually needed if downloadingCnt == 0.
-            cursor = db.query(DatabaseHelper.MESSAGES_TABLE + ","
-                    + DatabaseHelper.PARTICIPANTS_TABLE,
-                    projection,
-                    DatabaseHelper.MessageColumns.STATUS + " =? OR "
-                            + DatabaseHelper.MessageColumns.STATUS + " =?"
-                            + " AND " + subIdClause,
+            cursor = db.query(DatabaseHelper.MESSAGES_TABLE,
+                    MessageData.getProjection(),
+                    DatabaseHelper.MessageColumns.STATUS + " IN (?, ?) AND "
+                    + DatabaseHelper.MessageColumns.SELF_PARTICIPANT_ID + " =? ",
                     new String[]{
-                            Integer.toString(
-                                    MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD),
-                            Integer.toString(
-                                    MessageData.BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD),
-                            Integer.toString(
-                                    subId)
+                        Integer.toString(MessageData.BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD),
+                        Integer.toString(
+                                MessageData.BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD),
+                        selfId
                     },
                     null,
                     null,
diff --git a/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java b/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
index f408e47..489a0f1 100644
--- a/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ProcessSentMessageAction.java
@@ -207,6 +207,8 @@
         if (message == null) {
             LogUtil.w(TAG, "ProcessSentMessageAction: Sent message " + messageId
                     + " missing from local database");
+            ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(
+                    true /* failed */, processingAction);
             return;
         }
         final String conversationId = message.getConversationId();
diff --git a/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java b/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
index 5ffb35d..cb7a35d 100644
--- a/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ReceiveSmsMessageAction.java
@@ -44,6 +44,7 @@
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
 
     private static final String KEY_MESSAGE_VALUES = "message_values";
+    private static final String KEY_SUB_ID = "sub_id";
 
     /**
      * Create a message received from a particular number in a particular conversation
@@ -158,6 +159,7 @@
                     + " in conversation " + message.getConversationId()
                     + ", uri = " + messageUri);
 
+            actionParameters.putInt(KEY_SUB_ID, subId);
             ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
         } else {
             if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) {
diff --git a/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java b/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
index e899b0c..69c34da 100644
--- a/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
+++ b/src/com/android/messaging/datamodel/action/RedownloadMmsAction.java
@@ -36,7 +36,9 @@
  */
 public class RedownloadMmsAction extends Action implements Parcelable {
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
-    private static final int REQUEST_CODE_PENDING_INTENT = 102;
+    private static final int REQUEST_CODE_PENDING_INTENT = 101;
+
+    private static final String KEY_SUB_ID = "sub_id";
 
     /**
      * Download an MMS message
@@ -90,7 +92,8 @@
             BugleDatabaseOperations.updateMessageRow(db, message.getMessageId(), values);
 
             MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
-
+            actionParameters.putInt(KEY_SUB_ID,
+                    BugleDatabaseOperations.getSelfSubscriptionId(db, message.getSelfId()));
             // Whether we succeeded or failed we will check and maybe schedule some more work
             ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
         } else {
diff --git a/src/com/android/messaging/datamodel/action/ResendMessageAction.java b/src/com/android/messaging/datamodel/action/ResendMessageAction.java
index 2201965..b442ae6 100644
--- a/src/com/android/messaging/datamodel/action/ResendMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/ResendMessageAction.java
@@ -34,6 +34,8 @@
 public class ResendMessageAction extends Action implements Parcelable {
     private static final String TAG = LogUtil.BUGLE_DATAMODEL_TAG;
 
+    private static final String KEY_SUB_ID = "sub_id";
+
     /**
      * Manual send of existing message (no listener)
      */
@@ -78,8 +80,6 @@
 
             final ContentValues values = new ContentValues();
             values.put(MessageColumns.STATUS, MessageData.BUGLE_STATUS_OUTGOING_YET_TO_SEND);
-            values.put(MessageColumns.RECEIVED_TIMESTAMP, timestamp);
-            values.put(MessageColumns.SENT_TIMESTAMP, timestamp);
             values.put(MessageColumns.RETRY_START_TIMESTAMP, timestamp);
 
             // Row must exist as was just loaded above (on ActionService thread)
@@ -87,6 +87,9 @@
 
             MessagingContentProvider.notifyMessagesChanged(message.getConversationId());
 
+            actionParameters.putInt(KEY_SUB_ID,
+                    BugleDatabaseOperations.getSelfSubscriptionId(db, message.getSelfId()));
+
             // Whether we succeeded or failed we will check and maybe schedule some more work
             ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(false, this);
 
diff --git a/src/com/android/messaging/datamodel/action/SendMessageAction.java b/src/com/android/messaging/datamodel/action/SendMessageAction.java
index d7ebe8f..44c661d 100644
--- a/src/com/android/messaging/datamodel/action/SendMessageAction.java
+++ b/src/com/android/messaging/datamodel/action/SendMessageAction.java
@@ -44,7 +44,6 @@
  * Action used to send an outgoing message. It writes MMS messages to the telephony db
  * ({@link InsertNewMessageAction}) writes SMS messages to the telephony db). It also
  * initiates the actual sending. It will all be used for re-sending a failed message.
- * NOTE: This action must queue a ProcessPendingMessagesAction when it is done (success or failure).
  * <p>
  * This class is public (not package-private) because the SMS/MMS (e.g. MmsUtils) classes need to
  * access the EXTRA_* fields for setting up the 'sent' pending intent.
@@ -96,13 +95,17 @@
     private boolean queueAction(final String messageId, final Action processingAction) {
         actionParameters.putString(KEY_MESSAGE_ID, messageId);
 
-        final long timestamp = System.currentTimeMillis();
         final DatabaseWrapper db = DataModel.get().getDatabase();
 
         final MessageData message = BugleDatabaseOperations.readMessage(db, messageId);
         // Check message can be resent
         if (message != null && message.canSendMessage()) {
-            final boolean isSms = (message.getProtocol() == MessageData.PROTOCOL_SMS);
+            final boolean isSms = message.getIsSms();
+            long timestamp = System.currentTimeMillis();
+            if (!isSms) {
+                // MMS expects timestamp rounded to nearest second
+                timestamp = 1000 * ((timestamp + 500) / 1000);
+            }
 
             final ParticipantData self = BugleDatabaseOperations.getExistingParticipant(
                     db, message.getSelfId());
@@ -111,8 +114,13 @@
 
             // Update message status
             if (message.getYetToSend()) {
-                // Initial sending of message
-                message.markMessageSending(timestamp);
+                if (message.getReceivedTimeStamp() == message.getRetryStartTimestamp()) {
+                    // Initial sending of message
+                    message.markMessageSending(timestamp);
+                } else {
+                    // Manual resend of message
+                    message.markMessageManualResend(timestamp);
+                }
             } else {
                 // Automatic resend of message
                 message.markMessageResending(timestamp);
@@ -294,9 +302,6 @@
                 MmsUtils.MMS_REQUEST_MANUAL_RETRY, MessageData.RAW_TELEPHONY_STATUS_UNDEFINED,
                 isSms, this, subId, resultCode, httpStatusCode);
 
-        // Whether we succeeded or failed we will check and maybe schedule some more work
-        ProcessPendingMessagesAction.scheduleProcessPendingMessagesAction(true, this);
-
         return null;
     }
 
diff --git a/src/com/android/messaging/datamodel/data/ConversationData.java b/src/com/android/messaging/datamodel/data/ConversationData.java
index 55d5bfc..704e46f 100644
--- a/src/com/android/messaging/datamodel/data/ConversationData.java
+++ b/src/com/android/messaging/datamodel/data/ConversationData.java
@@ -780,8 +780,8 @@
     }
 
     /**
-     * A dummy implementation of {@link ConversationDataListener} so that subclasses may opt to
-     * implement some, but not all, of the interface methods.
+     * A placeholder implementation of {@link ConversationDataListener} so that subclasses may opt
+     * to implement some, but not all, of the interface methods.
      */
     public static class SimpleConversationDataListener implements ConversationDataListener {
 
diff --git a/src/com/android/messaging/datamodel/data/ConversationMessageData.java b/src/com/android/messaging/datamodel/data/ConversationMessageData.java
index 19e1b97..2a85277 100644
--- a/src/com/android/messaging/datamodel/data/ConversationMessageData.java
+++ b/src/com/android/messaging/datamodel/data/ConversationMessageData.java
@@ -523,7 +523,8 @@
     }
 
     public boolean getIsSendComplete() {
-        return mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE;
+        return (mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE
+                || mStatus == MessageData.BUGLE_STATUS_OUTGOING_DELIVERED);
     }
 
     public String getSenderFullName() {
@@ -575,8 +576,9 @@
     public boolean getCanForwardMessage() {
         // Even for outgoing messages, we only allow forwarding if the message has finished sending
         // as media often has issues when send isn't complete
-        return (mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE ||
-                mStatus == MessageData.BUGLE_STATUS_INCOMING_COMPLETE);
+        return (mStatus == MessageData.BUGLE_STATUS_OUTGOING_COMPLETE
+                || mStatus == MessageData.BUGLE_STATUS_OUTGOING_DELIVERED
+                || mStatus == MessageData.BUGLE_STATUS_INCOMING_COMPLETE);
     }
 
     public boolean getCanCopyMessageToClipboard() {
diff --git a/src/com/android/messaging/datamodel/data/DraftMessageData.java b/src/com/android/messaging/datamodel/data/DraftMessageData.java
index 7a7199a..f63c27f 100644
--- a/src/com/android/messaging/datamodel/data/DraftMessageData.java
+++ b/src/com/android/messaging/datamodel/data/DraftMessageData.java
@@ -339,11 +339,23 @@
      */
     private boolean addOneAttachmentNoNotify(final MessagePartData attachment) {
         Assert.isTrue(attachment.isAttachment());
+        // Check duplication.
+        for (final MessagePartData existingAttachment : mAttachments) {
+            if (existingAttachment.getContentUri().equals(attachment.getContentUri())) {
+                // Destroy existing attachment and replace with new attachment instead of destroying
+                // new one so that mSelectedImages in GalleryGridView could be maintained correctly.
+                mAttachments.remove(existingAttachment);
+                existingAttachment.destroyAsync();
+                addAttachment(attachment, null /*pendingAttachment*/);
+                return false;
+            }
+        }
+
         final boolean reachedLimit = getAttachmentCount() >= getAttachmentLimit();
-        if (reachedLimit || containsAttachment(attachment.getContentUri())) {
-            // Never go over the limit. Never add duplicated attachments.
+        if (reachedLimit) {
+            // Never go over the limit.
             attachment.destroyAsync();
-            return reachedLimit;
+            return true;
         } else {
             addAttachment(attachment, null /*pendingAttachment*/);
             return false;
diff --git a/src/com/android/messaging/datamodel/data/MessageData.java b/src/com/android/messaging/datamodel/data/MessageData.java
index cb5311e..b2cab48 100644
--- a/src/com/android/messaging/datamodel/data/MessageData.java
+++ b/src/com/android/messaging/datamodel/data/MessageData.java
@@ -297,9 +297,11 @@
     /**
      * Create a message not yet associated with a particular conversation
      */
-    public static MessageData createSharedMessage(final String messageText) {
+    public static MessageData createSharedMessage(final String messageText,
+            final String subjectText) {
         final MessageData message = new MessageData();
         message.mStatus = BUGLE_STATUS_OUTGOING_DRAFT;
+        message.mMmsSubject = subjectText;
         if (!TextUtils.isEmpty(messageText)) {
             message.mParts.add(MessagePartData.createTextMessagePart(messageText));
         }
@@ -580,7 +582,7 @@
             // primary user.
             return false;
         }
-        // Should show option for manual download iff status is manual download or failed
+        // Should show option for manual download if status is manual download or failed
         return (status == BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
                 status == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD ||
                 // If debug is enabled, allow to download an expired or unavailable message.
@@ -594,7 +596,7 @@
             // primary user.
             return false;
         }
-        // Can download iff status is retrying auto/manual downloading
+        // Can download if status is retrying auto/manual downloading
         return (mStatus == BUGLE_STATUS_INCOMING_RETRYING_MANUAL_DOWNLOAD ||
                 mStatus == BUGLE_STATUS_INCOMING_RETRYING_AUTO_DOWNLOAD);
     }
@@ -605,7 +607,7 @@
             // primary user.
             return false;
         }
-        // Can redownload iff status is manual download not started or download failed
+        // Can redownload if status is manual download not started or download failed
         return (mStatus == BUGLE_STATUS_INCOMING_DOWNLOAD_FAILED ||
                 mStatus == BUGLE_STATUS_INCOMING_YET_TO_MANUAL_DOWNLOAD ||
                 // If debug is enabled, allow to download an expired or unavailable message.
@@ -614,12 +616,12 @@
     }
 
     static boolean getShowResendMessage(final int status) {
-        // Should show option to resend iff status is failed
+        // Should show option to resend if status is failed
         return (status == BUGLE_STATUS_OUTGOING_FAILED);
     }
 
     static boolean getOneClickResendMessage(final int status, final int rawStatus) {
-        // Should show option to resend iff status is failed
+        // Should show option to resend if status is failed
         return (status == BUGLE_STATUS_OUTGOING_FAILED
                 && rawStatus == RAW_TELEPHONY_STATUS_UNDEFINED);
     }
diff --git a/src/com/android/messaging/datamodel/data/ParticipantData.java b/src/com/android/messaging/datamodel/data/ParticipantData.java
index 51e6ee2..95c74e2 100644
--- a/src/com/android/messaging/datamodel/data/ParticipantData.java
+++ b/src/com/android/messaging/datamodel/data/ParticipantData.java
@@ -22,10 +22,12 @@
 import android.graphics.Color;
 import android.os.Parcel;
 import android.os.Parcelable;
-import androidx.appcompat.mms.MmsManager;
 import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
 
+import androidx.appcompat.mms.MmsManager;
+import androidx.collection.ArrayMap;
+
 import com.android.ex.chips.RecipientEntry;
 import com.android.messaging.Factory;
 import com.android.messaging.R;
@@ -41,6 +43,10 @@
  * A class that encapsulates all of the data for a specific participant in a conversation.
  */
 public class ParticipantData implements Parcelable {
+
+    private static final ArrayMap<Integer, String> sSubIdtoParticipantIdCache =
+            new ArrayMap<Integer, String>();
+
     // We always use -1 as default/invalid sub id although system may give us anything negative
     public static final int DEFAULT_SELF_SUB_ID = MmsManager.DEFAULT_SUB_ID;
 
@@ -282,6 +288,34 @@
         return pd;
     }
 
+    public static String getParticipantId(final DatabaseWrapper db, final int subId) {
+        String id;
+        synchronized (sSubIdtoParticipantIdCache) {
+            id = sSubIdtoParticipantIdCache.get(subId);
+        }
+
+        if (id != null) {
+            return id;
+        }
+
+        try (final Cursor cursor =
+                    db.query(DatabaseHelper.PARTICIPANTS_TABLE,
+                            new String[] {ParticipantColumns._ID},
+                            ParticipantColumns.SUB_ID + " =?",
+                            new String[] {Integer.toString(subId)}, null, null, null)) {
+
+            if (cursor.moveToFirst()) {
+                // We found an existing participant in the database
+                id = cursor.getString(0);
+                synchronized (sSubIdtoParticipantIdCache) {
+                    // Add it to the cache for next time
+                    sSubIdtoParticipantIdCache.put(subId, id);
+                }
+            }
+        }
+        return id;
+    }
+
     private void maybeSetupUnknownSender() {
         if (isUnknownSender()) {
             // Because your locale may change, we setup the display string for the unknown sender
diff --git a/src/com/android/messaging/datamodel/media/BindableMediaRequest.java b/src/com/android/messaging/datamodel/media/BindableMediaRequest.java
index 36521d5..d979323 100644
--- a/src/com/android/messaging/datamodel/media/BindableMediaRequest.java
+++ b/src/com/android/messaging/datamodel/media/BindableMediaRequest.java
@@ -19,7 +19,7 @@
 import com.android.messaging.datamodel.media.MediaResourceManager.MediaResourceLoadListener;
 
 /**
- * The {@link MediaRequest} interface is threading-model-blind, allowing the implementations to
+ * The {@link MediaRequest} interface is threading-model-oblivious, allowing the implementations to
  * be processed synchronously or asynchronously.
  * This is a {@link MediaRequest} implementation that includes functionalities such as binding and
  * event callbacks for multi-threaded media request processing.
diff --git a/src/com/android/messaging/datamodel/media/MediaRequest.java b/src/com/android/messaging/datamodel/media/MediaRequest.java
index 703671b..78ae85e 100644
--- a/src/com/android/messaging/datamodel/media/MediaRequest.java
+++ b/src/com/android/messaging/datamodel/media/MediaRequest.java
@@ -21,7 +21,7 @@
  * Keeps track of a media loading request. MediaResourceManager uses this interface to load, encode,
  * decode, and cache different types of media resource.
  *
- * This interface defines a media request class that's threading-model-blind. Wrapper classes
+ * This interface defines a media request class that's threading-model-oblivious. Wrapper classes
  * (such as {@link AsyncMediaRequestWrapper} wraps around any base media request to offer async
  * extensions).
  */
@@ -67,4 +67,4 @@
      * Returns the descriptor defining the request.
      */
     MediaRequestDescriptor<T> getDescriptor();
-}
\ No newline at end of file
+}
diff --git a/src/com/android/messaging/mmslib/pdu/PduComposer.java b/src/com/android/messaging/mmslib/pdu/PduComposer.java
index f318382..ace49d1 100644
--- a/src/com/android/messaging/mmslib/pdu/PduComposer.java
+++ b/src/com/android/messaging/mmslib/pdu/PduComposer.java
@@ -1080,7 +1080,7 @@
             }
 
             if (dataLength != (attachment.getLength() - headerLength)) {
-                throw new RuntimeException("BUG: Length sanity check failed");
+                throw new RuntimeException("BUG: Length check failed");
             }
 
             mStack.pop();
diff --git a/src/com/android/messaging/mmslib/pdu/PduPersister.java b/src/com/android/messaging/mmslib/pdu/PduPersister.java
index 1139aa1..436867a 100644
--- a/src/com/android/messaging/mmslib/pdu/PduPersister.java
+++ b/src/com/android/messaging/mmslib/pdu/PduPersister.java
@@ -52,6 +52,7 @@
 import com.android.messaging.util.ContentType;
 import com.android.messaging.util.LogUtil;
 import com.android.messaging.util.OsUtil;
+import com.android.messaging.util.UriUtil;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -1018,8 +1019,7 @@
         String path = null;
         if (null != uri) {
             final String scheme = uri.getScheme();
-            if (null == scheme || scheme.equals("") ||
-                    scheme.equals(ContentResolver.SCHEME_FILE)) {
+            if (null == scheme || scheme.equals("") || UriUtil.isFileUri(uri)) {
                 path = uri.getPath();
 
             } else if (scheme.equals("http")) {
@@ -1415,11 +1415,11 @@
 
                     // For received messages (whether group MMS is enabled or not) we want to
                     // associate this message with the thread composed of all the recipients
-                    // EXCLUDING our own number. This includes the person who sent the
-                    // message (the FROM field above) in addition to the other people the message
-                    // was addressed TO (or CC fields to address group messaging compatibility
-                    // issues with devices that place numbers in this field). Typically our own
-                    // number is in the TO/CC field so we have to remove it in loadRecipients.
+                    // EXCLUDING our own number. This includes the person who sent the message
+                    // (the FROM field above) in addition to the other people the message was
+                    // addressed TO (or CC fields to address group messaging compatibility issues
+                    // with devices that place numbers in this field). Typically our own number is
+                    // in the TO/CC field so we have to remove it in checkAndLoadToCcRecipients.
                     checkAndLoadToCcRecipients(recipients, addressMap, subPhoneNumber);
                     break;
                 case PduHeaders.MESSAGE_TYPE_SEND_REQ:
@@ -1440,7 +1440,7 @@
 
         // Save parts first to avoid inconsistent message is loaded
         // while saving the parts.
-        final long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+        final long placeholderId = System.currentTimeMillis(); // Placeholder ID of the msg.
 
         // Figure out if this PDU is a text-only message
         boolean textOnly = true;
@@ -1463,7 +1463,7 @@
                 }
                 for (int i = 0; i < partsNum; i++) {
                     final PduPart part = body.getPart(i);
-                    persistPart(part, dummyId, preOpenedFiles);
+                    persistPart(part, placeholderId, preOpenedFiles);
 
                     // If we've got anything besides text/plain or SMIL part, then we've got
                     // an mms message with some other type of attachment.
@@ -1501,14 +1501,14 @@
                 throw new MmsException("persist() failed: return null.");
             }
             // Get the real ID of the PDU and update all parts which were
-            // saved with the dummy ID.
+            // saved with the placeholder ID.
             msgId = ContentUris.parseId(res);
         }
 
         values = new ContentValues(1);
         values.put(Part.MSG_ID, msgId);
         SqliteWrapper.update(mContext, mContentResolver,
-                Uri.parse("content://mms/" + dummyId + "/part"),
+                Uri.parse("content://mms/" + placeholderId + "/part"),
                 values, null, null);
         // We should return the longest URI of the persisted PDU, for
         // example, if input URI is "content://mms/inbox" and the _ID of
@@ -1582,9 +1582,17 @@
                 }
             }
         }
+
+        // If selfNumber is unavailable and there is only a single address in all TO and CC, we can
+        // skip adding it into recipients as assuming it is my own phone number.
+        final boolean isSelfNumberUnavailable = TextUtils.isEmpty(selfNumber);
+        if (isSelfNumberUnavailable && numbers.size() == 1) {
+            return;
+        }
+
         for (final String number : numbers) {
             // Only add numbers which aren't my own number.
-            if (TextUtils.isEmpty(selfNumber) || !PhoneNumberUtils.compare(number, selfNumber)) {
+            if (isSelfNumberUnavailable || !PhoneNumberUtils.compare(number, selfNumber)) {
                 if (!recipients.contains(number)) {
                     // Only add numbers which aren't already included.
                     recipients.add(number);
diff --git a/src/com/android/messaging/receiver/SendStatusReceiver.java b/src/com/android/messaging/receiver/SendStatusReceiver.java
index c1b2bca..8d2e10f 100644
--- a/src/com/android/messaging/receiver/SendStatusReceiver.java
+++ b/src/com/android/messaging/receiver/SendStatusReceiver.java
@@ -87,7 +87,7 @@
                 final String format = intent.getStringExtra("format");
                 status = smsMessage.getStatus();
                 // Simple matching up CDMA status with GSM status.
-                if (SmsMessage.FORMAT_3GPP2.equals(format)) {
+                if ("3gpp2".equals(format)) {
                     final int errorClass = (status >> 24) & 0x03;
                     final int statusCode = (status >> 16) & 0x3f;
                     switch (errorClass) {
diff --git a/src/com/android/messaging/sms/MmsSmsUtils.java b/src/com/android/messaging/sms/MmsSmsUtils.java
index 1a0ef99..7719359 100644
--- a/src/com/android/messaging/sms/MmsSmsUtils.java
+++ b/src/com/android/messaging/sms/MmsSmsUtils.java
@@ -101,6 +101,14 @@
     }
 
     /**
+     * This pattern is intended for searching for carrier specific phone numbers starting with star
+     * sign and digits such as the voice mail number.
+     * (e.g. *20 Chile Claro)
+     */
+    private static final Pattern PHONE_NUMBER_STARTING_WITH_STAR_PATTERN =
+            Pattern.compile("\\*[0-9]+");
+
+    /**
      * Returns true if the number is a Phone number
      *
      * @param number the input number to be tested
@@ -111,8 +119,12 @@
             return false;
         }
 
-        final Matcher match = Patterns.PHONE.matcher(number);
-        return match.matches();
+        Matcher match = Patterns.PHONE.matcher(number);
+        if (!match.matches()) {
+            match = PHONE_NUMBER_STARTING_WITH_STAR_PATTERN.matcher(number);
+            return match.matches();
+        }
+        return true;
     }
 
     /**
diff --git a/src/com/android/messaging/sms/MmsUtils.java b/src/com/android/messaging/sms/MmsUtils.java
index df0db34..32f0616 100644
--- a/src/com/android/messaging/sms/MmsUtils.java
+++ b/src/com/android/messaging/sms/MmsUtils.java
@@ -27,8 +27,6 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
 import android.media.MediaMetadataRetriever;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -1113,51 +1111,6 @@
         return subject;
     }
 
-    // return a semicolon separated list of phone numbers from a smsto: uri.
-    public static String getSmsRecipients(final Uri uri) {
-        String recipients = uri.getSchemeSpecificPart();
-        final int pos = recipients.indexOf('?');
-        if (pos != -1) {
-            recipients = recipients.substring(0, pos);
-        }
-        recipients = replaceUnicodeDigits(recipients).replace(',', ';');
-        return recipients;
-    }
-
-    // This function was lifted from Telephony.PhoneNumberUtils because it was @hide
-    /**
-     * Replace arabic/unicode digits with decimal digits.
-     * @param number
-     *            the number to be normalized.
-     * @return the replaced number.
-     */
-    private static String replaceUnicodeDigits(final String number) {
-        final StringBuilder normalizedDigits = new StringBuilder(number.length());
-        for (final char c : number.toCharArray()) {
-            final int digit = Character.digit(c, 10);
-            if (digit != -1) {
-                normalizedDigits.append(digit);
-            } else {
-                normalizedDigits.append(c);
-            }
-        }
-        return normalizedDigits.toString();
-    }
-
-    /**
-     * @return Whether the data roaming is enabled
-     */
-    private static boolean isDataRoamingEnabled() {
-        boolean dataRoamingEnabled = false;
-        final ContentResolver cr = Factory.get().getApplicationContext().getContentResolver();
-        if (OsUtil.isAtLeastJB_MR1()) {
-            dataRoamingEnabled = (Settings.Global.getInt(cr, Settings.Global.DATA_ROAMING, 0) != 0);
-        } else {
-            dataRoamingEnabled = (Settings.System.getInt(cr, Settings.System.DATA_ROAMING, 0) != 0);
-        }
-        return dataRoamingEnabled;
-    }
-
     /**
      * @return Whether to auto retrieve MMS
      */
@@ -1203,7 +1156,9 @@
     public static SmsMessage getSmsMessageFromDeliveryReport(final Intent intent) {
         final byte[] pdu = intent.getByteArrayExtra("pdu");
         final String format = intent.getStringExtra("format");
-        return SmsMessage.createFromPdu(pdu, format);
+        return OsUtil.isAtLeastM()
+                ? SmsMessage.createFromPdu(pdu, format)
+                : SmsMessage.createFromPdu(pdu);
     }
 
     /**
@@ -1544,7 +1499,7 @@
                 cursor = SqliteWrapper.query(
                         context,
                         resolver,
-                        Telephony.Carriers.SIM_APN_URI,
+                        Telephony.Carriers.CONTENT_URI,
                         TEST_CARRIERS_PROJECTION,
                         null/*selection*/,
                         null/*selectionArgs*/,
@@ -1927,10 +1882,9 @@
             final long expiry, final RetrieveConf retrieveConf) {
         final byte[] notificationTransactionId = stringToBytes(transactionId, "UTF-8");
         Uri messageUri = null;
-        int status = MMS_REQUEST_MANUAL_RETRY;
-        int retrieveStatus = PDU_HEADER_VALUE_UNDEFINED;
+        final int status;
+        final int retrieveStatus = retrieveConf.getRetrieveStatus();
 
-        retrieveStatus = retrieveConf.getRetrieveStatus();
         if (retrieveStatus == PduHeaders.RETRIEVE_STATUS_OK) {
             status = MMS_REQUEST_SUCCEEDED;
         } else if (retrieveStatus >= PduHeaders.RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE &&
@@ -1965,17 +1919,9 @@
             final Uri inboxUri = MmsUtils.insertReceivedMmsMessage(context, retrieveConf, subId,
                     subPhoneNumber, receivedTimestampInSeconds, expiry, transactionId);
             messageUri = ContentUris.withAppendedId(Mms.CONTENT_URI, ContentUris.parseId(inboxUri));
-        } else if (status == MMS_REQUEST_AUTO_RETRY) {
-            // For a retry do nothing
-        } else if (status == MMS_REQUEST_MANUAL_RETRY && autoDownload) {
-            // Failure from autodownload - just treat like manual download
-            sendNotifyResponseForMmsDownload(
-                    context,
-                    subId,
-                    notificationTransactionId,
-                    contentLocation,
-                    PduHeaders.STATUS_DEFERRED);
         }
+        // Do nothing for MMS_REQUEST_AUTO_RETRY and MMS_REQUEST_NO_RETRY.
+
         return new StatusPlusUri(status, retrieveStatus, messageUri);
     }
 
@@ -2093,16 +2039,6 @@
         return !phoneUtils.isAirplaneModeOn();
     }
 
-    public static boolean isMobileDataEnabled(final int subId) {
-        final PhoneUtils phoneUtils = PhoneUtils.get(subId);
-        return phoneUtils.isMobileDataEnabled();
-    }
-
-    public static boolean isAirplaneModeOn(final int subId) {
-        final PhoneUtils phoneUtils = PhoneUtils.get(subId);
-        return phoneUtils.isAirplaneModeOn();
-    }
-
     public static StatusPlusUri sendMmsMessage(final Context context, final int subId,
             final Uri messageUri, final Bundle extras) {
         int status = MMS_REQUEST_MANUAL_RETRY;
@@ -2145,7 +2081,7 @@
 
     public static StatusPlusUri updateSentMmsMessageStatus(final Context context,
             final Uri messageUri, final SendConf sendConf) {
-        int status = MMS_REQUEST_MANUAL_RETRY;
+        final int status;
         final int respStatus = sendConf.getResponseStatus();
 
         final ContentValues values = new ContentValues(2);
@@ -2158,12 +2094,16 @@
                 messageUri, values, null, null);
         if (respStatus == PduHeaders.RESPONSE_STATUS_OK) {
             status = MMS_REQUEST_SUCCEEDED;
-        } else if (respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE ||
-                respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM ||
-                respStatus == PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) {
+        } else if (respStatus >= PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE
+                && respStatus < PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_FAILURE) {
+            // Only RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE and RESPONSE_STATUS_ERROR_TRANSIENT
+            // _NETWORK_PROBLEM are used in the M-Send.conf. But for others transient failures
+            // including reserved values for future purposes, it should work same as transient
+            // failure always. (OMA-MMS-ENC-V1_2, 7.2.37. X-Mms-Response-Status field)
             status = MMS_REQUEST_AUTO_RETRY;
         } else {
             // else permanent failure
+            status = MMS_REQUEST_MANUAL_RETRY;
             LogUtil.e(TAG, "MmsUtils: failed to send message; respStatus = "
                     + String.format("0x%X", respStatus));
         }
@@ -2662,26 +2602,6 @@
     }
 
     /**
-     * The absence of a connection type.
-     */
-    public static final int TYPE_NONE = -1;
-
-    public static int getConnectivityEventNetworkType(final Context context, final Intent intent) {
-        final ConnectivityManager connMgr = (ConnectivityManager)
-                context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        if (OsUtil.isAtLeastJB_MR1()) {
-            return intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, TYPE_NONE);
-        } else {
-            final NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
-                    ConnectivityManager.EXTRA_NETWORK_INFO);
-            if (info != null) {
-                return info.getType();
-            }
-        }
-        return TYPE_NONE;
-    }
-
-    /**
      * Dump the raw MMS data into a file
      *
      * @param rawPdu The raw pdu data
diff --git a/src/com/android/messaging/sms/SmsSender.java b/src/com/android/messaging/sms/SmsSender.java
index 889973f..e429995 100644
--- a/src/com/android/messaging/sms/SmsSender.java
+++ b/src/com/android/messaging/sms/SmsSender.java
@@ -64,9 +64,6 @@
 
     private static final Random RANDOM = new Random();
 
-    // Whether we should send multipart SMS as separate messages
-    private static Boolean sSendMultipartSmsAsSeparateMessages = null;
-
     /**
      * Class that holds the sent status for all parts of a multipart message sending
      */
@@ -180,7 +177,7 @@
     }
 
     // This should be called from a RequestWriter queue thread
-    public static SendResult sendMessage(final Context context,  final int subId, String dest,
+    public static SendResult sendMessage(final Context context, final int subId, String dest,
             String message, final String serviceCenter, final boolean requireDeliveryReport,
             final Uri messageUri) throws SmsException {
         if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
@@ -281,12 +278,8 @@
                             messageUri, partId, subId),
                     0/*flag*/));
         }
-        if (sSendMultipartSmsAsSeparateMessages == null) {
-            sSendMultipartSmsAsSeparateMessages = MmsConfig.get(subId)
-                    .getSendMultipartSmsAsSeparateMessages();
-        }
         try {
-            if (sSendMultipartSmsAsSeparateMessages) {
+            if (MmsConfig.get(subId).getSendMultipartSmsAsSeparateMessages()) {
                 // If multipart sms is not supported, send them as separate messages
                 for (int i = 0; i < messageCount; i++) {
                     smsManager.sendTextMessage(dest,
diff --git a/src/com/android/messaging/ui/AttachmentPreview.java b/src/com/android/messaging/ui/AttachmentPreview.java
index 7eea14b..f4465c4 100644
--- a/src/com/android/messaging/ui/AttachmentPreview.java
+++ b/src/com/android/messaging/ui/AttachmentPreview.java
@@ -57,6 +57,8 @@
     private Runnable mHideRunnable;
     private boolean mPendingHideCanceled;
 
+    private PopupTransitionAnimation mPopupTransitionAnimation;
+
     private static final int CLOSE_BUTTON_REVEAL_STAGGER_MILLIS = 300;
 
     public AttachmentPreview(final Context context, final AttributeSet attrs) {
@@ -132,6 +134,7 @@
                             public void run() {
                                 // Only hide if we are didn't get overruled by showing
                                 if (!mPendingHideCanceled) {
+                                    stopPopupAnimation();
                                     mAttachmentView.removeAllViews();
                                     setVisibility(GONE);
                                 }
@@ -280,10 +283,19 @@
         mHideRunnable.run();
     }
 
-    static void tryAnimateViewIn(final MessagePartData attachmentData, final View view) {
+    private void tryAnimateViewIn(final MessagePartData attachmentData, final View view) {
         if (attachmentData instanceof MediaPickerMessagePartData) {
             final Rect startRect = ((MediaPickerMessagePartData) attachmentData).getStartRect();
-            new PopupTransitionAnimation(startRect, view).startAfterLayoutComplete();
+            stopPopupAnimation();
+            mPopupTransitionAnimation = new PopupTransitionAnimation(startRect, view);
+            mPopupTransitionAnimation.startAfterLayoutComplete();
+        }
+    }
+
+    private void stopPopupAnimation() {
+        if (mPopupTransitionAnimation != null) {
+            mPopupTransitionAnimation.cancel();
+            mPopupTransitionAnimation = null;
         }
     }
 
diff --git a/src/com/android/messaging/ui/MultiAttachmentLayout.java b/src/com/android/messaging/ui/MultiAttachmentLayout.java
index f620245..5bae8a5 100644
--- a/src/com/android/messaging/ui/MultiAttachmentLayout.java
+++ b/src/com/android/messaging/ui/MultiAttachmentLayout.java
@@ -33,6 +33,7 @@
 import com.android.messaging.datamodel.data.PendingAttachmentData;
 import com.android.messaging.datamodel.media.ImageRequestDescriptor;
 import com.android.messaging.ui.AsyncImageView.AsyncImageViewDelayLoader;
+import com.android.messaging.ui.animation.PopupTransitionAnimation;
 import com.android.messaging.util.AccessibilityUtil;
 import com.android.messaging.util.Assert;
 import com.android.messaging.util.UiUtils;
@@ -275,7 +276,11 @@
             // views will slide from their previous position to their new position within the
             // layout
             if (i == 0) {
-                AttachmentPreview.tryAnimateViewIn(attachment, attachmentWrapper.view);
+                if (attachment instanceof MediaPickerMessagePartData) {
+                    final Rect startRect = ((MediaPickerMessagePartData) attachment).getStartRect();
+                    new PopupTransitionAnimation(startRect, attachmentWrapper.view)
+                            .startAfterLayoutComplete();
+                }
             }
             attachmentWrapper.needsSlideAnimation = i > 0;
         }
diff --git a/src/com/android/messaging/ui/PermissionCheckActivity.java b/src/com/android/messaging/ui/PermissionCheckActivity.java
index e992a10..1b8cedb 100644
--- a/src/com/android/messaging/ui/PermissionCheckActivity.java
+++ b/src/com/android/messaging/ui/PermissionCheckActivity.java
@@ -106,10 +106,7 @@
         if (requestCode == REQUIRED_PERMISSIONS_REQUEST_CODE) {
             // We do not use grantResults as some of the granted permissions might have been
             // revoked while the permissions dialog box was being shown for the missing permissions.
-            if (OsUtil.hasRequiredPermissions()) {
-                Factory.get().onRequiredPermissionsAcquired();
-                redirect();
-            } else {
+            if (!redirectIfNeeded()) {
                 final long currentTimeMillis = SystemClock.elapsedRealtime();
                 // If the permission request completes very quickly, it must be because the system
                 // automatically denied. This can happen if the user had previously denied it
@@ -130,6 +127,7 @@
             return false;
         }
 
+        Factory.get().onRequiredPermissionsAcquired();
         redirect();
         return true;
     }
diff --git a/src/com/android/messaging/ui/SnackBarManager.java b/src/com/android/messaging/ui/SnackBarManager.java
index e107999..d5ca870 100644
--- a/src/com/android/messaging/ui/SnackBarManager.java
+++ b/src/com/android/messaging/ui/SnackBarManager.java
@@ -25,6 +25,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.MeasureSpec;
+import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -86,6 +87,23 @@
         }
     };
 
+    private final OnAttachStateChangeListener mAttachStateChangeListener =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+                    // Dismiss the PopupWindow and clear SnackBarManager state.
+                    mHideHandler.removeCallbacks(mDismissRunnable);
+                    mPopupWindow.dismiss();
+
+                    mCurrentSnackBar = null;
+                    mNextSnackBar = null;
+                    mIsCurrentlyDismissing = false;
+                }
+
+                @Override
+                public void onViewAttachedToWindow(View v) {}
+            };
+
     private final int mTranslationDurationMs;
     private final Handler mHideHandler;
 
@@ -181,6 +199,7 @@
             mPopupWindow.showAsDropDown(anchorView, 0, getRelativeOffset(snackBar));
         }
 
+        snackBar.getParentView().addOnAttachStateChangeListener(mAttachStateChangeListener);
 
         // Animate the toast bar into view.
         placeSnackBarOffScreen(snackBar);
@@ -238,6 +257,8 @@
                     // PopupWindow.dismiss() will fire an IllegalArgumentException if the activity
                     // has already ended while we were animating
                 }
+                snackBar.getParentView()
+                        .removeOnAttachStateChangeListener(mAttachStateChangeListener);
 
                 mCurrentSnackBar = null;
                 mIsCurrentlyDismissing = false;
diff --git a/src/com/android/messaging/ui/UIIntents.java b/src/com/android/messaging/ui/UIIntents.java
index b09c457..bf5edd7 100644
--- a/src/com/android/messaging/ui/UIIntents.java
+++ b/src/com/android/messaging/ui/UIIntents.java
@@ -47,6 +47,9 @@
     // The request code for picking a media from the Document picker.
     public static final int REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER = 1400;
 
+    // The request code for picking a contact card from existing Contacts apps.
+    public static final int REQUEST_PICK_CONTACT_CARD = 1500;
+
     // Indicates what type of notification this applies to (See BugleNotifications:
     // UPDATE_NONE, UPDATE_MESSAGES, UPDATE_ERRORS, UPDATE_ALL)
     public static final String UI_INTENT_EXTRA_NOTIFICATIONS_UPDATE = "notifications_update";
@@ -166,13 +169,20 @@
     public abstract void launchAddContactActivity(final Context context, final String destination);
 
     /**
-     * Launch an activity to show the document picker to pick an image/video.
+     * Launch an activity to show the document picker to pick an image/video/audio.
      *
      * @param fragment the requesting fragment
      */
     public abstract void launchDocumentImagePicker(final Fragment fragment);
 
     /**
+     * Launch an activity to show the contacts list to pick one.
+     *
+     * @param fragment the requesting fragment
+     */
+    public abstract void launchContactCardPicker(final Fragment fragment);
+
+    /**
      * Launch an activity to show people & options for a given conversation.
      */
     public abstract void launchPeopleAndOptionsActivity(final Activity context,
diff --git a/src/com/android/messaging/ui/UIIntentsImpl.java b/src/com/android/messaging/ui/UIIntentsImpl.java
index ac430cd..8a1224a 100644
--- a/src/com/android/messaging/ui/UIIntentsImpl.java
+++ b/src/com/android/messaging/ui/UIIntentsImpl.java
@@ -244,6 +244,19 @@
     }
 
     @Override
+    public void launchContactCardPicker(final Fragment fragment) {
+        final Intent intent = new Intent(Intent.ACTION_PICK);
+        intent.setType(Contacts.CONTENT_TYPE);
+
+        try {
+            fragment.startActivityForResult(intent, REQUEST_PICK_CONTACT_CARD);
+        } catch (final ActivityNotFoundException ex) {
+            LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
+            UiUtils.showToastAtBottom(R.string.activity_not_found_message);
+        }
+    }
+
+    @Override
     public void launchPeopleAndOptionsActivity(final Activity activity,
             final String conversationId) {
         final Intent intent = new Intent(activity, PeopleAndOptionsActivity.class);
diff --git a/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java b/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java
index ae4b2eb..5d19a83 100644
--- a/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java
+++ b/src/com/android/messaging/ui/appsettings/ApnEditorActivity.java
@@ -365,8 +365,8 @@
                 protected Void doInBackground(Void... params) {
                     ContentValues values = new ContentValues();
 
-                    // Add a dummy name "Untitled", if the user exits the screen without adding a
-                    // name but entered other information worth keeping.
+                    // Add a placeholder name "Untitled", if the user exits the screen without
+                    // adding a name but entered other information worth keeping.
                     values.put(Telephony.Carriers.NAME, name.length() < 1 ?
                             getResources().getString(R.string.untitled_apn) : name);
                     values.put(Telephony.Carriers.MMSPROXY, checkNotSet(mMmsProxy.getText()));
diff --git a/src/com/android/messaging/ui/appsettings/ApnPreference.java b/src/com/android/messaging/ui/appsettings/ApnPreference.java
index c5cc85a..64358eb 100644
--- a/src/com/android/messaging/ui/appsettings/ApnPreference.java
+++ b/src/com/android/messaging/ui/appsettings/ApnPreference.java
@@ -26,7 +26,6 @@
 import android.widget.CompoundButton;
 import android.widget.RadioButton;
 import android.widget.RelativeLayout;
-import android.widget.TextView;
 
 import com.android.messaging.R;
 import com.android.messaging.datamodel.data.ParticipantData;
@@ -81,7 +80,7 @@
             } else {
                 rb.setVisibility(View.GONE);
             }
-            setApnRadioButtonContentDescription(rb);
+            rb.setContentDescription(getTitle());
         }
 
         View textLayout = view.findViewById(R.id.text_layout);
@@ -92,13 +91,6 @@
         return view;
     }
 
-    public void setApnRadioButtonContentDescription(final CompoundButton buttonView) {
-        final View widget = (View) buttonView.getParent();
-        final TextView tv = (TextView) widget.findViewById(R.id.title);
-        final String apnTitle = tv.getText().toString();
-        buttonView.setContentDescription(apnTitle);
-    }
-
     public boolean isChecked() {
         return getKey().equals(mSelectedKey);
     }
@@ -128,7 +120,7 @@
             mCurrentChecked = null;
             mSelectedKey = null;
         }
-        setApnRadioButtonContentDescription(buttonView);
+        buttonView.setContentDescription(getTitle());
     }
 
     public void onClick(android.view.View v) {
diff --git a/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java b/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java
index 8b4644e..13f775a 100644
--- a/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/ApnSettingsActivity.java
@@ -274,7 +274,7 @@
             if (!mUnavailable) {
                 menu.add(0, MENU_NEW, 0,
                         getResources().getString(R.string.menu_new_apn))
-                        .setIcon(R.drawable.ic_add_gray)
+                        .setIcon(R.drawable.ic_add_white)
                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
                 menu.add(0, MENU_RESTORE, 0,
                         getResources().getString(R.string.menu_restore_default_apn))
diff --git a/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java b/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java
index 7ab5dc7..c2fd473 100644
--- a/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/PerSubscriptionSettingsActivity.java
@@ -28,10 +28,12 @@
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
-import androidx.core.app.NavUtils;
 import android.text.TextUtils;
 import android.view.MenuItem;
 
+import androidx.appcompat.mms.MmsManager;
+import androidx.core.app.NavUtils;
+
 import com.android.messaging.Factory;
 import com.android.messaging.R;
 import com.android.messaging.datamodel.ParticipantRefresh;
@@ -159,17 +161,18 @@
             }
 
             // Access Point Names (APNs)
-            final Preference apnsPref = findPreference(getString(R.string.sms_apns_key));
+            final PreferenceScreen apnsScreen =
+                    (PreferenceScreen) findPreference(getString(R.string.sms_apns_key));
 
-            if (MmsUtils.useSystemApnTable() && !ApnDatabase.doesDatabaseExist()) {
-                // Don't remove the ability to edit the local APN prefs if this device lets us
+            if (!MmsManager.shouldUseLegacyMms()
+                    || (MmsUtils.useSystemApnTable() && !ApnDatabase.doesDatabaseExist())) {
+                // 1) Remove the ability to edit the local APN prefs if it doesn't use legacy APIs.
+                // 2) Don't remove the ability to edit the local APN prefs if this device lets us
                 // access the system APN, but we can't find the MCC/MNC in the APN table and we
                 // created the local APN table in case the MCC/MNC was in there. In other words,
                 // if the local APN table exists, let the user edit it.
-                advancedCategory.removePreference(apnsPref);
+                advancedCategory.removePreference((Preference) apnsScreen);
             } else {
-                final PreferenceScreen apnsScreen = (PreferenceScreen) findPreference(
-                        getString(R.string.sms_apns_key));
                 apnsScreen.setIntent(UIIntents.get()
                         .getApnSettingsIntent(getPreferenceScreen().getContext(), mSubId));
             }
@@ -183,7 +186,13 @@
                 autoRetrieveMmsPreference.setEnabled(false);
                 final Preference deliveryReportsPreference =
                         findPreference(getString(R.string.delivery_reports_pref_key));
-                deliveryReportsPreference.setEnabled(false);
+                if (deliveryReportsPreference != null) {
+                    deliveryReportsPreference.setEnabled(false);
+                }
+            }
+
+            if (advancedCategory.getPreferenceCount() == 0) {
+                getPreferenceScreen().removePreference(advancedCategory);
             }
         }
 
diff --git a/src/com/android/messaging/ui/appsettings/SettingsActivity.java b/src/com/android/messaging/ui/appsettings/SettingsActivity.java
index e8c05ec..dc16d0a 100644
--- a/src/com/android/messaging/ui/appsettings/SettingsActivity.java
+++ b/src/com/android/messaging/ui/appsettings/SettingsActivity.java
@@ -46,7 +46,7 @@
 import java.util.List;
 
 /**
- * Shows the "master" settings activity that contains two parts, one for application-wide settings
+ * Shows the "primary" settings activity that contains two parts, one for application-wide settings
  * (dubbed "General settings"), and one or more for per-subscription settings (dubbed "Messaging
  * settings" for single-SIM, and the actual SIM name for multi-SIM). Clicking on either item
  * (e.g. "General settings") will open the detail settings activity (ApplicationSettingsActivity
diff --git a/src/com/android/messaging/ui/conversation/ComposeMessageView.java b/src/com/android/messaging/ui/conversation/ComposeMessageView.java
index 0f36e9a..c35927e 100644
--- a/src/com/android/messaging/ui/conversation/ComposeMessageView.java
+++ b/src/com/android/messaging/ui/conversation/ComposeMessageView.java
@@ -72,6 +72,7 @@
 import com.android.messaging.util.UiUtils;
 import com.android.messaging.util.UriUtil;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -683,7 +684,8 @@
         final boolean hasWorkingDraft = hasMessageText || hasSubject ||
                 mBinding.getData().hasAttachments();
 
-        final List<MessagePartData> attachments = draftMessageData.getReadOnlyAttachments();
+        final List<MessagePartData> attachments =
+                new ArrayList<MessagePartData>(draftMessageData.getReadOnlyAttachments());
         if (draftMessageData.getIsMms()) { // MMS case
             if (draftMessageData.hasAttachments()) {
                 if (hasAttachmentsChanged) {
diff --git a/src/com/android/messaging/ui/conversation/ConversationFragment.java b/src/com/android/messaging/ui/conversation/ConversationFragment.java
index 5c97c1c..6eb7089 100644
--- a/src/com/android/messaging/ui/conversation/ConversationFragment.java
+++ b/src/com/android/messaging/ui/conversation/ConversationFragment.java
@@ -51,6 +51,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.view.ActionMode;
 import android.view.Display;
@@ -774,20 +775,27 @@
             case R.id.action_call:
                 final String phoneNumber = mBinding.getData().getParticipantPhoneNumber();
                 Assert.notNull(phoneNumber);
-                final View targetView = getActivity().findViewById(R.id.action_call);
-                Point centerPoint;
-                if (targetView != null) {
-                    final int screenLocation[] = new int[2];
-                    targetView.getLocationOnScreen(screenLocation);
-                    final int centerX = screenLocation[0] + targetView.getWidth() / 2;
-                    final int centerY = screenLocation[1] + targetView.getHeight() / 2;
-                    centerPoint = new Point(centerX, centerY);
+                // Can't make a call to emergency numbers using ACTION_CALL.
+                if (PhoneNumberUtils.isEmergencyNumber(phoneNumber)) {
+                    UiUtils.showToast(R.string.disallow_emergency_call);
                 } else {
-                    // In the overflow menu, just use the center of the screen.
-                    final Display display = getActivity().getWindowManager().getDefaultDisplay();
-                    centerPoint = new Point(display.getWidth() / 2, display.getHeight() / 2);
+                    final View targetView = getActivity().findViewById(R.id.action_call);
+                    Point centerPoint;
+                    if (targetView != null) {
+                        final int screenLocation[] = new int[2];
+                        targetView.getLocationOnScreen(screenLocation);
+                        final int centerX = screenLocation[0] + targetView.getWidth() / 2;
+                        final int centerY = screenLocation[1] + targetView.getHeight() / 2;
+                        centerPoint = new Point(centerX, centerY);
+                    } else {
+                        // In the overflow menu, just use the center of the screen.
+                        final Display display =
+                                getActivity().getWindowManager().getDefaultDisplay();
+                        centerPoint = new Point(display.getWidth() / 2, display.getHeight() / 2);
+                    }
+                    UIIntents.get()
+                            .launchPhoneCallActivity(getActivity(), phoneNumber, centerPoint);
                 }
-                UIIntents.get().launchPhoneCallActivity(getActivity(), phoneNumber, centerPoint);
                 return true;
 
             case R.id.action_archive:
diff --git a/src/com/android/messaging/ui/conversation/ConversationMessageView.java b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
index 20c986d..282e172 100644
--- a/src/com/android/messaging/ui/conversation/ConversationMessageView.java
+++ b/src/com/android/messaging/ui/conversation/ConversationMessageView.java
@@ -377,6 +377,7 @@
                 // FALL THROUGH HERE
 
             case MessageData.BUGLE_STATUS_OUTGOING_COMPLETE:
+            case MessageData.BUGLE_STATUS_OUTGOING_DELIVERED:
             case MessageData.BUGLE_STATUS_INCOMING_COMPLETE:
             default:
                 if (!mData.getCanClusterWithNextMessage()) {
@@ -707,6 +708,7 @@
                 textBottomPadding = textBottomPaddingDefault;
                 textLeftPadding = messageTextLeftRightPadding;
                 textRightPadding = messageTextLeftRightPadding;
+                mMessageTextView.setTextIsSelectable(isSelected());
             } else {
                 // Attachment(s) only
                 contentLeftPadding = incoming ? arrowWidth : 0;
@@ -732,6 +734,7 @@
             textTopMargin = 0;
             textTopPadding = textTopPaddingDefault;
             textBottomPadding = textBottomPaddingDefault;
+            mMessageTextView.setTextIsSelectable(isSelected());
             if (showArrow && incoming) {
                 textLeftPadding = messageTextLeftRightPadding + arrowWidth;
             } else {
@@ -1029,6 +1032,11 @@
     @Override
     public boolean onLongClick(final View view) {
         if (view == mMessageTextView) {
+            // Avoid trying to reselect the message
+            if (isSelected()) {
+                return false;
+            }
+
             // Preemptively handle the long click event on message text so it's not handled by
             // the link spans.
             return performLongClick();
@@ -1194,7 +1202,7 @@
                 // This touch event is a long click, preemptively handle this touch event so that
                 // the link span won't get a onClicked() callback.
                 mIsLongClick = false;
-                return true;
+                return false;
             }
 
             if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
diff --git a/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java b/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
index 8af9f75..5500ae8 100644
--- a/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
+++ b/src/com/android/messaging/ui/conversation/LaunchConversationActivity.java
@@ -61,7 +61,12 @@
         final Intent intent = getIntent();
         final String action = intent.getAction();
         if (Intent.ACTION_SENDTO.equals(action) || Intent.ACTION_VIEW.equals(action)) {
-            String[] recipients = UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
+            String[] recipients = null;
+            final String commaSeparatedRecipients =
+                    UriUtil.parseRecipientsFromSmsMmsUri(intent.getData());
+            if (commaSeparatedRecipients != null) {
+                recipients = commaSeparatedRecipients.split(",");
+            }
             final boolean haveAddress = !TextUtils.isEmpty(intent.getStringExtra(ADDRESS));
             final boolean haveEmail = !TextUtils.isEmpty(intent.getStringExtra(Intent.EXTRA_EMAIL));
             if (recipients == null && (haveAddress || haveEmail)) {
diff --git a/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java b/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
index 412177e..9ac6e6b 100644
--- a/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
+++ b/src/com/android/messaging/ui/conversationlist/ShareIntentActivity.java
@@ -81,6 +81,12 @@
     public void onAttachFragment(final Fragment fragment) {
         final Intent intent = getIntent();
         final String action = intent.getAction();
+
+        String sharedSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
+        if (sharedSubject == null) {
+            sharedSubject = intent.getStringExtra(Intent.EXTRA_TITLE);
+        }
+
         if (Intent.ACTION_SEND.equals(action)) {
             final Uri contentUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
             if (UriUtil.isFileUri(contentUri)) {
@@ -101,11 +107,14 @@
                     // Try to get text string from content uri.
                     sharedText = getTextStringFromContentUri(contentUri);
                 }
-                mDraftMessage =
-                        sharedText != null ? MessageData.createSharedMessage(sharedText) : null;
+                if (sharedText != null) {
+                    mDraftMessage = MessageData.createSharedMessage(sharedText, sharedSubject);
+                } else {
+                    mDraftMessage = null;
+                }
             } else if (PendingAttachmentData.isSupportedMediaType(contentType)) {
                 if (contentUri != null) {
-                    mDraftMessage = MessageData.createSharedMessage(null);
+                    mDraftMessage = MessageData.createSharedMessage(null, sharedSubject);
                     addSharedPartToDraft(contentType, contentUri);
                 } else {
                     mDraftMessage = null;
@@ -149,7 +158,8 @@
                 }
 
                 if (strBuffer.length() > 0 || !uriMap.isEmpty()) {
-                    mDraftMessage = MessageData.createSharedMessage(strBuffer.toString());
+                    mDraftMessage =
+                            MessageData.createSharedMessage(strBuffer.toString(), sharedSubject);
                     for (final Map.Entry<Uri, String> e : uriMap.entrySet()) {
                         addSharedPartToDraft(e.getValue(), e.getKey());
                     }
diff --git a/src/com/android/messaging/ui/mediapicker/ContactMediaChooser.java b/src/com/android/messaging/ui/mediapicker/ContactMediaChooser.java
new file mode 100644
index 0000000..a81ed08
--- /dev/null
+++ b/src/com/android/messaging/ui/mediapicker/ContactMediaChooser.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 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.messaging.ui.mediapicker;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.messaging.R;
+import com.android.messaging.datamodel.data.PendingAttachmentData;
+import com.android.messaging.ui.UIIntents;
+import com.android.messaging.util.ContactUtil;
+import com.android.messaging.util.ContentType;
+import com.android.messaging.util.SafeAsyncTask;
+
+/**
+ * Chooser which allows the user to select an existing contact from contacts apps on this device.
+ * Note that this chooser requires the Manifest.permission.READ_CONTACTS which is one of the miminum
+ * set of permissions for this app. Thus no case to request READ_CONTACTS permission on it actually.
+ */
+class ContactMediaChooser extends MediaChooser {
+    private View mEnabledView;
+    private View mMissingPermissionView;
+
+    ContactMediaChooser(final MediaPicker mediaPicker) {
+        super(mediaPicker);
+    }
+
+    @Override
+    public int getSupportedMediaTypes() {
+        return MediaPicker.MEDIA_TYPE_VCARD;
+    }
+
+    @Override
+    public int getIconResource() {
+        return R.drawable.ic_person_light;
+    }
+
+    @Override
+    public int getIconDescriptionResource() {
+        return R.string.mediapicker_contactChooserDescription;
+    }
+
+    @Override
+    int getActionBarTitleResId() {
+        return R.string.mediapicker_contact_title;
+    }
+
+    @Override
+    protected View createView(final ViewGroup container) {
+        final LayoutInflater inflater = getLayoutInflater();
+        final View view =
+                inflater.inflate(
+                        R.layout.mediapicker_contact_chooser,
+                        container /* root */,
+                        false /* attachToRoot */);
+        mEnabledView = view.findViewById(R.id.mediapicker_enabled);
+        mMissingPermissionView = view.findViewById(R.id.missing_permission_view);
+        mEnabledView.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(final View v) {
+                        // Launch an external picker to pick a contact as attachment.
+                        UIIntents.get().launchContactCardPicker(mMediaPicker);
+                    }
+                });
+        return view;
+    }
+
+    @Override
+    protected void setSelected(final boolean selected) {
+        super.setSelected(selected);
+        if (selected && !ContactUtil.hasReadContactsPermission()) {
+            mMediaPicker.requestPermissions(
+                    new String[] {Manifest.permission.READ_CONTACTS},
+                    MediaPicker.READ_CONTACT_PERMISSION_REQUEST_CODE);
+        }
+    }
+
+    @Override
+    protected void onRequestPermissionsResult(
+            final int requestCode, final String permissions[], final int[] grantResults) {
+        if (requestCode == MediaPicker.READ_CONTACT_PERMISSION_REQUEST_CODE) {
+            final boolean permissionGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
+            mEnabledView.setVisibility(permissionGranted ? View.VISIBLE : View.GONE);
+            mMissingPermissionView.setVisibility(permissionGranted ? View.GONE : View.VISIBLE);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == UIIntents.REQUEST_PICK_CONTACT_CARD
+                && resultCode == Activity.RESULT_OK) {
+            Uri contactUri = data.getData();
+            if (contactUri != null) {
+                String lookupKey = null;
+                try (final Cursor c = getContext().getContentResolver().query(
+                                        contactUri,
+                                        new String[] {Contacts.LOOKUP_KEY},
+                                        null,
+                                        null,
+                                        null)) {
+                    if (c != null) {
+                        c.moveToFirst();
+                        lookupKey = c.getString(0);
+                    }
+                }
+                final Uri vCardUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
+                if (vCardUri != null) {
+                    SafeAsyncTask.executeOnThreadPool(new Runnable() {
+                        @Override
+                        public void run() {
+                            final PendingAttachmentData pendingItem =
+                                    PendingAttachmentData.createPendingAttachmentData(
+                                            ContentType.TEXT_VCARD.toLowerCase(), vCardUri);
+                            mMediaPicker.dispatchPendingItemAdded(pendingItem);
+                        }
+                    });
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java b/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
index d6de128..c36467c 100644
--- a/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
+++ b/src/com/android/messaging/ui/mediapicker/DocumentImagePicker.java
@@ -15,7 +15,6 @@
  */
 package com.android.messaging.ui.mediapicker;
 
-import android.app.Activity;
 import android.app.Fragment;
 import android.content.Intent;
 import android.net.Uri;
@@ -79,30 +78,27 @@
      * Must be called from the fragment/activity's onActivityResult().
      */
     public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
-        if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
-                && resultCode == Activity.RESULT_OK) {
-            // Sometimes called after media item has been picked from the document picker.
-            String url = data.getStringExtra(EXTRA_PHOTO_URL);
+        // Sometimes called after media item has been picked from the document picker.
+        String url = data.getStringExtra(EXTRA_PHOTO_URL);
+        if (url == null) {
+            // we're using the builtin photo picker which supplies the return
+            // url as it's "data"
+            url = data.getDataString();
             if (url == null) {
-                // we're using the builtin photo picker which supplies the return
-                // url as it's "data"
-                url = data.getDataString();
-                if (url == null) {
-                    final Bundle extras = data.getExtras();
-                    if (extras != null) {
-                        final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
-                        if (uri != null) {
-                            url = uri.toString();
-                        }
+                final Bundle extras = data.getExtras();
+                if (extras != null) {
+                    final Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
+                    if (uri != null) {
+                        url = uri.toString();
                     }
                 }
             }
+        }
 
-            // Guard against null uri cases for when the activity returns a null/invalid intent.
-            if (url != null) {
-                final Uri uri = Uri.parse(url);
-                prepareDocumentForAttachment(uri);
-            }
+        // Guard against null uri cases for when the activity returns a null/invalid intent.
+        if (url != null) {
+            final Uri uri = Uri.parse(url);
+            prepareDocumentForAttachment(uri);
         }
     }
 
diff --git a/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java b/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java
index 48eaa5d..6cf509b 100644
--- a/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java
+++ b/src/com/android/messaging/ui/mediapicker/GalleryGridItemView.java
@@ -161,14 +161,9 @@
         } else {
             final String contentType = mData.getContentType();
             if (ContentType.isAudioType(contentType)) {
-                final Context context = getContext();
                 setBackgroundColor(
                         getResources().getColor(R.color.gallery_image_default_background));
-                mIcon.setImageDrawable(
-                        context.getContentResolver()
-                                .getTypeInfo(contentType)
-                                .getIcon()
-                                .loadDrawable(context));
+                mIcon.setImageResource(R.drawable.ic_music);
                 mIcon.setColorFilter(
                         ConversationDrawables.get().getConversationThemeColor(),
                         PorterDuff.Mode.SRC_IN);
diff --git a/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java b/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java
index c9b544d..6192c4f 100644
--- a/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java
+++ b/src/com/android/messaging/ui/mediapicker/GalleryMediaChooser.java
@@ -17,6 +17,8 @@
 package com.android.messaging.ui.mediapicker;
 
 import android.Manifest;
+import android.app.Activity;
+import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.MatrixCursor;
@@ -35,6 +37,9 @@
 import com.android.messaging.datamodel.data.MediaPickerData;
 import com.android.messaging.datamodel.data.MessagePartData;
 import com.android.messaging.datamodel.data.MediaPickerData.MediaPickerDataListener;
+import com.android.messaging.datamodel.data.PendingAttachmentData;
+import com.android.messaging.ui.UIIntents;
+import com.android.messaging.ui.mediapicker.DocumentImagePicker.SelectionListener;
 import com.android.messaging.util.Assert;
 import com.android.messaging.util.OsUtil;
 
@@ -47,9 +52,21 @@
     private GalleryGridView mGalleryGridView;
     private View mMissingPermissionView;
 
+    /** Handles picking a media from the document picker. */
+    private DocumentImagePicker mDocumentImagePicker;
+
     GalleryMediaChooser(final MediaPicker mediaPicker) {
         super(mediaPicker);
         mAdapter = new GalleryGridAdapter(Factory.get().getApplicationContext(), null);
+        mDocumentImagePicker = new DocumentImagePicker(mMediaPicker,
+                new SelectionListener() {
+                    @Override
+                    public void onDocumentSelected(final PendingAttachmentData data) {
+                        if (mBindingRef.isBound()) {
+                            mMediaPicker.dispatchPendingItemAdded(data);
+                        }
+                    }
+                });
     }
 
     @Override
@@ -148,7 +165,8 @@
 
     @Override
     public void onDocumentPickerItemClicked() {
-        mMediaPicker.launchDocumentPicker();
+        // Launch an external picker to pick item from document picker as attachment.
+        mDocumentImagePicker.launchPicker();
     }
 
     @Override
@@ -230,4 +248,13 @@
         mGalleryGridView.setVisibility(granted ? View.VISIBLE : View.GONE);
         mMissingPermissionView.setVisibility(granted ? View.GONE : View.VISIBLE);
     }
+
+    @Override
+    protected void onActivityResult(
+            final int requestCode, final int resultCode, final Intent data) {
+        if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
+                && resultCode == Activity.RESULT_OK) {
+            mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
+        }
+    }
 }
diff --git a/src/com/android/messaging/ui/mediapicker/MediaChooser.java b/src/com/android/messaging/ui/mediapicker/MediaChooser.java
index ef4067c..0549e5b 100644
--- a/src/com/android/messaging/ui/mediapicker/MediaChooser.java
+++ b/src/com/android/messaging/ui/mediapicker/MediaChooser.java
@@ -18,6 +18,7 @@
 
 import android.app.FragmentManager;
 import android.content.Context;
+import android.content.Intent;
 import androidx.appcompat.app.ActionBar;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -203,6 +204,9 @@
     public void stopTouchHandling() {
     }
 
+    protected void onActivityResult(
+            final int requestCode, final int resultCode, final Intent data) {}
+
     @Override
     public int getConversationSelfSubId() {
         return mMediaPicker.getConversationSelfSubId();
diff --git a/src/com/android/messaging/ui/mediapicker/MediaPicker.java b/src/com/android/messaging/ui/mediapicker/MediaPicker.java
index b8fce8f..c7faa33 100644
--- a/src/com/android/messaging/ui/mediapicker/MediaPicker.java
+++ b/src/com/android/messaging/ui/mediapicker/MediaPicker.java
@@ -16,7 +16,6 @@
 
 package com.android.messaging.ui.mediapicker;
 
-
 import android.app.Activity;
 import android.app.Fragment;
 import android.content.Context;
@@ -48,7 +47,6 @@
 import com.android.messaging.datamodel.data.DraftMessageData.DraftMessageSubscriptionDataProvider;
 import com.android.messaging.ui.BugleActionBarActivity;
 import com.android.messaging.ui.FixedViewPagerAdapter;
-import com.android.messaging.ui.mediapicker.DocumentImagePicker.SelectionListener;
 import com.android.messaging.util.AccessibilityUtil;
 import com.android.messaging.util.Assert;
 import com.android.messaging.util.UiUtils;
@@ -159,9 +157,6 @@
     @VisibleForTesting
     final Binding<MediaPickerData> mBinding = BindingBase.createBinding(this);
 
-    /** Handles picking a media from the document picker. */
-    private DocumentImagePicker mDocumentImagePicker;
-
     /** Provides subscription-related data to access per-subscription configurations. */
     private DraftMessageSubscriptionDataProvider mSubscriptionDataProvider;
 
@@ -179,6 +174,7 @@
             new CameraMediaChooser(this),
             new GalleryMediaChooser(this),
             new AudioMediaChooser(this),
+            new ContactMediaChooser(this),
         };
 
         mOpen = false;
@@ -203,15 +199,6 @@
     public void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mBinding.getData().init(getLoaderManager());
-        mDocumentImagePicker = new DocumentImagePicker(this,
-                new SelectionListener() {
-            @Override
-            public void onDocumentSelected(final PendingAttachmentData data) {
-                if (mBinding.isBound()) {
-                    dispatchPendingItemAdded(data);
-                }
-            }
-        });
     }
 
     @Override
@@ -295,7 +282,7 @@
     @Override
     public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
+        mSelectedChooser.onActivityResult(requestCode, resultCode, data);
     }
 
     @Override
@@ -710,13 +697,6 @@
         mPagerAdapter.resetState();
     }
 
-    /**
-     * Launch an external picker to pick item from document picker as attachment.
-     */
-    public void launchDocumentPicker() {
-        mDocumentImagePicker.launchPicker();
-    }
-
     public ImmutableBindingRef<MediaPickerData> getMediaPickerDataBinding() {
         return BindingBase.createBindingReference(mBinding);
     }
@@ -725,6 +705,7 @@
     protected static final int LOCATION_PERMISSION_REQUEST_CODE = 2;
     protected static final int RECORD_AUDIO_PERMISSION_REQUEST_CODE = 3;
     protected static final int GALLERY_PERMISSION_REQUEST_CODE = 4;
+    protected static final int READ_CONTACT_PERMISSION_REQUEST_CODE = 5;
 
     @Override
     public void onRequestPermissionsResult(
diff --git a/src/com/android/messaging/util/BuglePrefsKeys.java b/src/com/android/messaging/util/BuglePrefsKeys.java
index ae409bc..a30b5a3 100644
--- a/src/com/android/messaging/util/BuglePrefsKeys.java
+++ b/src/com/android/messaging/util/BuglePrefsKeys.java
@@ -66,6 +66,6 @@
      * The attempt number when retrying ProcessPendingMessagesAction
      */
     public static final String PROCESS_PENDING_MESSAGES_RETRY_COUNT
-            = "process_pending_retry";
+            = BuglePrefs.SHARED_PREFERENCES_PER_SUBSCRIPTION_PREFIX + "process_pending_retry";
 
 }
diff --git a/src/com/android/messaging/util/ConnectivityUtil.java b/src/com/android/messaging/util/ConnectivityUtil.java
index 49f6e0a..a7467e4 100644
--- a/src/com/android/messaging/util/ConnectivityUtil.java
+++ b/src/com/android/messaging/util/ConnectivityUtil.java
@@ -16,40 +16,44 @@
 
 package com.android.messaging.util;
 
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 
+import com.android.messaging.datamodel.data.ParticipantData;
+
+/**
+ * ConnectivityUtil listens to the network service state changes.
+ *
+ * On N and beyond, This class instance can be created via ConnectivityUtil(context, subId), use
+ * ConnectivityUtil(context) for others.
+ *
+ * Note that TelephonyManager has createForSubscriptionId() for a specific subId from N but listen()
+ * does not use the subId on the manager, and uses the default subId on PhoneStateListener. From O,
+ * the manager uses its' own subId in listen().
+ */
 public class ConnectivityUtil {
     // Assume not connected until informed differently
     private volatile int mCurrentServiceState = ServiceState.STATE_POWER_OFF;
 
     private final TelephonyManager mTelephonyManager;
-    private final Context mContext;
-    private final ConnectivityBroadcastReceiver mReceiver;
-    private final ConnectivityManager mConnMgr;
 
     private ConnectivityListener mListener;
-    private final IntentFilter mIntentFilter;
 
     public interface ConnectivityListener {
-        public void onConnectivityStateChanged(final Context context, final Intent intent);
-        public void onPhoneStateChanged(final Context context, int serviceState);
+        public void onPhoneStateChanged(int serviceState);
     }
 
     public ConnectivityUtil(final Context context) {
-        mContext = context;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
-        mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mReceiver = new ConnectivityBroadcastReceiver();
-        mIntentFilter = new IntentFilter();
-        mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+    }
+
+    public ConnectivityUtil(final Context context, final int subId) {
+        Assert.isTrue(OsUtil.isAtLeastN());
+        mTelephonyManager =
+                ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+                        .createForSubscriptionId(subId);
     }
 
     public int getCurrentServiceState() {
@@ -75,14 +79,7 @@
     private void onPhoneStateChanged(final int serviceState) {
         final ConnectivityListener listener = mListener;
         if (listener != null) {
-            listener.onPhoneStateChanged(mContext, serviceState);
-        }
-    }
-
-    private void onConnectivityChanged(final Context context, final Intent intent) {
-        final ConnectivityListener listener = mListener;
-        if (listener != null) {
-            listener.onConnectivityStateChanged(context, intent);
+            listener.onPhoneStateChanged(serviceState);
         }
     }
 
@@ -95,9 +92,6 @@
                 mTelephonyManager.listen(mPhoneStateListener,
                         PhoneStateListener.LISTEN_SERVICE_STATE);
             }
-            if (mConnMgr != null) {
-                mContext.registerReceiver(mReceiver, mIntentFilter);
-            }
         }
         mListener = listener;
     }
@@ -108,140 +102,7 @@
                 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
                 mCurrentServiceState = ServiceState.STATE_POWER_OFF;
             }
-            if (mConnMgr != null) {
-                mContext.unregisterReceiver(mReceiver);
-            }
         }
         mListener = null;
     }
-
-    /**
-     * Connectivity change broadcast receiver. This gets the network connectivity updates.
-     * In case we don't get the active connectivity when we first acquire the network,
-     * this receiver will notify us when it is connected, so to unblock the waiting thread
-     * which is sending the message.
-     */
-    public class ConnectivityBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(final Context context, final Intent intent) {
-            if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
-                return;
-            }
-
-            onConnectivityChanged(context, intent);
-        }
-    }
-
-    private int mSignalLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
-    // We use a separate instance than mPhoneStateListener because the lifetimes are different.
-    private final PhoneStateListener mSignalStrengthListener = new PhoneStateListener() {
-        @Override
-        public void onSignalStrengthsChanged(final SignalStrength signalStrength) {
-            mSignalLevel = getLevel(signalStrength);
-        }
-    };
-
-    public void registerForSignalStrength() {
-        mTelephonyManager.listen(
-                mSignalStrengthListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
-    }
-
-    public void unregisterForSignalStrength() {
-        mTelephonyManager.listen(mSignalStrengthListener, PhoneStateListener.LISTEN_NONE);
-    }
-
-    /**
-     * @param subId This is ignored because TelephonyManager does not support it.
-     * @return Signal strength as level 0..4
-     */
-    public int getSignalLevel(final int subId) {
-        return mSignalLevel;
-    }
-
-    private static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
-    private static final int SIGNAL_STRENGTH_POOR = 1;
-    private static final int SIGNAL_STRENGTH_MODERATE = 2;
-    private static final int SIGNAL_STRENGTH_GOOD = 3;
-    private static final int SIGNAL_STRENGTH_GREAT = 4;
-
-    private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
-    private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
-    private static final int GSM_SIGNAL_STRENGTH_MODERATE = 8;
-
-    private static int getLevel(final SignalStrength signalStrength) {
-        if (signalStrength.isGsm()) {
-            // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java
-
-            // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
-            // asu = 0 (-113dB or less) is very weak
-            // signal, its better to show 0 bars to the user in such cases.
-            // asu = 99 is a special case, where the signal strength is unknown.
-            final int asu = signalStrength.getGsmSignalStrength();
-            if (asu <= 2 || asu == 99) return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-            else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) return SIGNAL_STRENGTH_GREAT;
-            else if (asu >= GSM_SIGNAL_STRENGTH_GOOD) return SIGNAL_STRENGTH_GOOD;
-            else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE) return SIGNAL_STRENGTH_MODERATE;
-            else return SIGNAL_STRENGTH_POOR;
-        } else {
-            // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java
-
-            final int cdmaLevel = getCdmaLevel(signalStrength);
-            final int evdoLevel = getEvdoLevel(signalStrength);
-            if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                /* We don't know evdo, use cdma */
-                return getCdmaLevel(signalStrength);
-            } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                /* We don't know cdma, use evdo */
-                return getEvdoLevel(signalStrength);
-            } else {
-                /* We know both, use the lowest level */
-                return cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
-            }
-        }
-    }
-
-    /**
-     * Get cdma as level 0..4
-     */
-    private static int getCdmaLevel(final SignalStrength signalStrength) {
-        final int cdmaDbm = signalStrength.getCdmaDbm();
-        final int cdmaEcio = signalStrength.getCdmaEcio();
-        int levelDbm;
-        int levelEcio;
-        if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
-        else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
-        else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
-        else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
-        else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        // Ec/Io are in dB*10
-        if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
-        else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
-        else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
-        else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
-        else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        final int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
-        return level;
-    }
-    /**
-     * Get Evdo as level 0..4
-     */
-    private static int getEvdoLevel(final SignalStrength signalStrength) {
-        final int evdoDbm = signalStrength.getEvdoDbm();
-        final int evdoSnr = signalStrength.getEvdoSnr();
-        int levelEvdoDbm;
-        int levelEvdoSnr;
-        if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
-        else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
-        else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
-        else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
-        else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
-        else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
-        else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
-        else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
-        else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        final int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
-        return level;
-    }
 }
diff --git a/src/com/android/messaging/util/DebugUtils.java b/src/com/android/messaging/util/DebugUtils.java
index 0d689fe..6e33f48 100644
--- a/src/com/android/messaging/util/DebugUtils.java
+++ b/src/com/android/messaging/util/DebugUtils.java
@@ -302,11 +302,10 @@
                     final int length = dis.readInt();
                     final byte[] pdu = new byte[length];
                     dis.read(pdu, 0, length);
-                    if (format == null) {
-                        messagesTemp[i] = SmsMessage.createFromPdu(pdu);
-                    } else {
-                        messagesTemp[i] = SmsMessage.createFromPdu(pdu, format);
-                    }
+                    messagesTemp[i] =
+                            OsUtil.isAtLeastM()
+                                    ? SmsMessage.createFromPdu(pdu, format)
+                                    : SmsMessage.createFromPdu(pdu);
                 }
                 messages = messagesTemp;
             } catch (final FileNotFoundException e) {
diff --git a/src/com/android/messaging/util/FileUtil.java b/src/com/android/messaging/util/FileUtil.java
index 7d59aa7..71fbb4b 100644
--- a/src/com/android/messaging/util/FileUtil.java
+++ b/src/com/android/messaging/util/FileUtil.java
@@ -117,15 +117,11 @@
         }
     }
 
-    private static boolean isFileUri(final Uri uri) {
-        return TextUtils.equals(uri.getScheme(), ContentResolver.SCHEME_FILE);
-    }
-
     // Checks if the file is in /data, and don't allow any app to send personal information.
     // We're told it's possible to create world readable hardlinks to other apps private data
     // so we ban all /data file uris.
     public static boolean isInPrivateDir(Uri uri) {
-        if (!isFileUri(uri)) {
+        if (!UriUtil.isFileUri(uri)) {
             return false;
         }
         final File file = new File(uri.getPath());
diff --git a/src/com/android/messaging/util/ImageUtils.java b/src/com/android/messaging/util/ImageUtils.java
index a228417..e541d69 100644
--- a/src/com/android/messaging/util/ImageUtils.java
+++ b/src/com/android/messaging/util/ImageUtils.java
@@ -480,7 +480,7 @@
             if (MediaScratchFileProvider.isMediaScratchSpaceUri(mUri)) {
                 inputFilePath = MediaScratchFileProvider.getFileFromUri(mUri).getAbsolutePath();
             } else {
-                if (!TextUtils.equals(mUri.getScheme(), ContentResolver.SCHEME_FILE)) {
+                if (!UriUtil.isFileUri(mUri)) {
                     Assert.fail("Expected a GIF file uri, but actual uri = " + mUri.toString());
                 }
                 inputFilePath = mUri.getPath();
diff --git a/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java b/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java
index b1078d1..1a93e9d 100644
--- a/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java
+++ b/src/com/android/messaging/util/MediaMetadataRetrieverWrapper.java
@@ -74,7 +74,7 @@
     public void release() {
         try {
             mRetriever.release();
-        } catch (RuntimeException e) {
+        } catch (RuntimeException | IOException e) {
             LogUtil.e(LogUtil.BUGLE_TAG, "MediaMetadataRetriever.release failed", e);
         }
     }
diff --git a/src/com/android/messaging/util/PhoneUtils.java b/src/com/android/messaging/util/PhoneUtils.java
index 671c65e..08889b8 100644
--- a/src/com/android/messaging/util/PhoneUtils.java
+++ b/src/com/android/messaging/util/PhoneUtils.java
@@ -26,7 +26,6 @@
 import android.net.ConnectivityManager;
 import android.provider.Settings;
 import android.provider.Telephony;
-import androidx.collection.ArrayMap;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SmsManager;
 import android.telephony.SubscriptionInfo;
@@ -34,14 +33,18 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import androidx.collection.ArrayMap;
+import androidx.core.os.BuildCompat;
+
 import com.android.messaging.Factory;
 import com.android.messaging.R;
 import com.android.messaging.datamodel.data.ParticipantData;
 import com.android.messaging.sms.MmsSmsUtils;
+
 import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
 import com.google.i18n.phonenumbers.PhoneNumberUtil;
 import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -369,6 +372,9 @@
         @Override
         @SuppressWarnings("deprecation")
         public boolean isDataRoamingEnabled() {
+            if (BuildCompat.isAtLeastT()) {
+                return mTelephonyManager.isDataRoamingEnabled();
+            }
             boolean dataRoamingEnabled = false;
             final ContentResolver cr = mContext.getContentResolver();
             if (OsUtil.isAtLeastJB_MR1()) {
diff --git a/src/com/android/messaging/util/Trace.java b/src/com/android/messaging/util/Trace.java
index da1e87c..69910f7 100644
--- a/src/com/android/messaging/util/Trace.java
+++ b/src/com/android/messaging/util/Trace.java
@@ -101,7 +101,7 @@
     }
 
     /**
-     * Dummy class that we use if we aren't really tracing.
+     * Placeholder class that we use if we aren't really tracing.
      */
     private static final class TraceShim extends AbstractTrace {
         @Override
diff --git a/src/com/android/messaging/util/UriUtil.java b/src/com/android/messaging/util/UriUtil.java
index ceff50c..f92155f 100644
--- a/src/com/android/messaging/util/UriUtil.java
+++ b/src/com/android/messaging/util/UriUtil.java
@@ -49,6 +49,8 @@
     private static final String SCHEME_MMSTO = "smsto";
     public static final HashSet<String> SMS_MMS_SCHEMES = new HashSet<String>(
         Arrays.asList(SCHEME_SMS, SCHEME_MMS, SCHEME_SMSTO, SCHEME_MMSTO));
+    private static final String SCHEME_HTTP = "http";
+    private static final String SCHEME_HTTPS = "https";
 
     public static final String SCHEME_BUGLE = "bugle";
     public static final HashSet<String> SUPPORTED_SCHEME = new HashSet<String>(
@@ -94,8 +96,11 @@
         return TextUtils.equals(scheme, ContentResolver.SCHEME_ANDROID_RESOURCE);
     }
 
+    /** Returns whether the given Uri is a file. */
     public static boolean isFileUri(final Uri uri) {
-        return uri != null && TextUtils.equals(uri.getScheme(), ContentResolver.SCHEME_FILE);
+        return uri != null &&
+                uri.getScheme() != null &&
+                uri.getScheme().trim().toLowerCase().contains(ContentResolver.SCHEME_FILE);
     }
 
     /**
@@ -212,9 +217,10 @@
                 inputStream = context.getContentResolver().openInputStream(sourceUri);
             } else {
                 // The content is remote. Download it.
-                final URL url = new URL(sourceUri.toString());
-                final URLConnection ucon = url.openConnection();
-                inputStream = new BufferedInputStream(ucon.getInputStream());
+                inputStream = getInputStreamFromRemoteUri(sourceUri);
+                if (inputStream == null) {
+                    return null;
+                }
             }
             return persistContentToScratchSpace(inputStream);
         } catch (final Exception ex) {
@@ -231,6 +237,23 @@
         }
     }
 
+    @DoesNotRunOnMainThread
+    private static InputStream getInputStreamFromRemoteUri(final Uri sourceUri)
+            throws IOException {
+        if (isRemoteUri(sourceUri)) {
+            final URL url = new URL(sourceUri.toString());
+            final URLConnection ucon = url.openConnection();
+            return new BufferedInputStream(ucon.getInputStream());
+        } else {
+            return null;
+        }
+    }
+
+    private static boolean isRemoteUri(final Uri sourceUri) {
+        return sourceUri.getScheme().equals(SCHEME_HTTP)
+            || sourceUri.getScheme().equals(SCHEME_HTTPS);
+    }
+
     /**
      * Persist a piece of content from the given input stream, byte by byte to the specified
      * directory.
@@ -269,9 +292,10 @@
                 inputStream = context.getContentResolver().openInputStream(sourceUri);
             } else {
                 // The content is remote. Download it.
-                final URL url = new URL(sourceUri.toString());
-                final URLConnection ucon = url.openConnection();
-                inputStream = new BufferedInputStream(ucon.getInputStream());
+                inputStream = getInputStreamFromRemoteUri(sourceUri);
+                if (inputStream == null) {
+                    return null;
+                }
             }
             return persistContent(inputStream, outputDir, contentType);
         } catch (final Exception ex) {
@@ -324,14 +348,13 @@
     }
 
     /**
-     * Extract recipient destinations from Uri of form
-     *     SCHEME:destionation[,destination]?otherstuff
+     * Extract recipient destinations from Uri of form SCHEME:destination[,destination]?otherstuff
      * where SCHEME is one of the supported sms/mms schemes.
      *
      * @param uri sms/mms uri
-     * @return recipient destinations or null
+     * @return a comma-separated list of recipient destinations or null.
      */
-    public static String[] parseRecipientsFromSmsMmsUri(final Uri uri) {
+    public static String parseRecipientsFromSmsMmsUri(final Uri uri) {
         if (!isSmsMmsUri(uri)) {
             return null;
         }
@@ -341,7 +364,7 @@
         }
         // replaceUnicodeDigits will replace digits typed in other languages (i.e. Egyptian) with
         // the usual ascii equivalents.
-        return TextUtil.replaceUnicodeDigits(parts[0]).replace(';', ',').split(",");
+        return TextUtil.replaceUnicodeDigits(parts[0]).replace(';', ',');
     }
 
     /**
diff --git a/src/com/android/messaging/widget/WidgetConversationService.java b/src/com/android/messaging/widget/WidgetConversationService.java
index 4fd3934..5b8587b 100644
--- a/src/com/android/messaging/widget/WidgetConversationService.java
+++ b/src/com/android/messaging/widget/WidgetConversationService.java
@@ -330,6 +330,7 @@
                     // FALL THROUGH HERE
 
                 case MessageData.BUGLE_STATUS_OUTGOING_COMPLETE:
+                case MessageData.BUGLE_STATUS_OUTGOING_DELIVERED:
                 case MessageData.BUGLE_STATUS_INCOMING_COMPLETE:
                 default:
                     if (!message.getCanClusterWithNextMessage()) {
diff --git a/tests/Android.mk b/tests/Android.mk
index aaff57a..0c93ea8 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,6 +21,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := messagingtests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_INSTRUMENTATION_FOR := messaging
 
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 601bc8e..cd66c48 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
         <option name="test-file-name" value="messaging.apk" />
     </target_preparer>
 
-    <test class="com.android.tradefed.testtype.InstrumentationTest" >
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.messaging.test" />
         <option name="runner" value="android.test.InstrumentationTestRunner" />
     </test>
diff --git a/tests/src/com/android/messaging/FakeFactory.java b/tests/src/com/android/messaging/FakeFactory.java
index 41ede77..4c7c9de 100644
--- a/tests/src/com/android/messaging/FakeFactory.java
+++ b/tests/src/com/android/messaging/FakeFactory.java
@@ -46,6 +46,7 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class FakeFactory extends Factory {
@@ -121,7 +122,8 @@
 
                         @Override
                         public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-                            return null;
+                            // Return empty list
+                            return new ArrayList<>();
                         }
 
                         @Override
diff --git a/tests/src/com/android/messaging/datamodel/DataModelTest.java b/tests/src/com/android/messaging/datamodel/DataModelTest.java
index 71723a4..0a1bccd 100644
--- a/tests/src/com/android/messaging/datamodel/DataModelTest.java
+++ b/tests/src/com/android/messaging/datamodel/DataModelTest.java
@@ -35,10 +35,10 @@
 
     @Override
     protected void setUp() throws Exception {
-      super.setUp();
-      dataModel = new DataModelImpl(getTestContext());
-      FakeFactory.register(mContext)
-              .withDataModel(dataModel);
+        super.setUp();
+        FakeFactory factory = FakeFactory.register(mContext);
+        dataModel = new DataModelImpl(getTestContext());
+        factory.withDataModel(dataModel);
     }
 
     @SmallTest
diff --git a/tests/src/com/android/messaging/datamodel/FakeDataModel.java b/tests/src/com/android/messaging/datamodel/FakeDataModel.java
index 5e80eab..1daaa20 100644
--- a/tests/src/com/android/messaging/datamodel/FakeDataModel.java
+++ b/tests/src/com/android/messaging/datamodel/FakeDataModel.java
@@ -48,7 +48,6 @@
 import com.android.messaging.datamodel.data.SubscriptionListData;
 import com.android.messaging.datamodel.data.TestDataFactory;
 import com.android.messaging.datamodel.data.VCardContactItemData;
-import com.android.messaging.util.ConnectivityUtil;
 
 public class FakeDataModel extends DataModel {
     private BackgroundWorker mWorker;
@@ -58,7 +57,6 @@
     private ContactPickerData mContactPickerData;
     private MediaPickerData mMediaPickerData;
     private PeopleAndOptionsData mPeopleAndOptionsData;
-    private ConnectivityUtil mConnectivityUtil;
     private SyncManager mSyncManager;
     private SettingsData mSettingsData;
     private DraftMessageData mDraftMessageData;
@@ -102,11 +100,6 @@
         return this;
     }
 
-    public FakeDataModel withConnectivityUtil(final ConnectivityUtil connectivityUtil) {
-        mConnectivityUtil = connectivityUtil;
-        return this;
-    }
-
     public FakeDataModel withSyncManager(final SyncManager syncManager) {
         mSyncManager = syncManager;
         return this;
@@ -220,11 +213,6 @@
     }
 
     @Override
-    public ConnectivityUtil getConnectivityUtil() {
-        return mConnectivityUtil;
-    }
-
-    @Override
     public SyncManager getSyncManager() {
         return mSyncManager;
     }
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java b/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
index 97e0f10..4d1ad5d 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionServiceSystemTest.java
@@ -35,7 +35,6 @@
 import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener;
 import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
 
 import java.util.ArrayList;
@@ -291,8 +290,7 @@
         FakeFactory.registerWithFakeContext(getContext(), mContext)
                 .withDataModel(new FakeDataModel(mContext)
                 .withBackgroundWorkerForActionService(mWorker)
-                .withActionService(new ActionService())
-                .withConnectivityUtil(new StubConnectivityUtil(mContext)));
+                .withActionService(new ActionService()));
 
         mLoader = new StubLoader();
         setContext(Factory.get().getApplicationContext());
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java b/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
index 6f66fa9..8daafb8 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionServiceTest.java
@@ -35,7 +35,6 @@
 import com.android.messaging.datamodel.action.ActionMonitor.ActionStateChangedListener;
 import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
 
 import java.util.ArrayList;
@@ -81,7 +80,7 @@
     }
 
     /**
-     * For a dummy action verify that the service intent is constructed and queued correctly and
+     * For a chat action verify that the service intent is constructed and queued correctly and
      * that when that intent is processed it actually executes the action.
      */
     public void testChatServiceCreatesIntentAndExecutesAction() {
@@ -145,8 +144,7 @@
         FakeFactory.registerWithFakeContext(getContext(),mContext)
                 .withDataModel(new FakeDataModel(mContext)
                 .withBackgroundWorkerForActionService(mWorker)
-                .withActionService(new ActionService())
-                .withConnectivityUtil(new StubConnectivityUtil(mContext)));
+                .withActionService(new ActionService()));
 
         mStates = new ArrayList<Integer>();
         setContext(Factory.get().getApplicationContext());
diff --git a/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java b/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
index d72a0f9..00aa267 100644
--- a/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
+++ b/tests/src/com/android/messaging/datamodel/action/ActionTestHelpers.java
@@ -21,8 +21,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 
-import com.android.messaging.util.ConnectivityUtil;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -174,18 +172,4 @@
             }
         }
     }
-
-    public static class StubConnectivityUtil extends ConnectivityUtil {
-        public StubConnectivityUtil(final Context context) {
-            super(context);
-        }
-
-        @Override
-        public void registerForSignalStrength() {
-        }
-
-        @Override
-        public void unregisterForSignalStrength() {
-        }
-    }
 }
diff --git a/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java b/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
index 0405c90..e977aa7 100644
--- a/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
+++ b/tests/src/com/android/messaging/datamodel/action/ReadWriteDraftMessageActionTest.java
@@ -36,7 +36,6 @@
 import com.android.messaging.datamodel.MessagingContentProvider;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService;
 import com.android.messaging.datamodel.action.ActionTestHelpers.StubActionService.StubActionServiceCallLog;
-import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
 import com.android.messaging.datamodel.action.ReadDraftDataAction.ReadDraftDataActionListener;
 import com.android.messaging.datamodel.data.MessageData;
 import com.android.messaging.datamodel.data.MessagePartData;
@@ -473,8 +472,7 @@
 
         mService = new StubActionService();
         final FakeDataModel fakeDataModel = new FakeDataModel(context)
-                .withActionService(mService)
-                .withConnectivityUtil(new StubConnectivityUtil(context));
+                .withActionService(mService);
         FakeFactory.registerWithFakeContext(getTestContext(), context)
                 .withDataModel(fakeDataModel);
 
diff --git a/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java b/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java
index 1997a4d..47b65f1 100644
--- a/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java
+++ b/tests/src/com/android/messaging/ui/contact/ContactPickerFragmentTest.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.database.Cursor;
+import androidx.viewpager.widget.PagerAdapter;
 import androidx.viewpager.widget.ViewPager;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.view.View;
diff --git a/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java b/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java
index 797a2f1..658a1be 100644
--- a/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java
+++ b/tests/src/com/android/messaging/ui/conversation/ConversationFragmentTest.java
@@ -20,6 +20,7 @@
 import android.app.Fragment;
 import android.database.Cursor;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
 import android.test.suitebuilder.annotation.LargeTest;
 
 import com.android.messaging.FakeFactory;
diff --git a/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java b/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java
index eaf9338..4e7c2d2 100644
--- a/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java
+++ b/tests/src/com/android/messaging/ui/mediapicker/MediaPickerTest.java
@@ -103,7 +103,7 @@
         final View view = mediaPicker.getView();
         assertNotNull(view);
         final ViewGroup tabStrip = (ViewGroup) view.findViewById(R.id.mediapicker_tabstrip);
-        assertEquals(tabStrip.getChildCount(), 3);
+        assertEquals(tabStrip.getChildCount(), 4);
         for (int i = 0; i < tabStrip.getChildCount(); i++) {
             final ImageButton tabButton = (ImageButton) tabStrip.getChildAt(i);
             assertEquals(View.VISIBLE, tabButton.getVisibility());
@@ -120,7 +120,7 @@
         final View view = mediaPicker.getView();
         assertNotNull(view);
         final ViewGroup tabStrip = (ViewGroup) view.findViewById(R.id.mediapicker_tabstrip);
-        assertEquals(tabStrip.getChildCount(), 3);
+        assertEquals(tabStrip.getChildCount(), 4);
         for (int i = 0; i < tabStrip.getChildCount(); i++) {
             final ImageButton tabButton = (ImageButton) tabStrip.getChildAt(i);
             assertEquals(i == 0, tabButton.isSelected());