Merge QP1A.190212.003

Change-Id: Id53bfa21dae871b31f2606f8f9fc1531f326d72f
diff --git a/Android.mk b/Android.mk
index 2a3a485..9e914c3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,8 @@
     guava \
     libphonenumber
 
+LOCAL_JAVA_LIBRARIES := telephony-common voip-common
+
 LOCAL_USE_AAPT2 := true
 
 LOCAL_AAPT_FLAGS := \
@@ -43,7 +45,8 @@
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
-LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
+#LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 21
 
 include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b0783d4..1ad051b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -515,6 +515,34 @@
             android:theme="@style/PeopleThemeAppCompat.FullScreenDialog.SimImportActivity"/>
 
 
+        <activity
+            android:name=".activities.MultiPickContactsActivity"
+            android:windowSoftInputMode="stateHidden|adjustResize"
+            android:screenOrientation="nosensor"
+            android:theme="@style/PeopleTheme"
+            android:configChanges="orientation|keyboardHidden|layoutDirection|fontScale|locale">
+            <intent-filter>
+                <action android:name="com.android.contacts.action.MULTI_PICK" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/person"/>
+                <data android:mimeType="vnd.android.cursor.dir/contact"/>
+                <data android:mimeType="vnd.android.cursor.dir/raw_contact"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.contacts.action.MULTI_PICK_CONTACT" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/person"/>
+                <data android:mimeType="vnd.android.cursor.dir/contact"/>
+                <data android:mimeType="vnd.android.cursor.dir/raw_contact"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.contacts.action.MULTI_PICK_EMAIL"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeType="vnd.android.cursor.dir/person"/>
+                <data android:mimeType="vnd.android.cursor.dir/contact"/>
+                <data android:mimeType="vnd.android.cursor.dir/raw_contact"/>
+            </intent-filter>
+        </activity>
         <service
             android:name=".vcard.VCardService"
             android:exported="false"/>
diff --git a/res/drawable-hdpi/ic_contact_picture_sim.png b/res/drawable-hdpi/ic_contact_picture_sim.png
new file mode 100644
index 0000000..7b6cc26
--- /dev/null
+++ b/res/drawable-hdpi/ic_contact_picture_sim.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_sim_1.png b/res/drawable-hdpi/ic_contact_picture_sim_1.png
new file mode 100644
index 0000000..ec2ebcb
--- /dev/null
+++ b/res/drawable-hdpi/ic_contact_picture_sim_1.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture_sim_2.png b/res/drawable-hdpi/ic_contact_picture_sim_2.png
new file mode 100644
index 0000000..3b68c68
--- /dev/null
+++ b/res/drawable-hdpi/ic_contact_picture_sim_2.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_sim.png b/res/drawable-mdpi/ic_contact_picture_sim.png
new file mode 100644
index 0000000..dd63d11
--- /dev/null
+++ b/res/drawable-mdpi/ic_contact_picture_sim.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_sim_1.png b/res/drawable-mdpi/ic_contact_picture_sim_1.png
new file mode 100644
index 0000000..4793a07
--- /dev/null
+++ b/res/drawable-mdpi/ic_contact_picture_sim_1.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture_sim_2.png b/res/drawable-mdpi/ic_contact_picture_sim_2.png
new file mode 100644
index 0000000..665a671
--- /dev/null
+++ b/res/drawable-mdpi/ic_contact_picture_sim_2.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_sim.png b/res/drawable-xhdpi/ic_contact_picture_sim.png
new file mode 100644
index 0000000..a39b7df
--- /dev/null
+++ b/res/drawable-xhdpi/ic_contact_picture_sim.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_sim_1.png b/res/drawable-xhdpi/ic_contact_picture_sim_1.png
new file mode 100644
index 0000000..300c028
--- /dev/null
+++ b/res/drawable-xhdpi/ic_contact_picture_sim_1.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_picture_sim_2.png b/res/drawable-xhdpi/ic_contact_picture_sim_2.png
new file mode 100644
index 0000000..c7ce422
--- /dev/null
+++ b/res/drawable-xhdpi/ic_contact_picture_sim_2.png
Binary files differ
diff --git a/res/drawable/expand_more.xml b/res/drawable/expand_more.xml
new file mode 100644
index 0000000..2d8b748
--- /dev/null
+++ b/res/drawable/expand_more.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M17,9.5l-5,5l-5-5H17z" />
+
+</vector>
diff --git a/res/drawable/quantum_ic_arrow_back_vd_theme_24.xml b/res/drawable/quantum_ic_arrow_back_vd_theme_24.xml
index 174cf12..bb87260 100644
--- a/res/drawable/quantum_ic_arrow_back_vd_theme_24.xml
+++ b/res/drawable/quantum_ic_arrow_back_vd_theme_24.xml
@@ -21,6 +21,6 @@
         android:viewportHeight="24.0"
         android:tint="?attr/colorControlNormal">
     <path
-        android:fillColor="@android:color/white"
+        android:fillColor="@android:color/darker_gray"
         android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
 </vector>
diff --git a/res/layout/action_mode.xml b/res/layout/action_mode.xml
new file mode 100644
index 0000000..6505a9b
--- /dev/null
+++ b/res/layout/action_mode.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/navigation_bar"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal" >
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent" >
+        <ImageView android:layout_gravity="end|center"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:src="@drawable/expand_more"
+            android:tint="@android:color/white" />
+        <Button android:id="@+id/selection_menu"
+            style="?android:attr/actionButtonStyle"
+            android:layout_width="@dimen/action_bar_select_button_width"
+            android:layout_height="match_parent"
+            android:background="?android:attr/selectableItemBackgroundBorderless"
+            android:divider="?android:attr/listDividerAlertDialog"
+            android:gravity="center_vertical|left"
+            android:paddingEnd="@dimen/action_bar_select_button_padding_end"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+            android:textColor="@color/action_bar_select_button_text_color"/>
+    </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/custom_pick_action_bar.xml b/res/layout/custom_pick_action_bar.xml
new file mode 100644
index 0000000..37603d9
--- /dev/null
+++ b/res/layout/custom_pick_action_bar.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dip"
+    android:layout_height="0dip" >
+
+    <View
+        android:layout_width="1px"
+        android:layout_height="1px"
+        android:focusable="true"
+        android:focusableInTouchMode="true" >
+
+        <requestFocus />
+    </View>
+
+    <EditText
+        android:id="@+id/search_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/search_box_icon_size"
+        android:layout_marginLeft="@dimen/search_box_text_left_margin"
+        android:background="@null"
+        android:fontFamily="@string/search_font_family"
+        android:imeOptions="flagNoExtractUi"
+        android:inputType="textFilter"
+        android:singleLine="true"
+        android:textColor="@color/multi_pick_search_view_edit_text_color"
+        android:textColorHint="@color/multi_pick_search_view_edit_hint_text_color"
+        android:textCursorDrawable="@null"
+        android:textSize="@dimen/contacts_text_size" />
+
+</FrameLayout>
diff --git a/res/layout/multi_pick_activity.xml b/res/layout/multi_pick_activity.xml
new file mode 100644
index 0000000..fee1624
--- /dev/null
+++ b/res/layout/multi_pick_activity.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/multi_pick_frame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/multi_pick_top_divider"
+        android:layout_width="match_parent"
+        android:layout_height="2dp"
+        android:background="@color/divider_line_color"
+        android:elevation="@dimen/multi_pick_top_divider_elevation"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" >
+        <FrameLayout
+            android:id="@+id/pick_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="@color/divider_line_color" />
+
+    <TextView
+        android:id="@+id/btn_ok"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/multi_pick_linear_layout_for_button_height"
+        android:layout_gravity="bottom"
+        android:enabled="false"
+        android:background="?android:attr/selectableItemBackgroundBorderless"
+        android:fontFamily="sans-serif-medium"
+        android:gravity="center"
+        android:text="@string/btn_ok"
+        android:textSize="@dimen/contacts_text_size" />
+
+</LinearLayout>
diff --git a/res/layout/multi_pick_contact_item.xml b/res/layout/multi_pick_contact_item.xml
new file mode 100644
index 0000000..be78f7a
--- /dev/null
+++ b/res/layout/multi_pick_contact_item.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/multi_pick_contacts_item_height"
+    android:descendantFocusability="blocksDescendants"
+    android:minHeight="?android:attr/listPreferredItemHeight" >
+
+    <TextView
+        android:id="@+id/section_index"
+        android:layout_width="@dimen/contact_section_index_width"
+        android:layout_height="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="16dp"
+        android:textColor="@color/contact_section_index_color"
+        android:textSize="@dimen/contact_section_text_size"
+        android:visibility="gone" />
+
+    <ImageView
+        android:id="@+id/pick_contact_photo"
+        android:layout_width="@dimen/pick_contact_photo_size"
+        android:layout_height="@dimen/pick_contact_photo_size"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="@dimen/multi_pick_linear_layout_padding"
+        android:layout_toRightOf="@+id/section_index"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="@dimen/multi_pick_linear_layout_padding"
+        android:layout_toEndOf="@+id/pick_contact_photo"
+        android:gravity="center_vertical"
+        android:orientation="horizontal" >
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center_vertical"
+            android:orientation="vertical"
+            android:paddingEnd="@dimen/multi_pick_linear_layout_padding" >
+
+            <TextView
+                android:id="@+id/pick_contact_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignTop="@+id/section_index"
+                android:layout_toEndOf="@+id/pick_contact_photo"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textColor="@color/contacts_or_name_text_color"
+                android:textSize="@dimen/contacts_text_size" />
+
+            <TextView
+                android:id="@+id/pick_contact_number"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="@color/multi_pick_label_text_color"
+                android:textSize="@dimen/phone_or_number_text_size" />
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/label"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="@dimen/multi_pick_linear_layout_padding"
+            android:gravity="center"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/multi_pick_label_text_color"
+            android:textSize="@dimen/multi_pick_label_text_size" />
+        <CheckBox
+            android:id="@+id/pick_contact_check"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="38dip"
+            android:layout_centerVertical="true"
+            android:layout_alignParentRight="true"
+            android:clickable = "false">
+	</CheckBox>
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/res/layout/multi_pick_contacts_fragment.xml b/res/layout/multi_pick_contacts_fragment.xml
new file mode 100644
index 0000000..96ff727
--- /dev/null
+++ b/res/layout/multi_pick_contacts_fragment.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#fafafa"
+    android:orientation="vertical">
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1.0"
+        android:divider="@null"
+        android:fastScrollEnabled="true" />
+
+</LinearLayout>
diff --git a/res/layout/popup_list_item.xml b/res/layout/popup_list_item.xml
new file mode 100644
index 0000000..517f42b
--- /dev/null
+++ b/res/layout/popup_list_item.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/popup_list_title"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+    android:singleLine="true"
+    android:gravity="center_vertical"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:minHeight="48dp"
+    android:minWidth="124dp"
+    android:textColor="@android:color/black"
+/>
diff --git a/res/menu/quickcontact.xml b/res/menu/quickcontact.xml
index 7d87708..807c1a4 100644
--- a/res/menu/quickcontact.xml
+++ b/res/menu/quickcontact.xml
@@ -43,6 +43,18 @@
         android:alphabeticShortcut="s" />
 
     <item
+        android:id="@+id/menu_copy_to_phone"
+        android:title="@string/menu_copyTo" />
+
+    <item
+        android:id="@+id/menu_copy_to_sim1"
+        android:title="@string/menu_copyTo" />
+
+    <item
+        android:id="@+id/menu_copy_to_sim2"
+        android:title="@string/menu_copyTo" />
+
+    <item
         android:id="@+id/menu_create_contact_shortcut"
         android:title="@string/menu_create_contact_shortcut" />
 
diff --git a/res/menu/search_menu.xml b/res/menu/search_menu.xml
index bf4b5dd..56a55d9 100644
--- a/res/menu/search_menu.xml
+++ b/res/menu/search_menu.xml
@@ -19,5 +19,6 @@
         android:id="@+id/menu_search"
         android:icon="@drawable/quantum_ic_search_vd_theme_24"
         android:title="@string/hint_findContacts"
+        android:showAsAction="always"
         contacts:showAsAction="always" />
 </menu>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index e5007f0..6b96704 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -511,4 +511,36 @@
     <string name="contacts_default_notification_channel" msgid="4754058700611188581">"通知"</string>
     <string name="yes_button" msgid="1268479086848288060">"是"</string>
     <string name="no_button" msgid="5742815694687835125">"否"</string>
+    <!-- no translation found for contacts_default_notification_channel (4754058700611188581) -->
+    <skip />
+    <string name="select_all">"全选"</string>
+    <string name="btn_ok">"确定"</string>
+    <string name="btn_cancel">"取消"</string>
+    <string name="deleteConfirmation_title">"要删除联系人吗?"</string>
+    <string name="ContactMultiDeleteConfirmation">"将会删除这些联系人。"</string>
+    <string name="delete_contacts_title">"删除联系人"</string>
+    <string name="delete_contacts_message">"正在删除..."</string>
+    <string name="delete_termination">"删除结束"</string>
+    <string name="too_many_contacts_add_to_group">联系人过多,应该少于<xliff:g id="count">%d</xliff:g>个</string>
+    <string name="contacts_selected">已选中%d个</string>
+
+    <string name="summary_count_numbers">%s 个人</string>
+    <!-- The hint text in search-view.-->
+    <string name="search_menu_search">搜索</string>
+    <string name="too_many_contacts_add">联系人太多,应该少于 <xliff:g id="count">%d</xliff:g>个</string>
+    <string name="menu_select_all" msgid="621719255150713545">"全选"</string>
+    <string name="menu_select_none" msgid="7093222469852132345">"取消全选"</string>
+    <string name="export_to_sim">"导出到 SIM 卡"</string>
+    <string name="exporting">"正在导出"</string>
+    <string name="progressdialog_cancel">取消</string>
+    <string name="sim_contact_export_failed">导出失败, 名字: <xliff:g id="name">%1$s</xliff:g>, 号码: <xliff:g id="number">%2$s</xliff:g>, 邮箱: <xliff:g id="email">%3$s</xliff:g></string>
+    <string name="export_sim_card_full">"SIM卡已满,<xliff:g id="insertCount">%d</xliff:g>条成功导出"</string>
+    <string name="export_finished">"导出结束"</string>
+    <string name="export_cancelled">导出操作被取消,<xliff:g id="insertCount">%s</xliff:g>条成功导出</string>
+    <string name="export_no_phone_or_email">导出失败, <xliff:g id="name">%s</xliff:g>没有电话号码或者邮箱地址</string>
+    <string name="copy_done">复制成功!</string>
+    <string name="copy_failure">复制失败!</string>
+    <string name="card_no_space">卡记录已满,部分信息未复制</string>
+    <string name="menu_copyTo">"复制到"</string>
+    <string name="no_empty_email_in_usim">"卡上电子邮件空间已满,部分邮件信息将不会复制 "</string>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index aeba877..d6d15a1 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -255,5 +255,29 @@
 
     <!-- Highlight color used in places such as ripples -->
     <color name="control_highlight_color">#1A000000</color>
+    <!-- The text color of contacts item's label. -->
+    <color name="multi_pick_label_text_color">#91000000</color>
+    <!-- Contacts item's text-colors. -->
+    <color name="contacts_or_name_text_color">#DF000000</color>
+    <!-- Confirm or disable button's text-colors in multi-pick. -->
+    <color name="ok_or_clear_button_disable_color">#44000000</color>
+    <color name="ok_or_clear_button_normal_color">#ff03a9f4</color>
+    <!-- Horizontal separator line should be 10% dark in the light theme. -->
+    <color name="divider_line_color">#1A000000</color>
+
+    <!-- Search-box's colors.  -->
+    <color name="searchbox_phone_text_color">#db000000</color>
+    <color name="searchbox_phone_background_color">#ffffffff</color>
+    <color name="searchbox_phone_hint_text_color">#42000000</color>
+
+    <!-- The selection button's expand window text-color in actionbar. -->
+    <color name="action_bar_expand_text_color">#FF000000</color>
+    <color name="action_bar_select_button_text_color">#FFFFFFFF</color>
+    <color name="multi_pick_search_view_edit_text_color">#DBFFFFFF</color>
+    <color name="multi_pick_search_view_edit_hint_text_color">#42FFFFFF</color>
+    <!-- Contact section index's text-color. -->
+    <color name="contact_section_index_color">#8C000000</color>
+    <!-- Popup select button background. -->
+    <color name="select_popup_button_background">#FFFFFFFF</color>
 
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 6d100ac..f5dee49 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -382,4 +382,51 @@
     <!-- Side padding within the navigation drawer -->
     <dimen name="drawer_label_header_end_padding">24dp</dimen>
 
+    <!-- Multi-pick contacts' list item height. -->
+    <dimen name="multi_pick_contacts_item_height">72dp</dimen>
+
+    <!-- The linear-layout's padding-end of Multi-pick list item. -->
+    <dimen name="multi_pick_linear_layout_padding">16dp</dimen>
+
+    <dimen name="multi_pick_linear_layout_for_button_height">54dp</dimen>
+    <!-- Label's text-size in contacts item. e.g.Home, Mobile, Custom. -->
+    <dimen name="multi_pick_label_text_size">14sp</dimen>
+
+    <!-- Phone number text-size in one contacts item. -->
+    <dimen name="phone_or_number_text_size">14sp</dimen>
+    <!-- Contacts name text-size in one contacts item. -->
+    <dimen name="contacts_text_size">16sp</dimen>
+
+    <!-- The index header view's width in multi-pick. e.g.A, B, C, #.-->
+    <dimen name="contact_section_index_width">40dp</dimen>
+    <!-- The index header text-size. -->
+    <dimen name="contact_section_text_size">24sp</dimen>
+    <!-- The index header's margin-start. -->
+    <dimen name="contact_section_index_margin_start">16dp</dimen>
+    <!-- The index header's margin-end. -->
+    <dimen name="contact_section_index_margin_end">42dp</dimen>
+    <dimen name="pick_contact_photo_size">40dp</dimen>
+
+    <!-- "OK" or "CLEAR" button's margin-end. -->
+    <dimen name="ok_or_clear_button_margin_end">14dp</dimen>
+
+    <!-- Navigation view's margin-start in actionbar. -->
+    <dimen name="navigation_image_view_margin_start">16dp</dimen>
+    <!-- Navigation view's margin-end in actionbar. -->
+    <dimen name="navigation_image_view_margin_end">20dp</dimen>
+
+    <!-- Selection button's width in actionbar.  -->
+    <dimen name="action_bar_select_button_width">80dp</dimen>
+    <!-- Selection button's padding-end in actionbar. -->
+    <dimen name="action_bar_select_button_padding_end">25dp</dimen>
+
+    <!-- Popup window's width after click selection button. -->
+    <dimen name="action_bar_select_view_width">124dp</dimen>
+    <!-- Selected the same contact item's height. -->
+    <dimen name="pick_contact_same_item_height">54dp</dimen>
+    <!-- Select the first contact height in some same contacts. -->
+    <dimen name="pick_contact_first_item_height">72dp</dimen>
+    <!-- The elevation multi-pick activity's top divider. -->
+    <dimen name="multi_pick_top_divider_elevation">10dp</dimen>
+    <dimen name="header_listview_height">12dp</dimen>
 </resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index a4b8b4b..42760f6 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -20,6 +20,7 @@
     <item type="id" name="dialog_sync_add"/>
 
     <!-- For ContactDeletionInteraction -->
+    <item type="id" name="dialog_delete_contact_confirmation"/>
     <item type="id" name="dialog_delete_contact_loader_id" />
 
     <!-- For ContactMultiDeletionInteraction -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 56c4aaa..586c66e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1533,4 +1533,36 @@
     <!-- Text of Negative Button in dialog -->
     <string name="no_button">No</string>
 
-</resources>
\ No newline at end of file
+    <string name="select_all">All</string>
+    <string name="btn_ok">Ok</string>
+    <string name="btn_cancel">Cancel</string>
+    <string name="deleteConfirmation_title">Delete contact?</string>
+    <string name="ContactMultiDeleteConfirmation">These contacts will be deleted.</string>
+    <string name="delete_termination">"Finished deleting"</string>
+    <string name="delete_contacts_title">Delete Contacts</string>
+    <string name="delete_contacts_message">Deleting Contacts ...</string>
+    <string name="contacts_selected">%d selected</string>
+    <string name="summary_count_numbers">%s numbers</string>
+    <string name="search_menu_search">Search</string>
+    <string name="too_many_contacts_add">Too many contacts, that should be less than <xliff:g id="count">%d</xliff:g></string>
+    <!-- Label to select all contacts in multiple picker -->
+    <string name="menu_select_all">"Select all"</string>
+
+    <!-- Label to clear all selection in multiple picker -->
+    <string name="menu_select_none">"Unselect all"</string>
+    <!-- Action that exports all contacts to SIM -->
+    <string name="export_to_sim">Export to SIM card</string>
+    <string name="progressdialog_cancel">Cancel</string>
+    <string name="sim_contact_export_failed">Export failed, Contact name: <xliff:g id="name">%1$s</xliff:g>, number: <xliff:g id="number">%2$s</xliff:g>, email: <xliff:g id="email">%3$s</xliff:g></string>
+    <string name="export_sim_card_full">Sim Card is full, <xliff:g id="insertCount">%d</xliff:g> items are exported</string>
+    <string name="sim_card_full">Error, Sim Card is full.</string>
+    <string name="export_finished">Export finished</string>
+    <string name="export_cancelled">Export is canceled, <xliff:g id="insertCount">%s</xliff:g>items are exported</string>
+    <string name="export_no_phone_or_email">Export failed, <xliff:g id="name">%s</xliff:g>has not phone number or email address</string>
+    <string name="exporting">Exporting</string>
+    <string name="copy_done">Copy done!</string>
+    <string name="copy_failure">Copy Failed!</string>
+    <string name="card_no_space">Card has no space ,some record copy failed</string>
+    <string name="menu_copyTo">"Copy to "</string>
+    <string name="no_empty_email_in_usim">"Email is full, some record copy failed "</string>
+</resources>
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index e5f808d..8b40613 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts;
 
+import android.accounts.Account;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.ContentResolver;
@@ -123,17 +124,23 @@
      */
     public static Drawable getDefaultAvatarDrawableForContact(Resources resources, boolean hires,
             DefaultImageRequest defaultImageRequest) {
+        return getDefaultAvatarDrawableForContact(resources, hires,
+                defaultImageRequest, null);
+    }
+
+    public static Drawable getDefaultAvatarDrawableForContact(Resources resources,
+            boolean hires, DefaultImageRequest defaultImageRequest, Account account) {
         if (defaultImageRequest == null) {
             if (sDefaultLetterAvatar == null) {
                 // Cache and return the letter tile drawable that is created by a null request,
                 // so that it doesn't have to be recreated every time it is requested again.
                 sDefaultLetterAvatar = LetterTileDefaultImageProvider.getDefaultImageForContact(
-                        resources, null);
+                        resources, null, account);
             }
             return sDefaultLetterAvatar;
         }
         return LetterTileDefaultImageProvider.getDefaultImageForContact(resources,
-                defaultImageRequest);
+                defaultImageRequest, account);
     }
 
     /**
@@ -375,6 +382,10 @@
          */
         public abstract void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
                 DefaultImageRequest defaultImageRequest);
+
+        public void applyDefaultImage(ImageView view, Account account, int extent,
+            boolean darkTheme, DefaultImageRequest defaultImageRequest) {
+        }
     }
 
     /**
@@ -386,14 +397,20 @@
         @Override
         public void applyDefaultImage(ImageView view, int extent, boolean darkTheme,
                 DefaultImageRequest defaultImageRequest) {
+            applyDefaultImage(view, null, extent, darkTheme, defaultImageRequest);
+        }
+
+        @Override
+        public void applyDefaultImage(ImageView view, Account account, int extent,
+                boolean darkTheme, DefaultImageRequest defaultImageRequest) {
             final Drawable drawable = getDefaultImageForContact(view.getResources(),
-                    defaultImageRequest);
+                    defaultImageRequest, account);
             view.setImageDrawable(drawable);
         }
 
         public static Drawable getDefaultImageForContact(Resources resources,
-                DefaultImageRequest defaultImageRequest) {
-            final LetterTileDrawable drawable = new LetterTileDrawable(resources);
+                DefaultImageRequest defaultImageRequest, Account account) {
+            final LetterTileDrawable drawable = new LetterTileDrawable(resources, account);
             if (defaultImageRequest != null) {
                 // If the contact identifier is null or empty, fallback to the
                 // displayName. In that case, use {@code null} for the contact's
@@ -460,8 +477,8 @@
      * it is displayed immediately.  Otherwise a request is sent to load the photo
      * from the database.
      */
-    public abstract void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
-            boolean isCircular, DefaultImageRequest defaultImageRequest,
+    public abstract void loadThumbnail(ImageView view, long photoId, Account account,
+            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
             DefaultImageProvider defaultProvider);
 
     /**
@@ -470,9 +487,22 @@
      */
     public final void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
             boolean isCircular, DefaultImageRequest defaultImageRequest) {
-        loadThumbnail(view, photoId, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
+        loadThumbnail(view, photoId, null, darkTheme, isCircular, defaultImageRequest,
+                DEFAULT_AVATAR);
     }
 
+    public final void loadThumbnail(ImageView view, long photoId, Account account,
+            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest) {
+        loadThumbnail(view, photoId, account, darkTheme, isCircular,
+                defaultImageRequest, DEFAULT_AVATAR);
+    }
+
+    public final void loadThumbnail(ImageView view, long photoId, boolean darkTheme,
+            boolean isCircular, DefaultImageRequest defaultImageRequest,
+            DefaultImageProvider defaultProvider) {
+        loadThumbnail(view, photoId, null, darkTheme, isCircular,
+            defaultImageRequest, defaultProvider);
+     }
 
     /**
      * Load photo into the supplied image view. If the photo is already cached,
@@ -491,9 +521,9 @@
      * @param defaultProvider The provider of default avatars (this is used if photoUri doesn't
      * refer to an existing image)
      */
-    public abstract void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
-            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
-            DefaultImageProvider defaultProvider);
+    public abstract void loadPhoto(ImageView view, Uri photoUri, Account account,
+            int requestedExtent, boolean darkTheme, boolean isCircular, DefaultImageRequest
+            defaultImageRequest, DefaultImageProvider defaultProvider);
 
     /**
      * Calls {@link #loadPhoto(ImageView, Uri, int, boolean, DefaultImageRequest,
@@ -505,10 +535,22 @@
      */
     public final void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
             boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest) {
-        loadPhoto(view, photoUri, requestedExtent, darkTheme, isCircular,
+        loadPhoto(view, photoUri, null, requestedExtent, darkTheme, isCircular,
                 defaultImageRequest, DEFAULT_AVATAR);
     }
 
+    public final void loadPhoto(ImageView view, Uri photoUri, Account account, int requestedExtent,
+            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest) {
+        loadPhoto(view, photoUri, account, requestedExtent, darkTheme, isCircular,
+                defaultImageRequest, DEFAULT_AVATAR);
+    }
+
+    public final void loadPhoto(ImageView view, Uri photoUri, int requestedExtent,
+            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
+            DefaultImageProvider defaultProvider) {
+        loadPhoto(view, photoUri, null, requestedExtent, darkTheme, isCircular,
+                defaultImageRequest, defaultProvider);
+    }
     /**
      * Calls {@link #loadPhoto(ImageView, Uri, boolean, boolean, DefaultImageRequest,
      * DefaultImageProvider)} with {@link #DEFAULT_AVATAR} and with the assumption, that
@@ -519,7 +561,15 @@
      */
     public final void loadDirectoryPhoto(ImageView view, Uri photoUri, boolean darkTheme,
             boolean isCircular, DefaultImageRequest defaultImageRequest) {
-        loadPhoto(view, photoUri, -1, darkTheme, isCircular, defaultImageRequest, DEFAULT_AVATAR);
+        loadPhoto(view, photoUri, null, -1, darkTheme, isCircular, defaultImageRequest,
+                DEFAULT_AVATAR);
+    }
+
+    public final void loadDirectoryPhoto(ImageView view, Uri photoUri,
+            Account account, boolean darkTheme, boolean isCircular,
+            DefaultImageRequest defaultImageRequest) {
+        loadPhoto(view, photoUri, account, -1, darkTheme, isCircular,
+                defaultImageRequest, DEFAULT_AVATAR);
     }
 
     /**
@@ -821,11 +871,12 @@
     }
 
     @Override
-    public void loadThumbnail(ImageView view, long photoId, boolean darkTheme, boolean isCircular,
-            DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
+    public void loadThumbnail(ImageView view, long photoId, Account account, boolean darkTheme,
+            boolean isCircular, DefaultImageRequest defaultImageRequest, DefaultImageProvider
+                    defaultProvider) {
         if (photoId == 0) {
             // No photo is needed
-            defaultProvider.applyDefaultImage(view, -1, darkTheme, defaultImageRequest);
+            defaultProvider.applyDefaultImage(view, account, -1, darkTheme, defaultImageRequest);
             mPendingRequests.remove(view);
         } else {
             if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoId);
@@ -835,19 +886,19 @@
     }
 
     @Override
-    public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
-            boolean isCircular, DefaultImageRequest defaultImageRequest,
+    public void loadPhoto(ImageView view, Uri photoUri, Account account, int requestedExtent,
+            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
             DefaultImageProvider defaultProvider) {
         if (photoUri == null) {
             // No photo is needed
-            defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme,
+            defaultProvider.applyDefaultImage(view, account, requestedExtent, darkTheme,
                     defaultImageRequest);
             mPendingRequests.remove(view);
         } else {
             if (DEBUG) Log.d(TAG, "loadPhoto request: " + photoUri);
             if (isDefaultImageUri(photoUri)) {
-                createAndApplyDefaultImageForUri(view, photoUri, requestedExtent, darkTheme,
-                        isCircular, defaultProvider);
+                createAndApplyDefaultImageForUri(view, photoUri, account, requestedExtent,
+                        darkTheme, isCircular, defaultProvider);
             } else {
                 loadPhotoByIdOrUri(view, Request.createFromUri(photoUri, requestedExtent,
                         darkTheme, isCircular, defaultProvider, defaultImageRequest));
@@ -855,11 +906,12 @@
         }
     }
 
-    private void createAndApplyDefaultImageForUri(ImageView view, Uri uri, int requestedExtent,
-            boolean darkTheme, boolean isCircular, DefaultImageProvider defaultProvider) {
+    private void createAndApplyDefaultImageForUri(ImageView view, Uri uri, Account account,
+            int requestedExtent, boolean darkTheme, boolean isCircular, DefaultImageProvider
+                    defaultProvider) {
         DefaultImageRequest request = getDefaultImageRequestFromUri(uri);
         request.isCircular = isCircular;
-        defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, request);
+        defaultProvider.applyDefaultImage(view, account, requestedExtent, darkTheme, request);
     }
 
     private void loadPhotoByIdOrUri(ImageView view, Request request) {
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
old mode 100755
new mode 100644
index 9f25726..3e7a94d
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -50,9 +50,12 @@
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import android.support.v4.os.ResultReceiver;
 import android.text.TextUtils;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.contacts.ContactUtils;
+import com.android.contacts.SimContactsOperation;
 import com.android.contacts.activities.ContactEditorActivity;
 import com.android.contacts.compat.CompatUtils;
 import com.android.contacts.compat.PinnedPositionsCompat;
@@ -189,6 +192,8 @@
 
     private static final int PERSIST_TRIES = 3;
 
+    private static SimContactsOperation mSimContactsOperation;
+
     private static final int MAX_CONTACTS_PROVIDER_BATCH_SIZE = 499;
 
     public interface Listener {
@@ -216,6 +221,7 @@
         super.onCreate();
         mGroupsDao = new GroupsDaoImpl(this);
         mSimContactDao = SimContactDao.create(this);
+        mSimContactsOperation = new SimContactsOperation(this);
     }
 
     public static void registerListener(Listener listener) {
@@ -511,128 +517,149 @@
         long insertedRawContactId = -1;
 
         // Attempt to persist changes
+        // flag indicate whether contact need saved to local database, only set false when
+        // sim contacts saved failure,otherwise it always true
+        boolean needSaveToLocal = true;
+
+        ArrayList<Long> rawContactsList = new ArrayList<Long>();
+        for (int i=0; i < state.size(); i++) {
+            final RawContactDelta entity = state.get(i);
+            final String accountType = entity.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+            final String accountName = entity.getValues().getAsString(RawContacts.ACCOUNT_NAME);
+            rawContactsList.add(entity.getRawContactId());
+            final int subscription = ContactUtils.getSubscription(
+                accountType, accountName);
+            boolean isCardOperation = (subscription != SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+                    ? true : false;
+            if (isCardOperation) {
+                needSaveToLocal = doSaveToSimCard(entity, resolver, subscription);
+                Log.d(TAG, "doSaveToSimCard result is  " + needSaveToLocal);
+            }
+        }
         int tries = 0;
         while (tries++ < PERSIST_TRIES) {
-            try {
-                // Build operations and try applying
-                final ArrayList<CPOWrapper> diffWrapper = state.buildDiffWrapper();
+            if (needSaveToLocal) {
+                try {
+                    // Build operations and try applying
+                    final ArrayList<CPOWrapper> diffWrapper = state.buildDiffWrapper();
 
-                final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
+                    final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
 
-                for (CPOWrapper cpoWrapper : diffWrapper) {
-                    diff.add(cpoWrapper.getOperation());
-                }
-
-                if (DEBUG) {
-                    Log.v(TAG, "Content Provider Operations:");
-                    for (ContentProviderOperation operation : diff) {
-                        Log.v(TAG, operation.toString());
+                    for (CPOWrapper cpoWrapper : diffWrapper) {
+                        diff.add(cpoWrapper.getOperation());
                     }
-                }
 
-                int numberProcessed = 0;
-                boolean batchFailed = false;
-                final ContentProviderResult[] results = new ContentProviderResult[diff.size()];
-                while (numberProcessed < diff.size()) {
-                    final int subsetCount = applyDiffSubset(diff, numberProcessed, results, resolver);
-                    if (subsetCount == -1) {
-                        Log.w(TAG, "Resolver.applyBatch failed in saveContacts");
-                        batchFailed = true;
-                        break;
-                    } else {
-                        numberProcessed += subsetCount;
+                    if (DEBUG) {
+                        Log.v(TAG, "Content Provider Operations:");
+                        for (ContentProviderOperation operation : diff) {
+                            Log.v(TAG, operation.toString());
+                        }
                     }
-                }
 
-                if (batchFailed) {
-                    // Retry save
-                    continue;
-                }
+                    int numberProcessed = 0;
+                    boolean batchFailed = false;
+                    final ContentProviderResult[] results = new ContentProviderResult[diff.size()];
+                    while (numberProcessed < diff.size()) {
+                        final int subsetCount = applyDiffSubset(diff, numberProcessed, results, resolver);
+                        if (subsetCount == -1) {
+                            Log.w(TAG, "Resolver.applyBatch failed in saveContacts");
+                            batchFailed = true;
+                            break;
+                        } else {
+                            numberProcessed += subsetCount;
+                        }
+                    }
 
-                final long rawContactId = getRawContactId(state, diffWrapper, results);
-                if (rawContactId == -1) {
-                    throw new IllegalStateException("Could not determine RawContact ID after save");
-                }
-                // We don't have to check to see if the value is still -1.  If we reach here,
-                // the previous loop iteration didn't succeed, so any ID that we obtained is bogus.
-                insertedRawContactId = getInsertedRawContactId(diffWrapper, results);
-                if (isProfile) {
-                    // Since the profile supports local raw contacts, which may have been completely
-                    // removed if all information was removed, we need to do a special query to
-                    // get the lookup URI for the profile contact (if it still exists).
-                    Cursor c = resolver.query(Profile.CONTENT_URI,
-                            new String[] {Contacts._ID, Contacts.LOOKUP_KEY},
-                            null, null, null);
-                    if (c == null) {
+                    if (batchFailed) {
+                        // Retry save
                         continue;
                     }
-                    try {
-                        if (c.moveToFirst()) {
-                            final long contactId = c.getLong(0);
-                            final String lookupKey = c.getString(1);
-                            lookupUri = Contacts.getLookupUri(contactId, lookupKey);
-                        }
-                    } finally {
-                        c.close();
+
+                    final long rawContactId = getRawContactId(state, diffWrapper, results);
+                    if (rawContactId == -1) {
+                        throw new IllegalStateException("Could not determine RawContact ID after save");
                     }
-                } else {
-                    final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
-                                    rawContactId);
-                    lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri);
-                }
-                if (lookupUri != null && Log.isLoggable(TAG, Log.VERBOSE)) {
-                    Log.v(TAG, "Saved contact. New URI: " + lookupUri);
-                }
-
-                // We can change this back to false later, if we fail to save the contact photo.
-                succeeded = true;
-                break;
-
-            } catch (RemoteException e) {
-                // Something went wrong, bail without success
-                FeedbackHelper.sendFeedback(this, TAG, "Problem persisting user edits", e);
-                break;
-
-            } catch (IllegalArgumentException e) {
-                // This is thrown by applyBatch on malformed requests
-                FeedbackHelper.sendFeedback(this, TAG, "Problem persisting user edits", e);
-                showToast(R.string.contactSavedErrorToast);
-                break;
-
-            } catch (OperationApplicationException e) {
-                // Version consistency failed, re-parent change and try again
-                Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
-                final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
-                boolean first = true;
-                final int count = state.size();
-                for (int i = 0; i < count; i++) {
-                    Long rawContactId = state.getRawContactId(i);
-                    if (rawContactId != null && rawContactId != -1) {
-                        if (!first) {
-                            sb.append(',');
+                    // We don't have to check to see if the value is still -1.  If we reach here,
+                    // the previous loop iteration didn't succeed, so any ID that we obtained is bogus.
+                    insertedRawContactId = getInsertedRawContactId(diffWrapper, results);
+                    if (isProfile) {
+                        // Since the profile supports local raw contacts, which may have been completely
+                        // removed if all information was removed, we need to do a special query to
+                        // get the lookup URI for the profile contact (if it still exists).
+                        Cursor c = resolver.query(Profile.CONTENT_URI,
+                                new String[] {Contacts._ID, Contacts.LOOKUP_KEY },
+                                null, null, null);
+                        if (c == null) {
+                            continue;
                         }
-                        sb.append(rawContactId);
-                        first = false;
+                        try {
+                            if (c.moveToFirst()) {
+                                final long contactId = c.getLong(0);
+                                final String lookupKey = c.getString(1);
+                                lookupUri = Contacts.getLookupUri(contactId, lookupKey);
+                            }
+                        } finally {
+                            c.close();
+                        }
+                    } else {
+                        final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
+                                rawContactId);
+                        lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri);
                     }
-                }
-                sb.append(")");
+                    if (lookupUri != null && Log.isLoggable(TAG, Log.VERBOSE)) {
+                        Log.v(TAG, "Saved contact. New URI: " + lookupUri);
+                    }
 
-                if (first) {
-                    throw new IllegalStateException(
-                            "Version consistency failed for a new contact", e);
-                }
+                    // We can change this back to false later, if we fail to save the contact photo.
+                    succeeded = true;
+                    break;
 
-                final RawContactDeltaList newState = RawContactDeltaList.fromQuery(
-                        isProfile
-                                ? RawContactsEntity.PROFILE_CONTENT_URI
-                                : RawContactsEntity.CONTENT_URI,
-                        resolver, sb.toString(), null, null);
-                state = RawContactDeltaList.mergeAfter(newState, state);
+                } catch (RemoteException e) {
+                    // Something went wrong, bail without success
+                    FeedbackHelper.sendFeedback(this, TAG, "Problem persisting user edits", e);
+                    break;
 
-                // Update the new state to use profile URIs if appropriate.
-                if (isProfile) {
-                    for (RawContactDelta delta : state) {
-                        delta.setProfileQueryUri();
+                } catch (IllegalArgumentException e) {
+                    // This is thrown by applyBatch on malformed requests
+                    FeedbackHelper.sendFeedback(this, TAG, "Problem persisting user edits", e);
+                    showToast(R.string.contactSavedErrorToast);
+                    break;
+
+                } catch (OperationApplicationException e) {
+                    // Version consistency failed, re-parent change and try again
+                    Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
+                    final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
+                    boolean first = true;
+                    final int count = state.size();
+                    for (int i = 0; i < count; i++) {
+                        Long rawContactId = state.getRawContactId(i);
+                        if (rawContactId != null && rawContactId != -1) {
+                            if (!first) {
+                                sb.append(',');
+                            }
+                            sb.append(rawContactId);
+                            first = false;
+                        }
+                    }
+                    sb.append(")");
+
+                    if (first) {
+                        throw new IllegalStateException(
+                                "Version consistency failed for a new contact", e);
+                    }
+
+                    final RawContactDeltaList newState = RawContactDeltaList.fromQuery(
+                            isProfile
+                                    ? RawContactsEntity.PROFILE_CONTENT_URI
+                                    : RawContactsEntity.CONTENT_URI,
+                            resolver, sb.toString(), null, null);
+                    state = RawContactDeltaList.mergeAfter(newState, state);
+
+                    // Update the new state to use profile URIs if appropriate.
+                    if (isProfile) {
+                        for (RawContactDelta delta : state) {
+                            delta.setProfileQueryUri();
+                        }
                     }
                 }
             }
@@ -671,6 +698,22 @@
         }
     }
 
+    private boolean doSaveToSimCard(RawContactDelta entity, ContentResolver resolver,
+            int slot) {
+            boolean isInsert = entity.isContactInsert();
+            ContentValues values = entity.buildSimDiff();
+            if (isInsert) {
+                Uri resultUri = mSimContactsOperation.insert(values, slot);
+                if (resultUri != null)
+                    return true;
+            } else {
+                int resultInt = mSimContactsOperation.update(values, slot);
+                if (resultInt == 1)
+                    return true;
+            }
+            return false;
+        }
+
     /**
      * Splits "diff" into subsets based on "MAX_CONTACTS_PROVIDER_BATCH_SIZE", applies each of the
      * subsets, adds the returned array to "results".
@@ -1197,7 +1240,20 @@
             return;
         }
 
-        getContentResolver().delete(contactUri, null, null);
+        final List<String> segments = contactUri.getPathSegments();
+        // Contains an Id.
+        final long uriContactId = Long.parseLong(segments.get(3));
+        int subscription = mSimContactsOperation.getSimSubscription(uriContactId);
+        if (subscription != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            ContentValues values = mSimContactsOperation
+                    .getSimAccountValues(uriContactId);
+            int result = mSimContactsOperation.delete(values, subscription);
+            if (result == RESULT_SUCCESS) {
+                getContentResolver().delete(contactUri, null, null);
+            }
+        } else {
+            getContentResolver().delete(contactUri, null, null);
+        }
     }
 
     private void deleteMultipleContacts(Intent intent) {
diff --git a/src/com/android/contacts/ContactUtils.java b/src/com/android/contacts/ContactUtils.java
new file mode 100644
index 0000000..6241942
--- /dev/null
+++ b/src/com/android/contacts/ContactUtils.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2017, The Linux Foundation. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+       copyright notice, this list of conditions and the following
+       disclaimer in the documentation and/or other materials provided
+       with the distribution.
+     * Neither the name of The Linux Foundation nor the names of its
+       contributors may be used to endorse or promote products derived
+       from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package com.android.contacts;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import android.net.Uri;
+
+import com.android.contacts.model.account.SimAccountType;
+import com.android.contacts.SimContactsConstants;
+
+import android.os.IBinder;
+
+import android.os.ServiceManager;
+import com.android.internal.telephony.IIccPhoneBook;
+
+import java.util.ArrayList;
+/**
+ * Shared static SIM contact methods.
+ */
+public class ContactUtils {
+
+    private static final String TAG = "ContactUtils";
+    private static final int NAME_POS = 0;
+    private static final int NUMBER_POS = 1;
+    private static final int EMAIL_POS = 2;
+    private static final int ANR_POS = 3;
+    private static final int ADN_COUNT_POS = 0;
+    private static final int ADN_USED_POS = 1;
+    private static final int EMAIL_COUNT_POS = 2;
+    private static final int EMAIL_USED_POS = 3;
+    private static final int ANR_COUNT_POS = 4;
+    private static final int ANR_USED_POS = 5;
+    public static final int NAME_LENGTH_POS = 6;
+    public static final int NUMBER_LENGTH_POS = 7;
+    public static final int EMAIL_LENGTH_POS = 8;
+    public static final int ANR_LENGTH_POS = 9;
+
+    public final static int[] IC_SIM_PICTURE = {
+        R.drawable.ic_contact_picture_sim_1,
+        R.drawable.ic_contact_picture_sim_2,
+    };
+
+    public static int getSubscription(String accountType, String accountName) {
+        int subscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        if (accountType == null || accountName == null)
+            return subscription;
+        if (accountType.equals(SimAccountType.ACCOUNT_TYPE)) {
+            if (accountName.equals(SimContactsConstants.SIM_NAME)
+                    || accountName.equals(SimContactsConstants.SIM_NAME_1)) {
+                subscription = SimContactsConstants.SLOT1;
+            } else if (accountName.equals(SimContactsConstants.SIM_NAME_2)) {
+                subscription = SimContactsConstants.SLOT2;
+            }
+        }
+        return subscription;
+    }
+
+    public static Account getAcount(Context c , int slot) {
+        Account account = null;
+        TelephonyManager tm = (TelephonyManager) c
+                .getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm.getPhoneCount() > 1) {
+            if (slot == SimContactsConstants.SLOT1) {
+                account = new Account(SimContactsConstants.SIM_NAME_1,
+                        SimAccountType.ACCOUNT_TYPE);
+            } else if (slot == SimContactsConstants.SLOT2) {
+                account = new Account(SimContactsConstants.SIM_NAME_2,
+                        SimAccountType.ACCOUNT_TYPE);
+            }
+        } else {
+            if (slot == SimContactsConstants.SLOT1)
+                account = new Account(SimContactsConstants.SIM_NAME,
+                        SimAccountType.ACCOUNT_TYPE);
+        }
+        return account;
+    }
+
+    public static int[] getAdnRecordsCapacity(Context c, int slot) {
+        int subId = getActiveSubId(c, slot);
+        return getAdnRecordsCapacityForSubscriber(subId);
+    }
+
+    /**
+     * Returns the subscription's card can save anr or not.
+     */
+    public static boolean canSaveAnr(Context c, int slot) {
+        int adnCapacity[] = getAdnRecordsCapacity(c, slot);
+        return adnCapacity[ANR_COUNT_POS] > 0;
+    }
+
+    /**
+     * Returns the subscription's card can save email or not.
+     */
+    public static boolean canSaveEmail(Context c, int slot) {
+        int adnCapacity[] = getAdnRecordsCapacity(c, slot);
+        return adnCapacity[EMAIL_COUNT_POS] > 0;
+    }
+
+    public static int getOneSimAnrCount(Context c, int slot) {
+        int count = 0;
+        int adnCapacity[] = getAdnRecordsCapacity(c, slot);
+        int anrCount = adnCapacity[ANR_COUNT_POS];
+        int adnCount = adnCapacity[ADN_COUNT_POS];
+        if (adnCount > 0) {
+            count = anrCount % adnCount != 0 ? (anrCount / adnCount + 1)
+                    : (anrCount / adnCount);
+        }
+        return count;
+    }
+
+    public static int getOneSimEmailCount(Context c, int slot) {
+        int count = 0;
+        int adnCapacity[] = getAdnRecordsCapacity(c, slot);
+        int emailCount = adnCapacity[EMAIL_COUNT_POS];
+        int adnCount = adnCapacity[ADN_COUNT_POS];
+        if (adnCount > 0) {
+            count = emailCount % adnCount != 0 ? (emailCount / adnCount + 1)
+                    : (emailCount / adnCount);
+        }
+        return count;
+    }
+
+    public static int getSimFreeCount(Context context, int slot) {
+        int adnCapacity[] = getAdnRecordsCapacity(context, slot);
+        int count = adnCapacity[ADN_COUNT_POS]-adnCapacity[ADN_USED_POS];
+        Log.d(TAG, "spare adn:" + count);
+        return count;
+    }
+
+    public static int getSpareAnrCount(Context c, int slot) {
+        int adnCapacity[] = getAdnRecordsCapacity(c, slot);
+        int spareCount = adnCapacity[ANR_COUNT_POS]-adnCapacity[ANR_USED_POS];
+        Log.d(TAG, "spare anr:" + spareCount);
+        return spareCount;
+    }
+
+    public static int getSpareEmailCount(Context c, int slot) {
+        int adnCapacity[] = getAdnRecordsCapacity(c, slot);
+        int spareCount = adnCapacity[EMAIL_COUNT_POS]-adnCapacity[EMAIL_USED_POS];
+        Log.d(TAG, "spare email:" + spareCount);
+        return spareCount;
+    }
+
+    public static int getActiveSubId(Context c , int slot) {
+        SubscriptionInfo subInfoRecord = null;
+        try {
+            SubscriptionManager sm = SubscriptionManager.from(c);
+            subInfoRecord = sm.getActiveSubscriptionInfoForSimSlotIndex(slot);
+        } catch (Exception e) {
+        }
+        if (subInfoRecord != null)
+            return subInfoRecord.getSubscriptionId();
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    public static int getActiveSlotId(Context c , int subId) {
+        SubscriptionInfo subInfoRecord = null;
+        try {
+            SubscriptionManager sm = SubscriptionManager.from(c);
+            subInfoRecord = sm.getActiveSubscriptionInfo(subId);
+        } catch (Exception e) {
+        }
+        if (subInfoRecord != null)
+            return subInfoRecord.getSimSlotIndex();
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    public static boolean insertToPhone(String[] values, Context c, int slot) {
+        Account account = getAcount(c, slot);
+        final String name = values[NAME_POS];
+        final String phoneNumber = values[NUMBER_POS];
+        final String emailAddresses = values[EMAIL_POS];
+        final String anrs = values[ANR_POS];
+        final String[] emailAddressArray;
+        final String[] anrArray;
+        boolean success = true;
+        if (!TextUtils.isEmpty(emailAddresses)) {
+            emailAddressArray = emailAddresses.split(",");
+        } else {
+            emailAddressArray = null;
+        }
+        if (!TextUtils.isEmpty(anrs)) {
+            anrArray = anrs.split(SimContactsConstants.ANR_SEP);
+        } else {
+            anrArray = null;
+        }
+        Log.d(TAG, "insertToPhone: name= " + name + ", phoneNumber= "
+                + phoneNumber + ", emails= " + emailAddresses + ", anrs= "
+                + anrs + ", account= " + account);
+        final ArrayList<ContentProviderOperation> operationList =
+                new ArrayList<ContentProviderOperation>();
+        ContentProviderOperation.Builder builder = ContentProviderOperation
+                .newInsert(RawContacts.CONTENT_URI);
+        builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
+
+        if (account != null) {
+            builder.withValue(RawContacts.ACCOUNT_NAME, account.name);
+            builder.withValue(RawContacts.ACCOUNT_TYPE, account.type);
+        }
+        operationList.add(builder.build());
+
+        // do not allow empty value insert into database.
+        if (!TextUtils.isEmpty(name)) {
+            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+            builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
+            builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+            builder.withValue(StructuredName.GIVEN_NAME, name);
+            operationList.add(builder.build());
+        }
+        if (!TextUtils.isEmpty(phoneNumber)) {
+            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+            builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
+            builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+            builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
+            builder.withValue(Phone.NUMBER, phoneNumber);
+            builder.withValue(Data.IS_PRIMARY, 1);
+            operationList.add(builder.build());
+        }
+        if (anrArray != null) {
+            for (String anr : anrArray) {
+                if (!TextUtils.isEmpty(anr)) {
+                    builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+                    builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
+                    builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+                    builder.withValue(Phone.TYPE, Phone.TYPE_HOME);
+                    builder.withValue(Phone.NUMBER, anr);
+                    operationList.add(builder.build());
+                }
+            }
+        }
+        if (emailAddressArray != null) {
+            for (String emailAddress : emailAddressArray) {
+                if (!TextUtils.isEmpty(emailAddress)) {
+                    builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+                    builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
+                    builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+                    builder.withValue(Email.ADDRESS, emailAddress);
+                    operationList.add(builder.build());
+                }
+            }
+        }
+
+        try {
+            ContentProviderResult[] results =
+                    c.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
+            for (ContentProviderResult result: results) {
+                if (result.uri == null) {
+                    success = false;
+                    break;
+                }
+            }
+            return success;
+        } catch (Exception e) {
+            Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+            return false;
+        }
+    }
+
+    public static Uri insertToCard(Context context, String name, String number, String emails,
+            String anrNumber, int slot) {
+        return insertToCard(context, name, number, emails, anrNumber,slot, true);
+    }
+
+    public static Uri insertToCard(Context context, String name, String number, String emails,
+            String anrNumber, int slot, boolean insertToPhone) {
+        Uri result;
+        ContentValues mValues = new ContentValues();
+        mValues.clear();
+        mValues.put(SimContactsConstants.STR_TAG, name);
+        if (!TextUtils.isEmpty(number)) {
+            number = PhoneNumberUtils.stripSeparators(number);
+            mValues.put(SimContactsConstants.STR_NUMBER, number);
+        }
+        if (!TextUtils.isEmpty(emails)) {
+            mValues.put(SimContactsConstants.STR_EMAILS, emails);
+        }
+        if (!TextUtils.isEmpty(anrNumber)) {
+            anrNumber = anrNumber.replaceAll("[^0123456789PWN\\,\\;\\*\\#\\+\\:]", "");
+            mValues.put(SimContactsConstants.STR_ANRS, anrNumber);
+        }
+
+        SimContactsOperation mSimContactsOperation = new SimContactsOperation(context);
+        result = mSimContactsOperation.insert(mValues, slot);
+        if (result != null) {
+            if (insertToPhone) {
+                // we should import the contact to the sim account at the same
+                // time.
+                String[] value = new String[] { name, number, emails, anrNumber };
+                insertToPhone(value, context, slot);
+            }
+        } else {
+            Log.e(TAG, "export contact: [" + name + ", " + number + ", " + emails + "] to slot "
+                    + slot + " failed");
+        }
+        return result;
+    }
+
+    //judge the max length of number,anr,email,name for sim contact
+    public static boolean isInValidData(String data, int maxLength) {
+        if (!TextUtils.isEmpty(data)) {
+            if (data.getBytes().length > data.length()) {
+                if (data.length() > ((maxLength - 1) / 2))
+                    return true;
+            } else if (data.length() > maxLength)
+                return true;
+        }
+        return false;
+    }
+
+    /*      capacity[0]  is the max count of ADN
+            capacity[1]  is the used count of ADN
+            capacity[2]  is the max count of EMAIL
+            capacity[3]  is the used count of EMAIL
+            capacity[4]  is the max count of ANR
+            capacity[5]  is the used count of ANR
+            capacity[6]  is the max length of name
+            capacity[7]  is the max length of number
+            capacity[8]  is the max length of email
+            capacity[9]  is the max length of anr
+    */
+    private static int[] getAdnRecordsCapacityForSubscriber(int subId) {
+        int defaultCapacity[] = { 0, 0, 0, 0, 0, 0, 14, 40, 40, 40 };
+        try {
+            IIccPhoneBook iccIpb = IIccPhoneBook.Stub
+                    .asInterface(ServiceManager.getService("simphonebook"));
+            if (iccIpb != null) {
+                int[] capacity = iccIpb.getAdnRecordsCapacityForSubscriber(subId);
+                if (capacity != null)
+                    return capacity;
+            }
+        }catch (Exception e){
+            Log.e(TAG," getAdnRecordsCapacityForSubscriber error = "+e.toString());
+        }
+        return defaultCapacity;
+    }
+}
diff --git a/src/com/android/contacts/DynamicShortcuts.java b/src/com/android/contacts/DynamicShortcuts.java
index 8130776..4999be8 100644
--- a/src/com/android/contacts/DynamicShortcuts.java
+++ b/src/com/android/contacts/DynamicShortcuts.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts;
 
+import android.accounts.Account;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.app.job.JobInfo;
@@ -305,11 +306,16 @@
     }
 
     public ShortcutInfo getQuickContactShortcutInfo(long id, String lookupKey, String displayName) {
+        return getQuickContactShortcutInfo(id, lookupKey, displayName, null);
+    }
+
+    public ShortcutInfo getQuickContactShortcutInfo(long id, String lookupKey, String displayName,
+            Account account) {
         final ShortcutInfo.Builder builder = builderForContactShortcut(id, lookupKey, displayName);
         if (builder == null) {
             return null;
         }
-        addIconForContact(id, lookupKey, displayName, builder);
+        addIconForContact(id, lookupKey, displayName, builder, account);
         return builder.build();
     }
 
@@ -336,9 +342,14 @@
 
     private void addIconForContact(long id, String lookupKey, String displayName,
             ShortcutInfo.Builder builder) {
+        addIconForContact(id, lookupKey, displayName, builder, null);
+    }
+
+    private void addIconForContact(long id, String lookupKey, String displayName,
+            ShortcutInfo.Builder builder, Account account) {
         Bitmap bitmap = getContactPhoto(id);
         if (bitmap == null) {
-            bitmap = getFallbackAvatar(displayName, lookupKey);
+            bitmap = getFallbackAvatar(displayName, lookupKey, account);
         }
         final Icon icon;
         if (BuildCompat.isAtLeastO()) {
@@ -414,7 +425,7 @@
         return bitmap;
     }
 
-    private Bitmap getFallbackAvatar(String displayName, String lookupKey) {
+    private Bitmap getFallbackAvatar(String displayName, String lookupKey, Account account) {
         // Use a circular icon if we're not on O or higher.
         final boolean circularIcon = !BuildCompat.isAtLeastO();
 
@@ -425,7 +436,7 @@
             request.scale = LetterTileDrawable.getAdaptiveIconScale();
         }
         final Drawable avatar = ContactPhotoManager.getDefaultAvatarDrawableForContact(
-                mContext.getResources(), true, request);
+                mContext.getResources(), true, request, account);
         final Bitmap result = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
         // The avatar won't draw unless it thinks it is visible
         avatar.setVisible(true, true);
diff --git a/src/com/android/contacts/ShortcutIntentBuilder.java b/src/com/android/contacts/ShortcutIntentBuilder.java
index 5ea6b7e..b1eba53 100644
--- a/src/com/android/contacts/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/ShortcutIntentBuilder.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts;
 
+import android.accounts.Account;
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -40,6 +41,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
@@ -62,12 +64,16 @@
     private static final String[] CONTACT_COLUMNS = {
         Contacts.DISPLAY_NAME,
         Contacts.PHOTO_ID,
-        Contacts.LOOKUP_KEY
+        Contacts.LOOKUP_KEY,
+        RawContacts.ACCOUNT_TYPE,
+        RawContacts.ACCOUNT_NAME,
     };
 
     private static final int CONTACT_DISPLAY_NAME_COLUMN_INDEX = 0;
     private static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 1;
     private static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 2;
+    private static final int CONTACT_ACCOUNT_TYPE_COLUMN_INDEX = 3;
+    private static final int CONTACT_ACCOUNT_NAME_COLUMN_INDEX = 4;
 
     private static final String[] PHONE_COLUMNS = {
         Phone.DISPLAY_NAME,
@@ -75,7 +81,9 @@
         Phone.NUMBER,
         Phone.TYPE,
         Phone.LABEL,
-        Phone.LOOKUP_KEY
+        Phone.LOOKUP_KEY,
+        RawContacts.ACCOUNT_TYPE,
+        RawContacts.ACCOUNT_NAME,
     };
 
     private static final int PHONE_DISPLAY_NAME_COLUMN_INDEX = 0;
@@ -84,6 +92,8 @@
     private static final int PHONE_TYPE_COLUMN_INDEX = 3;
     private static final int PHONE_LABEL_COLUMN_INDEX = 4;
     private static final int PHONE_LOOKUP_KEY_COLUMN_INDEX = 5;
+    private static final int PHONE_ACCOUNT_TYPE_COLUMN_INDEX = 6;
+    private static final int PHONE_ACCOUNT_NAME_COLUMN_INDEX = 7;
 
     private static final String[] PHOTO_COLUMNS = {
         Photo.PHOTO,
@@ -157,7 +167,7 @@
         protected String mLookupKey;
         protected byte[] mBitmapData;
         protected long mPhotoId;
-
+        private Account mAccount;
         public LoadingAsyncTask(Uri uri) {
             mUri = uri;
         }
@@ -193,6 +203,8 @@
     }
 
     private final class ContactLoadingAsyncTask extends LoadingAsyncTask {
+        private Account mAccount;
+
         public ContactLoadingAsyncTask(Uri uri) {
             super(uri);
         }
@@ -207,6 +219,12 @@
                         mDisplayName = cursor.getString(CONTACT_DISPLAY_NAME_COLUMN_INDEX);
                         mPhotoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
                         mLookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+                        final String accountType = cursor
+                                .getString(CONTACT_ACCOUNT_TYPE_COLUMN_INDEX);
+                        final String accountName = cursor
+                                .getString(CONTACT_ACCOUNT_NAME_COLUMN_INDEX);
+                        if (accountType != null && accountName != null)
+                            mAccount = new Account(accountName, accountType);
                     }
                 } finally {
                     cursor.close();
@@ -215,7 +233,8 @@
         }
         @Override
         protected void onPostExecute(Void result) {
-            createContactShortcutIntent(mUri, mContentType, mDisplayName, mLookupKey, mBitmapData);
+            createContactShortcutIntent(mUri, mContentType, mDisplayName,
+                    mAccount, mLookupKey, mBitmapData);
         }
     }
 
@@ -224,6 +243,7 @@
         private String mPhoneNumber;
         private int mPhoneType;
         private String mPhoneLabel;
+        private Account mAccount;
 
         public PhoneNumberLoadingAsyncTask(Uri uri, String shortcutAction) {
             super(uri);
@@ -243,6 +263,12 @@
                         mPhoneType = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
                         mPhoneLabel = cursor.getString(PHONE_LABEL_COLUMN_INDEX);
                         mLookupKey = cursor.getString(PHONE_LOOKUP_KEY_COLUMN_INDEX);
+                        final String accountType = cursor
+                                .getString(PHONE_ACCOUNT_TYPE_COLUMN_INDEX);
+                        final String accountName = cursor
+                                .getString(PHONE_ACCOUNT_NAME_COLUMN_INDEX);
+                        if (accountType != null)
+                            mAccount = new Account(accountName, accountType);
                     }
                 } finally {
                     cursor.close();
@@ -253,11 +279,12 @@
         @Override
         protected void onPostExecute(Void result) {
             createPhoneNumberShortcutIntent(mUri, mDisplayName, mLookupKey, mBitmapData,
-                    mPhoneNumber, mPhoneType, mPhoneLabel, mShortcutAction);
+                    mPhoneNumber, mPhoneType, mPhoneLabel, mAccount, mShortcutAction);
         }
     }
 
-    private Drawable getPhotoDrawable(byte[] bitmapData, String displayName, String lookupKey) {
+    private Drawable getPhotoDrawable(byte[] bitmapData, String displayName, String lookupKey,
+            Account account) {
         if (bitmapData != null) {
             Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
             return new BitmapDrawable(mContext.getResources(), bitmap);
@@ -269,12 +296,12 @@
                 request.scale = LetterTileDrawable.getAdaptiveIconScale();
             }
             return ContactPhotoManager.getDefaultAvatarDrawableForContact(mContext.getResources(),
-                    false, request);
+                    false, request, account);
         }
     }
 
     private void createContactShortcutIntent(Uri contactUri, String contentType, String displayName,
-            String lookupKey, byte[] bitmapData) {
+            Account account, String lookupKey, byte[] bitmapData) {
         Intent intent = null;
         if (TextUtils.isEmpty(displayName)) {
             displayName = mContext.getResources().getString(R.string.missing_name);
@@ -285,12 +312,12 @@
                     mContext.getSystemService(Context.SHORTCUT_SERVICE);
             final DynamicShortcuts dynamicShortcuts = new DynamicShortcuts(mContext);
             final ShortcutInfo shortcutInfo = dynamicShortcuts.getQuickContactShortcutInfo(
-                    contactId, lookupKey, displayName);
+                    contactId, lookupKey, displayName, account);
             if (shortcutInfo != null) {
                 intent = sm.createShortcutResultIntent(shortcutInfo);
             }
         }
-        final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+        final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey, account);
 
         final Intent shortcutIntent = ImplicitIntentsUtil.getIntentForQuickContactLauncherShortcut(
                 mContext, contactUri);
@@ -312,8 +339,8 @@
 
     private void createPhoneNumberShortcutIntent(Uri uri, String displayName, String lookupKey,
             byte[] bitmapData, String phoneNumber, int phoneType, String phoneLabel,
-            String shortcutAction) {
-        final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+            Account account, String shortcutAction) {
+        final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey, account);
         final Bitmap icon;
         final Uri phoneUri;
         final String shortcutName;
diff --git a/src/com/android/contacts/SimContactsConstants.java b/src/com/android/contacts/SimContactsConstants.java
new file mode 100644
index 0000000..d196a4f
--- /dev/null
+++ b/src/com/android/contacts/SimContactsConstants.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015,2017, The Linux Foundation. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+         copyright notice, this list of conditions and the following
+         disclaimer in the documentation and/or other materials provided
+         with the distribution.
+       * Neither the name of The Linux Foundation nor the names of its
+         contributors may be used to endorse or promote products derived
+         from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package com.android.contacts;
+
+
+public interface SimContactsConstants {
+
+    public static final String SIM_NAME = "SIM";
+    public static final String SIM_NAME_1 = "SIM1";
+    public static final String SIM_NAME_2 = "SIM2";
+    public static final int SLOT1 = 0;
+    public static final int SLOT2 = 1;
+    public static final String SLOT_KEY = "slot";
+    public static final String ACCOUNT_TYPE_SIM = "com.android.sim";
+    public static final String ACCOUNT_TYPE = "account_type";
+    public static final String ACCOUNT_NAME = "account_name";
+    public static final String ACCOUNT_DATA = "data_set";
+    public static final String STR_TAG = "tag";
+    public static final String STR_NUMBER = "number";
+    public static final String STR_EMAILS = "emails";
+    public static final String STR_ANRS = "anrs";
+    public static final String STR_NEW_TAG = "newTag";
+    public static final String STR_NEW_NUMBER = "newNumber";
+    public static final String STR_NEW_EMAILS = "newEmails";
+    public static final String STR_NEW_ANRS = "newAnrs";
+    public static final String ANR_SEP = ":";
+    public static final String EMAIL_SEP = ",";
+    public static final String SIM_URI = "content://icc/adn";
+    public static final String SIM_SUB_URI = "content://icc/adn/subId/";
+    public static final String RESULT_KEY = "result";
+    public static final String ACTION_MULTI_PICK_CONTACT =
+            "com.android.contacts.action.MULTI_PICK_CONTACT";
+    public static final String ACTION_MULTI_PICK =
+            "com.android.contacts.action.MULTI_PICK";
+    public static final String ACTION_MULTI_PICK_EMAIL =
+            "com.android.contacts.action.MULTI_PICK_EMAIL";
+}
+
+
diff --git a/src/com/android/contacts/SimContactsOperation.java b/src/com/android/contacts/SimContactsOperation.java
new file mode 100644
index 0000000..dc52af8
--- /dev/null
+++ b/src/com/android/contacts/SimContactsOperation.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2015-2017, The Linux Foundation. All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above
+       copyright notice, this list of conditions and the following
+       disclaimer in the documentation and/or other materials provided
+       with the distribution.
+     * Neither the name of The Linux Foundation nor the names of its
+       contributors may be used to endorse or promote products derived
+       from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package com.android.contacts;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.RawContacts;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.SimContactsConstants;
+import com.android.contacts.ContactUtils;
+
+public class SimContactsOperation {
+
+    private static final String  TAG = "SimContactsOperation";
+    private static final boolean DBG = true;
+    public static final String[] CONTACT_PROJECTION = new String[] {
+        Contacts.Entity.RAW_CONTACT_ID,
+        Contacts.Entity.CONTACT_ID,
+        RawContacts.ACCOUNT_NAME,
+        RawContacts.ACCOUNT_TYPE,
+        Data.DATA1,
+        Data.DATA2,
+        Data.MIMETYPE,
+    };
+
+    private static final int CONTACT_COLUMN_RAW_ID = 0;
+    private static final int CONTACT_COLUMN_CONTACT_ID = 1;
+    private static final int CONTACT_COLUMN_ACCOUNT_NAME = 2;
+    private static final int CONTACT_COLUMN_ACCOUNT_TYPE = 3;
+    private static final int CONTACT_COLUMN_DATA = 4;
+    private static final int CONTACT_COLUMN_DATA_TYPE = 5;
+    private static final int CONTACT_COLUMN_DATA_MIMETYPE = 6;
+
+    private static Context mContext;
+    private ContentResolver mResolver;
+    private ContentValues mValues = new ContentValues();
+
+    public SimContactsOperation(Context context) {
+        mContext = context;
+        this.mResolver = context.getContentResolver();
+    }
+
+    public Uri insert(ContentValues values, int subscription) {
+
+        Uri uri = getContentUri(subscription);
+        if (uri == null)
+            return null;
+        String number = values.getAsString(SimContactsConstants.STR_NUMBER);
+        String anrs = values.getAsString(SimContactsConstants.STR_ANRS);
+        if (!TextUtils.isEmpty(anrs)) {
+            anrs = anrs.replaceAll("[^0123456789PWN\\,\\;\\*\\#\\+\\:]", "");
+        }
+        String emails = values.getAsString(SimContactsConstants.STR_EMAILS);
+        values.put(SimContactsConstants.STR_NUMBER,PhoneNumberUtils.stripSeparators(number));
+        values.put(SimContactsConstants.STR_ANRS,anrs);
+        values.put(SimContactsConstants.STR_EMAILS,emails);
+
+        Uri resultUri;
+        resultUri = mResolver.insert(uri,values);
+        return resultUri;
+    }
+
+    public int update(ContentValues values,int subscription) {
+        Uri uri = getContentUri(subscription);
+
+        int result = 0;
+        if (uri == null)
+            return result;
+        String oldNumber = values.getAsString(SimContactsConstants.STR_NUMBER);
+        String newNumber = values.getAsString(SimContactsConstants.STR_NEW_NUMBER);
+        String oldAnrs = values.getAsString(SimContactsConstants.STR_ANRS);
+        String newAnrs = values.getAsString(SimContactsConstants.STR_NEW_ANRS);
+        values.put(SimContactsConstants.STR_NUMBER,PhoneNumberUtils.stripSeparators(oldNumber));
+        values.put(SimContactsConstants.STR_NEW_NUMBER,PhoneNumberUtils.stripSeparators(newNumber));
+
+        String oldTag = values.getAsString(SimContactsConstants.STR_TAG);
+        String newTag = values.getAsString(SimContactsConstants.STR_NEW_TAG);
+        String oldEmails = values.getAsString(SimContactsConstants.STR_EMAILS);
+        String newEmails = values.getAsString(SimContactsConstants.STR_NEW_EMAILS);
+
+        if (TextUtils.isEmpty(newTag) && TextUtils.isEmpty(newNumber)
+                && TextUtils.isEmpty(newAnrs) && TextUtils.isEmpty(newEmails)){
+            return result;
+        }
+        if (!TextUtils.isEmpty(oldAnrs)) {
+            oldAnrs = oldAnrs.replaceAll("[^0123456789PWN\\,\\;\\*\\#\\+\\:]", "");
+        }
+        if (!TextUtils.isEmpty(newAnrs)) {
+            newAnrs = newAnrs.replaceAll("[^0123456789PWN\\,\\;\\*\\#\\+\\:]", "");
+        }
+        values.put(SimContactsConstants.STR_ANRS,oldAnrs);
+        values.put(SimContactsConstants.STR_NEW_ANRS,newAnrs);
+
+        result = mResolver.update(uri,values,null,null);
+        return result;
+
+    }
+
+    public int delete(ContentValues values, int subscription) {
+        Uri uri = getContentUri(subscription);
+
+        int result = 0;
+        if (uri == null)
+            return result;
+        StringBuilder buf = new StringBuilder();
+        String num = null;
+        String name = values.getAsString(SimContactsConstants.STR_TAG);
+        String number = values.getAsString(SimContactsConstants.STR_NUMBER);
+        String emails = values.getAsString(SimContactsConstants.STR_EMAILS);
+        String anrs = values.getAsString(SimContactsConstants.STR_ANRS);
+        if (number != null)
+            num = PhoneNumberUtils.stripSeparators(number);
+        if (anrs != null)
+            anrs = anrs.replaceAll("[^0123456789PWN\\,\\;\\*\\#\\+\\:]", "");
+        if (!TextUtils.isEmpty(name)) {
+            buf.append("tag='");
+            buf.append(name);
+            buf.append("'");
+        }
+        if (!TextUtils.isEmpty(num)) {
+            buf.append(" AND number='");
+            buf.append(num);
+            buf.append("'");
+        }
+        if (!TextUtils.isEmpty(emails)) {
+            buf.append(" AND emails='");
+            buf.append(emails);
+            buf.append("'");
+        }
+        if (!TextUtils.isEmpty(anrs)) {
+            buf.append(" AND anrs='");
+            buf.append(anrs);
+            buf.append("'");
+        }
+        result = mResolver.delete(uri,buf.toString(),null);
+        return result;
+    }
+
+    private Uri getContentUri(int slot) {
+        Uri uri = null;
+        int sub = ContactUtils.getActiveSubId(mContext, slot);
+        if (sub > 0) {
+            uri = Uri.parse(SimContactsConstants.SIM_SUB_URI + sub);
+        }
+        return uri;
+    }
+
+    private static Cursor setupContactCursor(long contactId) {
+        ContentResolver resolver = mContext.getContentResolver();
+        Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,
+                contactId);
+        Uri entityUri = Uri.withAppendedPath(contactUri,
+                Contacts.Entity.CONTENT_DIRECTORY);
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(entityUri,
+                    CONTACT_PROJECTION, null, null, null);
+        } catch (Exception e) {
+            Log.e(TAG, e.getMessage());
+        } finally {
+            if (cursor != null && cursor.moveToFirst()) {
+                return cursor;
+            }
+            if (cursor != null) {
+                cursor.close();
+            }
+            return null;
+        }
+    }
+
+    public static ContentValues getSimAccountValues(long contactId) {
+        ContentValues mValues = new ContentValues();
+        Cursor cursor = setupContactCursor(contactId);
+        StringBuilder anr = new StringBuilder();
+        StringBuilder email = new StringBuilder();
+        if (cursor == null) {
+            mValues.clear();
+            return mValues;
+        }
+
+        try {
+            if (cursor.getCount() == 0 || !cursor.moveToFirst()) {
+                cursor.close();
+                return mValues;
+            }
+            do {
+                String accountType = cursor
+                        .getString(CONTACT_COLUMN_ACCOUNT_TYPE);
+                String accountName = cursor.getString(CONTACT_COLUMN_ACCOUNT_NAME);
+                String mimeType = cursor
+                        .getString(CONTACT_COLUMN_DATA_MIMETYPE);
+                String data = cursor.getString(CONTACT_COLUMN_DATA);
+                String nameAndType = cursor.getString(CONTACT_COLUMN_DATA_TYPE);
+                mValues.put(SimContactsConstants.ACCOUNT_TYPE, accountType);
+                mValues.put(SimContactsConstants.ACCOUNT_NAME, accountName);
+                if (TextUtils.isEmpty(data))
+                    continue;
+                if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(accountType)) {
+                    if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
+                        mValues.put(SimContactsConstants.STR_TAG, nameAndType);
+                    } else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
+                        if (Integer.parseInt(nameAndType) == Phone.TYPE_MOBILE) {
+                            mValues.put(SimContactsConstants.STR_NUMBER, data);
+                        } else {
+                            if (!TextUtils.isEmpty(anr.toString())) {
+                                anr.append(SimContactsConstants.ANR_SEP);
+                            }
+                            anr.append(data);
+                        }
+                    } else if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) {
+                        if (!TextUtils.isEmpty(email.toString())) {
+                            email.append(SimContactsConstants.EMAIL_SEP);
+                        }
+                        email.append(data);
+                    }
+                }
+            } while (cursor.moveToNext());
+
+            if(!TextUtils.isEmpty(anr.toString()))
+                mValues.put(SimContactsConstants.STR_ANRS, anr.toString());
+            if (!TextUtils.isEmpty(email.toString()))
+                mValues.put(SimContactsConstants.STR_EMAILS, email.toString());
+        } catch (Exception e) {
+            Log.d(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+        } finally {
+            cursor.close();
+        } Log.d(TAG,"getSimAccountValue: " + mValues.toString());
+        return mValues;
+    }
+
+    public static int getSimSubscription(long contactId) {
+        int subscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        Cursor cursor = setupContactCursor(contactId);
+        try {
+            if (cursor == null || cursor.getCount() == 0) {
+                return subscription;
+            }
+            String accountName = cursor.getString(cursor
+                    .getColumnIndex(RawContacts.ACCOUNT_NAME));
+            String accountType = cursor.getString(cursor
+                    .getColumnIndex(RawContacts.ACCOUNT_TYPE));
+            if (accountType == null || accountName == null) {
+                cursor.close();
+                return subscription;
+            }
+            if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(accountType)) {
+                subscription = ContactUtils.getSubscription(accountType,
+                        accountName);
+            }
+        } finally {
+            if (cursor != null)
+                cursor.close();
+        }
+        return subscription;
+    }
+
+}
diff --git a/src/com/android/contacts/SimImportFragment.java b/src/com/android/contacts/SimImportFragment.java
index 6042939..babc3d3 100644
--- a/src/com/android/contacts/SimImportFragment.java
+++ b/src/com/android/contacts/SimImportFragment.java
@@ -43,6 +43,7 @@
 
 import com.android.contacts.compat.CompatUtils;
 import com.android.contacts.database.SimContactDao;
+import com.android.contacts.model.account.SimAccountType;
 import com.android.contacts.editor.AccountHeaderPresenter;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.SimCard;
@@ -128,7 +129,18 @@
         } else {
             // Default may be null in which case the first account in the list will be selected
             // after they are loaded.
-            mAccountHeaderPresenter.setCurrentAccount(mPreferences.getDefaultAccount());
+            AccountWithDataSet account = mPreferences.getDefaultAccount();
+            // filter sim account here, uses the default google account instead
+            if (account != null && account.type != null && account.type.equals(
+                    SimAccountType.ACCOUNT_TYPE)) {
+                List<AccountInfo> gAccount = mAccountTypeManager.getWritableGoogleAccounts();
+                if (gAccount.size() > 0) {
+                    account = gAccount.get(0).getAccount();
+                } else {
+                    account = AccountWithDataSet.getNullAccount();
+                }
+            }
+            mAccountHeaderPresenter.setCurrentAccount(account);
         }
         mAccountHeaderPresenter.setObserver(new AccountHeaderPresenter.Observer() {
             @Override
@@ -437,6 +449,8 @@
                 return contact.getPhone();
             } else if (contact.hasEmails()) {
                 return contact.getEmails()[0];
+            } else if(contact.hasAnrs()) {
+                return contact.getAnrs()[0];
             } else {
                 // This isn't really possible because we skip empty SIM contacts during loading
                 return "";
@@ -461,7 +475,8 @@
         protected ListenableFuture<LoaderResult> loadData() {
             final ListenableFuture<List<Object>> future = Futures.<Object>allAsList(
                     mAccountTypeManager
-                            .filterAccountsAsync(AccountTypeManager.writableFilter()),
+                            .filterAccountsAsync(AccountTypeManager.AccountFilter
+                                    .CONTACTS_WRITABLE_WITHOUT_SIM),
                     ContactsExecutors.getSimReadExecutor().<Object>submit(
                             new Callable<Object>() {
                         @Override
diff --git a/src/com/android/contacts/activities/MultiPickContactsActivity.java b/src/com/android/contacts/activities/MultiPickContactsActivity.java
new file mode 100644
index 0000000..a7b4d3f
--- /dev/null
+++ b/src/com/android/contacts/activities/MultiPickContactsActivity.java
@@ -0,0 +1,988 @@
+/**
+ * Copyright (C) 2013-2018, The Linux Foundation. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.contacts.activities;
+
+import android.accounts.Account;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContentUris;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Handler;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.contacts.ContactUtils;
+import com.android.contacts.SimContactsConstants;
+import com.android.contacts.SimContactsOperation;
+import com.android.contacts.activities.RequestPermissionsActivity;
+import com.android.contacts.list.ContactsFragment;
+import com.android.contacts.list.ContactsPickMode;
+import com.android.contacts.list.OnCheckListActionListener;
+import com.android.contacts.R;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+public class MultiPickContactsActivity extends Activity implements
+        OnCheckListActionListener, View.OnClickListener, View.OnFocusChangeListener {
+
+    private final static String TAG = "MultiPickContactsActivity";
+    private final static boolean DEBUG = true;
+
+    private ContactsPickMode mPickMode;
+    private int mSelectedNums = 0;
+    private ContactsFragment mContactsFragment;
+    private boolean mSearchUiVisible = false;
+    // contains data ids
+    private Bundle mChoiceSet;
+    private Bundle mBackupChoiceSet;
+    private TextView mOKButton;
+    private ActionBar mActionBar;
+    private EditText mSearchView;
+    private ViewGroup mSearchViewContainer;
+    private View mSelectionContainer;
+    private Button mSelectionButton;
+    private MenuItem searchItem;
+    private SelectionMenu mSelectionMenu;
+    private Context mContext;
+    private ProgressDialog mProgressDialog;
+    private SimContactsOperation mSimContactsOperation;
+    private boolean mDelete = false;
+    private int mExportSub = -1;
+
+    private static final int ACCOUNT_TYPE_COLUMN_ID = 5;
+    private static final int ACCOUNT_NAME_COLUMN_ID = 6;
+    private static final int BUFFER_LENGTH = 400;
+
+    private static final int TOAST_EXPORT_FINISHED = 0;
+    // only for sim card is full
+    private static final int TOAST_SIM_CARD_FULL = 1;
+    // there is a case export is canceled by user
+    private static final int TOAST_EXPORT_CANCELED = 2;
+    // only for not have phone number or email address
+    private static final int TOAST_EXPORT_NO_PHONE_OR_EMAIL = 3;
+    // only for export failed in exporting progress
+    private static final int TOAST_SIM_EXPORT_FAILED = 4;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (RequestPermissionsActivity.startPermissionActivityIfNeeded(this)) {
+            return;
+        }
+        setContentView(R.layout.multi_pick_activity);
+        mChoiceSet = new Bundle();
+        mContext = getApplicationContext();
+        FragmentManager fragmentManager = getFragmentManager();
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+        mContactsFragment = (ContactsFragment) fragmentManager
+                .findFragmentByTag("tab-contacts");
+        if (mContactsFragment == null) {
+            mContactsFragment = new ContactsFragment();
+            transaction.add(R.id.pick_layout, mContactsFragment, "tab-contacts");
+        }
+        mContactsFragment.setCheckListListener(this);
+        transaction.commitAllowingStateLoss();
+        mActionBar = getActionBar();
+        mActionBar.setDisplayShowHomeEnabled(true);
+        mActionBar.setDisplayHomeAsUpEnabled(true);
+        mActionBar.setTitle(null);
+        mPickMode = ContactsPickMode.getInstance();
+        mPickMode.setMode(getIntent());
+        mDelete = getIntent().getBooleanExtra("delete", false);
+        mExportSub = getIntent().getIntExtra("exportSub", -1);
+        inflateSearchView();
+        mSimContactsOperation = new SimContactsOperation(this);
+        initResource();
+        mActionBar.setElevation(4 * getResources().getDisplayMetrics().density);
+    }
+
+    private void initResource() {
+        mOKButton = (TextView) findViewById(R.id.btn_ok);
+        mOKButton.setOnClickListener(this);
+        setOkStatus();
+    }
+
+    private void inflateSearchView() {
+        LayoutInflater inflater = LayoutInflater.from(mActionBar.getThemedContext());
+        mSearchViewContainer = (ViewGroup) inflater.inflate(R.layout.custom_pick_action_bar, null);
+        mSearchView = (EditText) mSearchViewContainer.findViewById(R.id.search_view);
+        mSearchView.setHintTextColor(getColor(R.color.searchbox_phone_hint_text_color));
+        mSearchView.setTextColor(getColor(R.color.searchbox_phone_text_color));
+        mSelectionContainer = inflater.inflate(R.layout.action_mode, null);
+        mSelectionButton = (Button) mSelectionContainer.findViewById(R.id.selection_menu);
+        mSelectionButton.setLayoutParams(new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
+        String countTitle = mContext.getResources().getString(R.string.contacts_selected,
+                mSelectedNums);
+        mSelectionButton.setText(countTitle);
+        mSelectionButton.setElevation(4 * getResources().getDisplayMetrics().density);
+        // Setup selection bar
+        if (mSelectionMenu == null) {
+            mSelectionMenu = new SelectionMenu(this, mSelectionButton,
+                    new PopupList.OnPopupItemClickListener() {
+                        @Override
+                        public boolean onPopupItemClick(int itemId) {
+                            if (itemId == SelectionMenu.SELECT_OR_DESELECT) {
+                                setAllSelected();
+                            }
+                            return true;
+                        }
+                    });
+            mSelectionMenu.getPopupList().addItem(
+                    SelectionMenu.SELECT_OR_DESELECT,
+                    getString(R.string.menu_select_all));
+        }
+        mActionBar.setDisplayShowCustomEnabled(true);
+        configureSearchMode();
+        mSearchView.setHint(getString(R.string.enter_contact_name));
+        mSearchView.setFocusable(true);
+        mSearchView.setOnFocusChangeListener(this);
+        mSearchView.addTextChangedListener(new SearchTextWatcher());
+    }
+
+    private class SearchTextWatcher implements TextWatcher {
+        @Override
+        public void onTextChanged(CharSequence queryString, int start, int before, int count) {
+            updateState(queryString.toString());
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        }
+    }
+
+    private void setAllSelected() {
+        boolean selectAll = false;
+        int checkNum = mChoiceSet.size();
+        int num = mContactsFragment.getListAdapter().getCount();
+        if (checkNum < num) {
+            selectAll = true;
+        }
+        mContactsFragment.setSelectedAll(selectAll);
+    }
+
+    private void addSelectionMenuPopupListItem(String countTitle) {
+        mSelectionMenu.getPopupList().addItem(SelectionMenu.SELECTED, countTitle);
+        boolean selectAll = true;
+        int checkNum = mChoiceSet.size();
+        int num = mContactsFragment.getListAdapter().getCount();
+        if (checkNum == num && num > 0) {
+            selectAll = false;
+        }
+        mSelectionMenu.getPopupList().addItem(
+                SelectionMenu.SELECT_OR_DESELECT,
+                getString(selectAll ? R.string.menu_select_all
+                        : R.string.menu_select_none));
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.search_menu, menu);
+        searchItem = menu.findItem(R.id.menu_search);
+        searchItem.setVisible(!mSearchUiVisible);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.menu_search:
+                mSearchUiVisible = true;
+                enterSearchMode();
+                configureSearchMode();
+                return true;
+            case android.R.id.home:
+                onBackPressed();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void configureSearchMode() {
+        TextView topDividerLine = (TextView) findViewById(R.id.multi_pick_top_divider);
+        if (mSearchUiVisible) {
+            topDividerLine.setVisibility(View.VISIBLE);
+            mActionBar.setHomeAsUpIndicator(R.drawable.quantum_ic_arrow_back_vd_theme_24);
+            int searchboxStatusBarColor = getColor(R.color.searchbox_phone_background_color);
+            ColorDrawable searchboxStatusBarDrawable = new ColorDrawable(searchboxStatusBarColor);
+            mActionBar.setBackgroundDrawable(searchboxStatusBarDrawable);
+            mSelectionContainer.setVisibility(View.GONE);
+            mSearchViewContainer.setVisibility(View.VISIBLE);
+            mActionBar.setCustomView(mSearchViewContainer, new ActionBar.LayoutParams(
+                    ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT));
+            mSearchView.requestFocus();
+        } else {
+            topDividerLine.setVisibility(View.GONE);
+            mActionBar.setHomeAsUpIndicator(null);
+            int normalStatusBarColor = getColor(R.color.primary_color);
+            getActionBar().setBackgroundDrawable(new ColorDrawable(normalStatusBarColor));
+            mSearchViewContainer.setVisibility(View.GONE);
+            mSelectionContainer.setVisibility(View.VISIBLE);
+            mActionBar.setCustomView(mSelectionContainer, new ActionBar.LayoutParams(
+                    ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT));
+            if (mSearchView != null && !TextUtils.isEmpty(mSearchView.getText().toString())) {
+                mSearchView.setText(null);
+            }
+        }
+    }
+
+    private void updateActionBar() {
+        mSelectedNums = (mChoiceSet.isEmpty() ? 0 : mChoiceSet.size());
+        String countTitle = mContext.getResources().getString(R.string.contacts_selected,
+                mSelectedNums);
+        mSelectionButton.setText(countTitle);
+        mSelectionMenu.getPopupList().clearItems();
+        addSelectionMenuPopupListItem(countTitle);
+        invalidateOptionsMenu();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mSearchUiVisible) {
+            mSearchUiVisible = false;
+            exitSearchMode();
+            configureSearchMode();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    public void onFocusChange(View view, boolean hasFocus) {
+        switch (view.getId()) {
+            case R.id.search_view: {
+                if (hasFocus) {
+                    final InputMethodManager imm = (InputMethodManager) getSystemService(
+                            Context.INPUT_METHOD_SERVICE);
+                    imm.showSoftInput(mSearchView.findFocus(), 0);
+                    updateState(null);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mProgressDialog != null) {
+            mProgressDialog.cancel();
+        }
+        super.onDestroy();
+    }
+
+    private void updateState(String query) {
+        if (!TextUtils.isEmpty(query)) {
+            if (!mPickMode.isSearchMode()) {
+                mSearchUiVisible = true;
+                enterSearchMode();
+                configureSearchMode();
+            }
+        }
+        mContactsFragment.doFilter(query);
+    }
+
+    private void setOkStatus() {
+        if (0 == mChoiceSet.size()) {
+            mOKButton.setEnabled(false);
+            mOKButton.setTextColor(
+                    mContext.getResources().getColor(R.color.ok_or_clear_button_disable_color));
+        } else {
+            mOKButton.setEnabled(true);
+            mOKButton.setTextColor(
+                    mContext.getResources().getColor(R.color.ok_or_clear_button_normal_color));
+        }
+    }
+
+    private void enterSearchMode() {
+        mOKButton.setVisibility(View.GONE);
+        searchItem.setVisible(false);
+        mPickMode.enterSearchMode();
+    }
+
+    private void exitSearchMode() {
+        mOKButton.setVisibility(View.VISIBLE);
+        searchItem.setVisible(true);
+        mPickMode.exitSearchMode();
+        mContactsFragment.startQuery();
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id, Bundle bundle) {
+        switch (id) {
+            case R.id.dialog_delete_contact_confirmation: {
+                return new AlertDialog.Builder(this)
+                    .setTitle(R.string.deleteConfirmation_title)
+                    .setIconAttribute(android.R.attr.alertDialogIcon)
+                    .setMessage(R.string.ContactMultiDeleteConfirmation)
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .setPositiveButton(android.R.string.ok,
+                            new DeleteClickListener()).create();
+            }
+
+        }
+        return super.onCreateDialog(id, bundle);
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        switch (id) {
+            case R.id.btn_ok:
+                if (mPickMode.isSearchMode()) {
+                    exitSearchMode();
+                }
+                if (mDelete) {
+                    showDialog(R.id.dialog_delete_contact_confirmation);
+                } else if(mExportSub > -1) {
+                    new ExportToSimThread().start();
+                } else {
+                    Intent intent = new Intent();
+                    Bundle bundle = new Bundle();
+                    bundle.putBundle(SimContactsConstants.RESULT_KEY, mChoiceSet);
+                    intent.putExtras(bundle);
+                    this.setResult(RESULT_OK, intent);
+                    finish();
+                }
+                break;
+        }
+    }
+
+    private void hideSoftKeyboard() {
+        // Hide soft keyboard, if visible
+        InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        inputMethodManager.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
+    }
+
+    protected static void log(String msg) {
+        if (DEBUG)
+            Log.d(TAG, msg);
+    }
+
+    private class DeleteClickListener implements DialogInterface.OnClickListener {
+        public void onClick(DialogInterface dialog, int which) {
+            CharSequence title = getString(R.string.delete_contacts_title);
+            CharSequence message = getString(R.string.delete_contacts_message);
+            Thread thread = new DeleteContactsThread();
+
+            DialogInterface.OnKeyListener keyListener = new DialogInterface.OnKeyListener() {
+                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+                    switch (keyCode) {
+                        case KeyEvent.KEYCODE_SEARCH:
+                        case KeyEvent.KEYCODE_CALL:
+                            return true;
+                        default:
+                            return false;
+                    }
+                }
+            };
+
+            mProgressDialog = new ProgressDialog(MultiPickContactsActivity.this);
+            mProgressDialog.setTitle(title);
+            mProgressDialog.setMessage(message);
+            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+            mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+                    getString(R.string.btn_cancel), (OnClickListener) thread);
+            mProgressDialog.setOnCancelListener((OnCancelListener) thread);
+            mProgressDialog.setOnKeyListener(keyListener);
+            mProgressDialog.setProgress(0);
+            mProgressDialog.setMax(mChoiceSet.size());
+
+            // set dialog can not be canceled by touching outside area of
+            // dialog.
+            mProgressDialog.setCanceledOnTouchOutside(false);
+            mProgressDialog.show();
+
+            mOKButton.setEnabled(false);
+            mOKButton.setTextColor(mContext.getResources().getColor(
+                    R.color.ok_or_clear_button_disable_color));
+
+            thread.start();
+        }
+    }
+
+    /**
+     * Delete contacts thread
+     */
+    public class DeleteContactsThread extends Thread
+            implements DialogInterface.OnCancelListener, DialogInterface.OnClickListener {
+        boolean mCanceled = false;
+        // Use to save the operated contacts.
+        private ArrayList<ContentProviderOperation> mOpsContacts = null;
+        public DeleteContactsThread() {
+        }
+
+        @Override
+        public void run() {
+            Bundle choiceSet = (Bundle) mChoiceSet.clone();
+            Set<String> keySet = choiceSet.keySet();
+            Iterator<String> iterator = keySet.iterator();
+            ContentProviderOperation cpo = null;
+            ContentProviderOperation.Builder builder = null;
+            // Current contact count we can delete.
+            int count = 0;
+            // The contacts we batch delete once.
+            final int BATCH_DELETE_CONTACT_NUMBER = 400;
+            mOpsContacts = new ArrayList<ContentProviderOperation>();
+            while (!mCanceled & iterator.hasNext()) {
+                String id = String.valueOf(iterator.next());
+                String[] value = choiceSet.getStringArray(id);
+                long longId = Long.parseLong(id);
+                String accountType = value[ACCOUNT_TYPE_COLUMN_ID];
+                String accountName = value[ACCOUNT_NAME_COLUMN_ID];
+                Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, id);
+                //delete sim contacts first
+                if (accountType != null && accountType
+                                .equals(SimContactsConstants.ACCOUNT_TYPE_SIM)) {
+                    int subscription = ContactUtils.getSubscription(
+                            accountType, accountName);
+                    ContentValues values = mSimContactsOperation
+                            .getSimAccountValues(longId);
+                    int result = mSimContactsOperation.delete(values,
+                            subscription);
+                    if (result == 0) {
+                        mProgressDialog.incrementProgressBy(1);
+                        continue;
+                    }
+                }
+                builder = ContentProviderOperation.newDelete(uri);
+                cpo = builder.build();
+                mOpsContacts.add(cpo);
+                mProgressDialog.incrementProgressBy(1);
+                if (count % BATCH_DELETE_CONTACT_NUMBER == 0) {
+                    batchDelete();
+                }
+                count++;
+            }
+            batchDelete();
+            mOpsContacts = null;
+            finish();
+        }
+
+        /**
+         * Batch delete contacts more efficient than one by one.
+         */
+        private void batchDelete() {
+            try {
+                mContext.getContentResolver().applyBatch(
+                        android.provider.ContactsContract.AUTHORITY, mOpsContacts);
+                mOpsContacts.clear();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void onCancel(DialogInterface dialogInterface) {
+            // Cancel delete operate.
+            mCanceled = true;
+            Toast.makeText(mContext, R.string.delete_termination, Toast.LENGTH_SHORT).show();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialogInterface, int i) {
+            if (i == DialogInterface.BUTTON_NEGATIVE) {
+                mCanceled = true;
+            }
+        }
+    }
+
+    /**
+     * A thread that export contacts to sim card
+     */
+    public class ExportToSimThread extends Thread {
+        private int slot;
+        private boolean canceled = false;
+        private int freeSimCount = 0;
+        private ArrayList<ContentProviderOperation> operationList =
+                new ArrayList<ContentProviderOperation>();
+        private Account account;
+        final int BATCH_INSERT_NUMBER = 400;
+
+        public ExportToSimThread() {
+            slot = ContactUtils.getActiveSlotId(mContext, mExportSub);
+            account = ContactUtils.getAcount(mContext, slot);
+            showExportProgressDialog();
+        }
+
+        @Override
+        public void run() {
+            boolean isSimCardFull = false;
+            // in case export is stopped, record the count of inserted
+            // successfully
+            int insertCount = 0;
+            freeSimCount = ContactUtils.getSimFreeCount(mContext, slot);
+            boolean canSaveAnr = ContactUtils.canSaveAnr(mContext, slot);
+            boolean canSaveEmail = ContactUtils.canSaveEmail(mContext, slot);
+            int emailCountInOneSimContact = ContactUtils
+                    .getOneSimEmailCount(mContext, slot);
+            int phoneCountInOneSimContact = ContactUtils.getOneSimAnrCount(
+                    mContext, slot) + 1;
+            int emptyAnr = ContactUtils.getSpareAnrCount(mContext, slot);
+            int emptyEmail = ContactUtils.getSpareEmailCount(mContext, slot);
+            int emptyNumber = freeSimCount + emptyAnr;
+
+            Log.d(TAG, "freeSimCount = " + freeSimCount);
+            Bundle choiceSet = (Bundle) mChoiceSet.clone();
+            Set<String> set = choiceSet.keySet();
+            Iterator<String> i = set.iterator();
+            while (i.hasNext() && !canceled) {
+                String id = String.valueOf(i.next());
+                String name = "";
+                ArrayList<String> arrayNumber = new ArrayList<String>();
+                ArrayList<String> arrayEmail = new ArrayList<String>();
+
+                Uri dataUri = Uri.withAppendedPath(
+                        ContentUris.withAppendedId(Contacts.CONTENT_URI,
+                                Long.parseLong(id)),
+                        Contacts.Data.CONTENT_DIRECTORY);
+                final String[] projection = new String[] { Contacts._ID,
+                        Contacts.Data.MIMETYPE, Contacts.Data.DATA1, };
+                Cursor c = mContext.getContentResolver().query(dataUri,
+                        projection, null, null, null);
+                try {
+                    if (c != null && c.moveToFirst()) {
+                        do {
+                            String mimeType = c.getString(1);
+                            if (StructuredName.CONTENT_ITEM_TYPE
+                                    .equals(mimeType)) {
+                                name = c.getString(2);
+                            }
+                            if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                                String number = c.getString(2);
+                                if (!TextUtils.isEmpty(number)
+                                        && emptyNumber-- > 0) {
+                                    arrayNumber.add(number);
+                                }
+                            }
+                            if (canSaveEmail) {
+                                if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                                    String email = c.getString(2);
+                                    if (!TextUtils.isEmpty(email)
+                                            && emptyEmail-- > 0) {
+                                        arrayEmail.add(email);
+                                    }
+                                }
+                            }
+                        } while (c.moveToNext());
+                    }
+                } finally {
+                    if (c != null) {
+                        c.close();
+                    }
+                }
+                if (freeSimCount > 0 && 0 == arrayNumber.size()
+                        && 0 == arrayEmail.size()) {
+                    mToastHandler.sendMessage(mToastHandler.obtainMessage(
+                            TOAST_EXPORT_NO_PHONE_OR_EMAIL, name));
+                    continue;
+                }
+
+                int nameCount = (name != null && !name.equals("")) ? 1 : 0;
+                int groupNumCount = (arrayNumber.size() % phoneCountInOneSimContact) != 0 ?
+                        (arrayNumber.size() / phoneCountInOneSimContact + 1)
+                        : (arrayNumber.size() / phoneCountInOneSimContact);
+                int groupEmailCount = emailCountInOneSimContact == 0 ? 0
+                        : ((arrayEmail.size() % emailCountInOneSimContact) != 0 ? (arrayEmail
+                                .size() / emailCountInOneSimContact + 1)
+                                : (arrayEmail.size() / emailCountInOneSimContact));
+                // recalute the group when spare anr is not enough
+                if (canSaveAnr && emptyAnr >= 0 && emptyAnr <= groupNumCount) {
+                    groupNumCount = arrayNumber.size() - emptyAnr;
+                }
+                int groupCount = Math.max(groupEmailCount,
+                        Math.max(nameCount, groupNumCount));
+
+                Uri result = null;
+                if (DEBUG) {
+                    Log.d(TAG, "GroupCount = " + groupCount);
+                }
+                for (int k = 0; k < groupCount; k++) {
+                    if (freeSimCount > 0) {
+                        String num = arrayNumber.size() > 0 ? arrayNumber
+                                .remove(0) : null;
+                        StringBuilder anrNum = new StringBuilder();
+                        StringBuilder email = new StringBuilder();
+                        if (canSaveAnr) {
+                            for (int j = 1; j < phoneCountInOneSimContact; j++) {
+                                if (arrayNumber.size() > 0 && emptyAnr-- > 0) {
+                                    String s = arrayNumber.remove(0);
+                                    anrNum.append(s);
+                                    anrNum.append(SimContactsConstants.ANR_SEP);
+                                }
+                            }
+                        }
+                        if (canSaveEmail) {
+                            for (int j = 0; j < emailCountInOneSimContact; j++) {
+                                if (arrayEmail.size() > 0) {
+                                    String s = arrayEmail.remove(0);
+                                    email.append(s);
+                                    email.append(SimContactsConstants.EMAIL_SEP);
+                                }
+                            }
+                        }
+
+                        result = ContactUtils.insertToCard(mContext, name,
+                                num, email.toString(), anrNum.toString(),
+                                slot, false);
+
+                        if (null == result) {
+                            // Failed to insert to SIM card
+                            int anrNumber = 0;
+                            if (!TextUtils.isEmpty(anrNum)) {
+                                anrNumber += anrNum.toString().split(
+                                        SimContactsConstants.ANR_SEP).length;
+                            }
+                            // reset emptyNumber and emptyAnr to the value
+                            // before
+                            // the insert operation
+                            emptyAnr += anrNumber;
+                            emptyNumber += anrNumber;
+                            if (!TextUtils.isEmpty(num)) {
+                                emptyNumber++;
+                            }
+
+                            if (!TextUtils.isEmpty(email)) {
+                                // reset emptyEmail to the value before the
+                                // insert
+                                // operation
+                                emptyEmail += email.toString().split(
+                                        SimContactsConstants.EMAIL_SEP).length;
+                            }
+
+                            mToastHandler.sendMessage(mToastHandler
+                                    .obtainMessage(
+                                            TOAST_SIM_EXPORT_FAILED,
+                                            new String[] { name, num,
+                                                    email.toString() }));
+
+                        } else {
+                            if (DEBUG) {
+                                Log.d(TAG, "Exported contact [" + name + ", "
+                                        + id + "] to slot " + slot);
+                            }
+                            insertCount++;
+                            freeSimCount--;
+                            batchInsert(name, num, anrNum.toString(),
+                                    email.toString());
+                        }
+                    } else {
+                        isSimCardFull = true;
+                        mToastHandler.sendMessage(mToastHandler.obtainMessage(
+                                TOAST_SIM_CARD_FULL, insertCount, 0));
+                        break;
+                    }
+                }
+
+                if (isSimCardFull) {
+                    break;
+                }
+            }
+
+            if (operationList.size() > 0) {
+                try {
+                    mContext.getContentResolver().applyBatch(
+                            android.provider.ContactsContract.AUTHORITY,
+                            operationList);
+                } catch (Exception e) {
+                    Log.e(TAG,
+                            String.format("%s: %s", e.toString(),
+                                    e.getMessage()));
+                } finally {
+                    operationList.clear();
+                }
+            }
+
+            if (!isSimCardFull) {
+                // if canceled, show toast indicating export is interrupted.
+                if (canceled) {
+                    mToastHandler.sendMessage(mToastHandler.obtainMessage(TOAST_EXPORT_CANCELED,
+                            insertCount, 0));
+                } else {
+                    mToastHandler.sendEmptyMessage(TOAST_EXPORT_FINISHED);
+                }
+            }
+            finish();
+        }
+
+        private void batchInsert(String name, String phoneNumber, String anrs,
+                String emailAddresses) {
+            final String[] emailAddressArray;
+            final String[] anrArray;
+            if (!TextUtils.isEmpty(emailAddresses)) {
+                emailAddressArray = emailAddresses.split(",");
+            } else {
+                emailAddressArray = null;
+            }
+            if (!TextUtils.isEmpty(anrs)) {
+                anrArray = anrs.split(SimContactsConstants.ANR_SEP);
+            } else {
+                anrArray = null;
+            }
+            Log.d(TAG, "insertToPhone: name= " + name + ", phoneNumber= " + phoneNumber
+                    + ", emails= " + emailAddresses + ", anrs= " + anrs + ", account= " + account);
+            ContentProviderOperation.Builder builder = ContentProviderOperation
+                    .newInsert(RawContacts.CONTENT_URI);
+            builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
+
+            int ref = operationList.size();
+            if (account != null) {
+                builder.withValue(RawContacts.ACCOUNT_NAME, account.name);
+                builder.withValue(RawContacts.ACCOUNT_TYPE, account.type);
+            }
+            operationList.add(builder.build());
+            // do not allow empty value insert into database.
+            if (!TextUtils.isEmpty(name)) {
+                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+                builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, ref);
+                builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+                builder.withValue(StructuredName.GIVEN_NAME, name);
+                operationList.add(builder.build());
+            }
+
+            if (!TextUtils.isEmpty(phoneNumber)) {
+                builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+                builder.withValueBackReference(Phone.RAW_CONTACT_ID, ref);
+                builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+                builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
+                builder.withValue(Phone.NUMBER, phoneNumber);
+                builder.withValue(Data.IS_PRIMARY, 1);
+                operationList.add(builder.build());
+            }
+
+            if (anrArray != null) {
+                for (String anr : anrArray) {
+                    if (!TextUtils.isEmpty(anr)) {
+                        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+                        builder.withValueBackReference(Phone.RAW_CONTACT_ID, ref);
+                        builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+                        builder.withValue(Phone.TYPE, Phone.TYPE_HOME);
+                        builder.withValue(Phone.NUMBER, anr);
+                        operationList.add(builder.build());
+                    }
+                }
+            }
+
+            if (emailAddressArray != null) {
+                for (String emailAddress : emailAddressArray) {
+                    if (!TextUtils.isEmpty(emailAddress)) {
+                        builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+                        builder.withValueBackReference(Email.RAW_CONTACT_ID, ref);
+                        builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+                        builder.withValue(Email.TYPE, Email.TYPE_MOBILE);
+                        builder.withValue(Email.ADDRESS, emailAddress);
+                        operationList.add(builder.build());
+                    }
+                }
+            }
+
+            if (BATCH_INSERT_NUMBER - operationList.size() < 10) {
+                try {
+                    mContext.getContentResolver().applyBatch(
+                            android.provider.ContactsContract.AUTHORITY,
+                            operationList);
+                } catch (Exception e) {
+                    Log.e(TAG,
+                            String.format("%s: %s", e.toString(),
+                                    e.getMessage()));
+                } finally {
+                    operationList.clear();
+                }
+            }
+        }
+
+        private Handler mToastHandler = new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                int exportCount = 0;
+                switch (msg.what) {
+                case TOAST_EXPORT_FINISHED:
+                    Toast.makeText(mContext, R.string.export_finished,
+                            Toast.LENGTH_SHORT).show();
+                    break;
+                case TOAST_SIM_CARD_FULL:
+                    exportCount = msg.arg1;
+                    Toast.makeText(
+                            mContext,
+                            mContext.getString(
+                                    R.string.export_sim_card_full, exportCount),
+                            Toast.LENGTH_SHORT).show();
+                    break;
+                case TOAST_EXPORT_CANCELED:
+                    exportCount = msg.arg1;
+                    Toast.makeText(
+                            mContext,
+                            mContext.getString(R.string.export_cancelled,
+                                    String.valueOf(exportCount)),
+                            Toast.LENGTH_SHORT).show();
+                    break;
+                case TOAST_EXPORT_NO_PHONE_OR_EMAIL:
+                    String name = (String) msg.obj;
+                    Toast.makeText(
+                            mContext,
+                            mContext.getString(
+                                    R.string.export_no_phone_or_email, name),
+                            Toast.LENGTH_SHORT).show();
+                    break;
+                case TOAST_SIM_EXPORT_FAILED:
+                    String[] contactInfos = (String[]) msg.obj;
+                    if (contactInfos != null && contactInfos.length == 3) {
+                        String toastS = mContext.getString(
+                                R.string.sim_contact_export_failed,
+                                contactInfos[0] == null ? "" : contactInfos[0],
+                                contactInfos[1] == null ? "" : contactInfos[1],
+                                contactInfos[2] == null ? "" : contactInfos[2]);
+
+                        Toast.makeText(mContext, toastS,
+                                Toast.LENGTH_SHORT).show();
+                    }
+                    break;
+                }
+            }
+        };
+
+        public void showExportProgressDialog(){
+            mProgressDialog = new ProgressDialog(MultiPickContactsActivity.this);
+            mProgressDialog.setTitle(R.string.export_to_sim);
+            mProgressDialog.setOnCancelListener(new OnCancelListener() {
+                public void onCancel(DialogInterface dialog) {
+                    Log.d(TAG, "Cancel exporting contacts");
+                    canceled = true;
+                }
+            });
+            mProgressDialog.setMessage(mContext.getString(R.string.exporting));
+            mProgressDialog.setMax(mChoiceSet.size());
+            mProgressDialog.setCanceledOnTouchOutside(false);
+
+            // add a cancel button to let user cancel explicitly.
+            mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+                    mContext.getString(R.string.progressdialog_cancel),
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            Log.d(TAG, "Cancel exporting contacts by click button");
+                            canceled = true;
+                        }
+                    });
+
+            mProgressDialog.show();
+            mOKButton.setEnabled(false);
+            mOKButton.setTextColor(
+                    mContext.getResources().getColor(R.color.ok_or_clear_button_disable_color));
+        }
+    }
+
+    @Override
+    public boolean onContainsKey(String key) {
+        return mChoiceSet.containsKey(key);
+    }
+
+    @Override
+    public void putValue(String key, String[] value) {
+        mChoiceSet.putStringArray(key, value);
+        setOkStatus();
+    }
+
+    @Override
+    public void onRemove(String key) {
+        mChoiceSet.remove(key);
+        setOkStatus();
+    }
+
+    @Override
+    public void onClear() {
+        mChoiceSet.clear();
+        setOkStatus();
+    }
+
+    @Override
+    public void onHideSoftKeyboard() {
+        hideSoftKeyboard();
+    }
+
+    @Override
+    public void onUpdateActionBar() {
+        updateActionBar();
+    }
+
+    @Override
+    public void exitSearch() {
+        if (mPickMode.isSearchMode()) {
+            mSearchUiVisible = false;
+            exitSearchMode();
+            configureSearchMode();
+        }
+    }
+}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 243ec00..112fa1b 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -736,7 +736,7 @@
             fragment.updateStatus(mProviderStatus);
         }
         if (!transaction.isEmpty()) {
-            transaction.commit();
+            transaction.commitAllowingStateLoss();
             fragmentManager.executePendingTransactions();
         }
 
diff --git a/src/com/android/contacts/activities/PopupList.java b/src/com/android/contacts/activities/PopupList.java
new file mode 100644
index 0000000..09c0853
--- /dev/null
+++ b/src/com/android/contacts/activities/PopupList.java
@@ -0,0 +1,231 @@
+/*
+ Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.contacts.activities;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.android.contacts.R;
+
+public class PopupList {
+    private final Context mContext;
+    private final View mAnchorView;
+    private final ArrayList<Item> mItems = new ArrayList<Item>();
+
+    private PopupWindow mPopupWindow;
+    private ListView mContentList;
+    private OnPopupItemClickListener mOnPopupItemClickListener;
+
+    private int mPopupOffsetX;
+    private int mPopupOffsetY;
+    private int mPopupWidth;
+    private int mPopupHeight;
+
+    private final PopupWindow.OnDismissListener mOnDismissListener =
+            new PopupWindow.OnDismissListener() {
+                @Override
+                public void onDismiss() {
+                    if (mPopupWindow == null) return;
+                    mPopupWindow = null;
+                    ViewTreeObserver observer = mAnchorView.getViewTreeObserver();
+                    if (observer.isAlive()) {
+                        observer.removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
+                    }
+                }
+            };
+
+    private final OnItemClickListener mOnItemClickListener =
+            new OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    if (mPopupWindow == null) return;
+                    mPopupWindow.dismiss();
+                    if (mOnPopupItemClickListener != null) {
+                        mOnPopupItemClickListener.onPopupItemClick((int) id);
+                    }
+                }
+            };
+
+    private final OnGlobalLayoutListener mOnGlobalLayoutListener =
+            new OnGlobalLayoutListener() {
+                @Override
+                public void onGlobalLayout() {
+                    if (mPopupWindow == null) return;
+                    updatePopupLayoutParams();
+                    // Need to update the position of the popup window
+                    mPopupWindow.update(mAnchorView,
+                            mPopupOffsetX, mPopupOffsetY, mPopupWidth, mPopupHeight);
+                }
+            };
+
+    public PopupList(Context context, View anchorView) {
+        mContext = context;
+        mAnchorView = anchorView;
+    }
+
+    public void setOnPopupItemClickListener(OnPopupItemClickListener listener) {
+        mOnPopupItemClickListener = listener;
+    }
+
+    public void addItem(int id, String title) {
+        mItems.add(new Item(id, title));
+    }
+
+    public void clearItems() {
+        mItems.clear();
+    }
+
+    public boolean isShowing() {
+        if (mPopupWindow != null) {
+            return mPopupWindow.isShowing();
+        }
+        return false;
+    }
+
+    public void show() {
+        if (mPopupWindow != null) return;
+        mAnchorView.getViewTreeObserver()
+                .addOnGlobalLayoutListener(mOnGlobalLayoutListener);
+        mPopupWindow = createPopupWindow();
+        updatePopupLayoutParams();
+        mPopupWindow.setWidth(mPopupWidth);
+        mPopupWindow.setHeight(mPopupHeight);
+        mPopupWindow.showAsDropDown(mAnchorView, mPopupOffsetX, mPopupOffsetY);
+    }
+
+    public void dismiss() {
+        if (mPopupWindow != null)
+            mPopupWindow.dismiss();
+    }
+
+    private void updatePopupLayoutParams() {
+        ListView content = mContentList;
+        PopupWindow popup = mPopupWindow;
+
+        Rect p = new Rect();
+        popup.getBackground().getPadding(p);
+
+        int maxHeight = mPopupWindow.getMaxAvailableHeight(mAnchorView) - p.top - p.bottom;
+        mContentList.measure(
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
+        mPopupWidth = content.getMeasuredWidth() + p.top + p.bottom;
+        mPopupHeight = Math.min(maxHeight, content.getMeasuredHeight() + p.left + p.right);
+        mPopupOffsetX = -p.left;
+        mPopupOffsetY = -mAnchorView.getHeight()-p.top;
+    }
+
+    private PopupWindow createPopupWindow() {
+        PopupWindow popup = new PopupWindow(mContext);
+        popup.setOnDismissListener(mOnDismissListener);
+        popup.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
+
+        mContentList = new ListView(mContext, null,
+                android.R.attr.dropDownListViewStyle);
+        mContentList.setBackgroundResource(R.color.select_popup_button_background);
+        mContentList.setAdapter(new ItemDataAdapter());
+        mContentList.setOnItemClickListener(mOnItemClickListener);
+        popup.setElevation(24);
+        popup.setContentView(mContentList);
+        popup.setFocusable(true);
+        popup.setOutsideTouchable(true);
+
+        return popup;
+    }
+
+    public Item findItem(int id) {
+        for (Item item : mItems) {
+            if (item.id == id) return item;
+        }
+        return null;
+    }
+
+    public static interface OnPopupItemClickListener {
+        public boolean onPopupItemClick(int itemId);
+    }
+
+    public static class Item {
+        public final int id;
+        public String title;
+
+        public Item(int id, String title) {
+            this.id = id;
+            this.title = title;
+        }
+
+        public void setTitle(String title) {
+            this.title = title;
+        }
+    }
+
+    private class ItemDataAdapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mItems.get(position).id;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(mContext)
+                        .inflate(R.layout.popup_list_item, null);
+            }
+            TextView text = (TextView) convertView.findViewById(R.id.popup_list_title);
+            text.setText(mItems.get(position).title);
+            return convertView;
+        }
+    }
+}
diff --git a/src/com/android/contacts/activities/SelectionMenu.java b/src/com/android/contacts/activities/SelectionMenu.java
new file mode 100644
index 0000000..46fed4e
--- /dev/null
+++ b/src/com/android/contacts/activities/SelectionMenu.java
@@ -0,0 +1,86 @@
+/*
+ Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of The Linux Foundation nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.contacts.activities;
+
+import com.android.contacts.activities.PopupList;
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.Button;
+
+public class SelectionMenu implements View.OnClickListener {
+    private final Context mContext;
+    private final Button mButton;
+    private final PopupList mPopupList;
+    public static final int SELECTED = 0;
+    public static final int SELECT_OR_DESELECT = 1;
+
+    public SelectionMenu(Context context, Button button,
+            PopupList.OnPopupItemClickListener listener) {
+        mContext = context;
+        mButton = button;
+        mPopupList = new PopupList(context, mButton);
+        mPopupList.setOnPopupItemClickListener(listener);
+        mButton.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        mPopupList.show();
+    }
+
+    public void showPopupList() {
+        mPopupList.show();
+    }
+
+    public boolean isPopupListShow() {
+        return mPopupList.isShowing();
+    }
+
+    public void dismiss() {
+        mPopupList.dismiss();
+    }
+
+    public void updateSelectAllMode(boolean inSelectAllMode) {
+        PopupList.Item item = mPopupList.findItem(SELECT_OR_DESELECT);
+        if (item != null) {
+            item.setTitle(mContext.getString(
+                    inSelectAllMode ? R.string.menu_select_none : R.string.menu_select_all));
+        }
+    }
+
+    public void setTitle(CharSequence title) {
+        mButton.setText(title);
+    }
+
+    public PopupList getPopupList(){
+        return mPopupList;
+    }
+}
diff --git a/src/com/android/contacts/database/SimContactDaoImpl.java b/src/com/android/contacts/database/SimContactDaoImpl.java
index 5ba6bd5..36ad9f2 100644
--- a/src/com/android/contacts/database/SimContactDaoImpl.java
+++ b/src/com/android/contacts/database/SimContactDaoImpl.java
@@ -82,6 +82,7 @@
     public static String NAME = "name";
     public static String NUMBER = "number";
     public static String EMAILS = "emails";
+    public static String ANRS = "anrs";
 
     private final Context mContext;
     private final ContentResolver mResolver;
@@ -292,7 +293,7 @@
         final int colName = cursor.getColumnIndex(NAME);
         final int colNumber = cursor.getColumnIndex(NUMBER);
         final int colEmails = cursor.getColumnIndex(EMAILS);
-
+        final int colAnrs = cursor.getColumnIndex(ANRS);
         final ArrayList<SimContact> result = new ArrayList<>();
 
         while (cursor.moveToNext()) {
@@ -300,10 +301,14 @@
             final String name = cursor.getString(colName);
             final String number = cursor.getString(colNumber);
             final String emails = cursor.getString(colEmails);
-
-            final SimContact contact = new SimContact(id, name, number, parseEmails(emails));
+            String anrs = "";
+            if (colAnrs >=0 )
+                anrs = cursor.getString(colAnrs);
+            final SimContact contact = new SimContact(id, name, number, parseEmails(emails),
+                    parseAnrs(anrs));
             // Only include contact if it has some useful data
-            if (contact.hasName() || contact.hasPhone() || contact.hasEmails()) {
+            if (contact.hasName() || contact.hasPhone() || contact.hasEmails()
+                    || contact.hasAnrs()) {
                 result.add(contact);
             }
         }
@@ -396,6 +401,10 @@
         return !TextUtils.isEmpty(emails) ? emails.split(",") : null;
     }
 
+    private String[] parseAnrs(String anrs) {
+        return !TextUtils.isEmpty(anrs) ? anrs.split(":") : null;
+    }
+
     private boolean hasTelephony() {
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
     }
diff --git a/src/com/android/contacts/drawer/DrawerAdapter.java b/src/com/android/contacts/drawer/DrawerAdapter.java
index 0c8423a..ee09d20 100644
--- a/src/com/android/contacts/drawer/DrawerAdapter.java
+++ b/src/com/android/contacts/drawer/DrawerAdapter.java
@@ -272,7 +272,6 @@
         }
         final ContactListFilter account = item.account;
         final TextView textView = ((TextView) result.findViewById(R.id.title));
-        textView.setText(account.accountName);
         final boolean activated = account.equals(mSelectedAccount)
                 && mSelectedView == ContactsView.ACCOUNT_VIEW;
         textView.setTextAppearance(mActivity, activated
@@ -283,6 +282,11 @@
                 mAccountDisplayFactory.getAccountDisplayInfoFor(item.account);
         icon.setScaleType(ImageView.ScaleType.FIT_CENTER);
         icon.setImageDrawable(displayableAccount.getIcon());
+        if (account.accountName != null) {
+            textView.setText(account.accountName);
+        }else {
+            textView.setText(displayableAccount.getNameLabel());
+        }
 
         result.setTag(account);
         result.setActivated(activated);
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index a8e3cd8..4cef614 100755
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -1783,6 +1783,11 @@
         getEditorActivity().changePhoto(getPhotoMode());
     }
 
+    @Override
+    public void onClearPhotoCache() {
+        mUpdatedPhotos.remove(String.valueOf(mPhotoRawContactId));
+    }
+
     private int getPhotoMode() {
         return getContent().isWritablePhotoSet() ? PhotoActionPopup.Modes.WRITE_ABLE_PHOTO
                 : PhotoActionPopup.Modes.NO_PHOTO;
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index df4c5f9..d393b3b 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -51,6 +51,7 @@
 import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.account.AccountType.EditType;
 import com.android.contacts.model.dataitem.DataKind;
+import com.android.contacts.model.account.SimAccountType;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.DialogManager.DialogShowingView;
 
@@ -234,6 +235,14 @@
         }
     }
 
+    private boolean isSimAccount() {
+        if (mState != null && mState.getAccountType() != null
+                && mState.getAccountType().equals(SimAccountType.ACCOUNT_TYPE)) {
+            return true;
+        }
+        return false;
+    }
+
     public void setDeleteButtonVisible(boolean visible) {
         if (mIsDeletable) {
             mDeleteContainer.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
@@ -417,7 +426,7 @@
         setVisibility(View.VISIBLE);
 
         // Display label selector if multiple types available
-        final boolean hasTypes = RawContactModifier.hasEditTypes(kind);
+        final boolean hasTypes = RawContactModifier.hasEditTypes(kind) && !isSimAccount();
         setupLabelButton(hasTypes);
         mLabel.setEnabled(!readOnly && isEnabled());
         if (mKind.titleRes > 0) {
diff --git a/src/com/android/contacts/editor/PickRawContactDialogFragment.java b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
index 5a9c9fd..fe5d7d2 100644
--- a/src/com/android/contacts/editor/PickRawContactDialogFragment.java
+++ b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
@@ -3,6 +3,7 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
+import android.accounts.Account;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -129,12 +130,15 @@
 
             holder.accountName.setText(accountDisplayLabel);
             holder.accountIcon.setImageDrawable(account.getDisplayIcon(mContext));
+            Account accounts = null;
+            if(rawContact.accountType != null)
+                accounts = new Account(rawContact.accountName, rawContact.accountType);
             final ContactPhotoManager.DefaultImageRequest
                     request = new ContactPhotoManager.DefaultImageRequest(
                     displayName, String.valueOf(rawContact.id), /* isCircular = */ true);
 
             ContactPhotoManager.getInstance(mContext).loadThumbnail(holder.photo,
-                    rawContact.photoId,
+                    rawContact.photoId, accounts,
                     /* darkTheme = */ false,
                     /* isCircular = */ true,
                     request);
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 19da5bc..22593b3 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -61,6 +61,7 @@
 import com.android.contacts.model.account.AccountInfo;
 import com.android.contacts.model.account.AccountType;
 import com.android.contacts.model.account.AccountWithDataSet;
+import com.android.contacts.model.account.SimAccountType;
 import com.android.contacts.model.dataitem.CustomDataItem;
 import com.android.contacts.model.dataitem.DataKind;
 import com.android.contacts.util.AccountsListAdapter;
@@ -116,6 +117,8 @@
          * Invoked after editors have been bound for the contact.
          */
         public void onEditorsBound();
+
+        public void onClearPhotoCache();
     }
     /**
      * Sorts kinds roughly the same as quick contacts; we diverge in the following ways:
@@ -824,6 +827,10 @@
                         UiClosables.closeQuietly(popup);
                         final AccountWithDataSet newAccount = adapter.getItem(position);
                         if (mListener != null && !mPrimaryAccount.equals(newAccount)) {
+                            if (newAccount !=null && newAccount.type != null
+                                    && SimAccountType.ACCOUNT_TYPE.equals(newAccount.type)) {
+                                mListener.onClearPhotoCache();
+                            }
                             mIsExpanded = false;
                             mListener.onRebindEditorsForNewContact(
                                     rawContactDelta,
diff --git a/src/com/android/contacts/interactions/ExportDialogFragment.java b/src/com/android/contacts/interactions/ExportDialogFragment.java
index c7ce072..6d5888b 100644
--- a/src/com/android/contacts/interactions/ExportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ExportDialogFragment.java
@@ -39,11 +39,17 @@
 import android.widget.Toast;
 
 import com.android.contacts.R;
+import com.android.contacts.SimContactsConstants;
+import com.android.contacts.list.AccountFilterActivity;
+import com.android.contacts.list.ContactListFilter;
 import com.android.contacts.util.ImplicitIntentsUtil;
 import com.android.contacts.vcard.ExportVCardActivity;
+import com.android.contacts.database.SimContactDao;
+import com.android.contacts.model.SimCard;
 import com.android.contacts.vcard.ShareVCardActivity;
 import com.android.contacts.vcard.VCardCommonArguments;
 
+import java.util.List;
 /**
  * An dialog invoked to import/export contacts.
  */
@@ -61,7 +67,7 @@
     };
 
     private SubscriptionManager mSubscriptionManager;
-
+    private SimContactDao mSimDao;
     /** Preferred way to show this dialog */
     public static void show(FragmentManager fragmentManager, Class callingActivity,
             int exportMode) {
@@ -91,7 +97,7 @@
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         final String callingActivity = getArguments().getString(
                 VCardCommonArguments.ARG_CALLING_ACTIVITY);
-
+        mSimDao = SimContactDao.create(getContext());
         // Adapter that shows a list of string resources
         final ArrayAdapter<AdapterEntry> adapter = new ArrayAdapter<AdapterEntry>(getActivity(),
                 R.layout.select_dialog_item) {
@@ -126,6 +132,8 @@
             }
         }
 
+        addItems(adapter);
+
         final DialogInterface.OnClickListener clickListener =
                 new DialogInterface.OnClickListener() {
             @Override
@@ -150,6 +158,14 @@
                                 callingActivity);
                         getActivity().startActivity(exportIntent);
                     }
+                } else if (resId == R.string.export_to_sim) {
+                    dismissDialog = true;
+                    int sub = adapter.getItem(which).mSim.getSubscriptionId();
+                    Intent pickContactIntent = new Intent(
+                            SimContactsConstants.ACTION_MULTI_PICK_CONTACT,
+                            Contacts.CONTENT_URI);
+                    pickContactIntent.putExtra("exportSub", sub);
+                    getActivity().startActivity(pickContactIntent);
                 } else {
                     dismissDialog = true;
                     Log.e(TAG, "Unexpected resource: "
@@ -214,21 +230,46 @@
         }
     }
 
+    private void addItems(ArrayAdapter<AdapterEntry> adapter) {
+        final List<SimCard> sims = mSimDao.getSimCards();
+
+        if (sims.size() == 1) {
+            adapter.add(new AdapterEntry(getString(R.string.import_from_sim),
+                    R.string.export_to_sim, sims.get(0)));
+        } else {
+            for (int i = 0; i < sims.size(); i++) {
+                final SimCard sim = sims.get(i);
+                adapter.add(new AdapterEntry(getSimDescription(sim, i),
+                        R.string.export_to_sim, sim));
+            }
+        }
+    }
+
+    // use same display res as import from sim
+    private CharSequence getSimDescription(SimCard sim, int index) {
+        final CharSequence name = sim.getDisplayName();
+        if (name != null) {
+            return getString(R.string.import_from_sim_summary_fmt, name);
+        } else {
+            return getString(R.string.import_from_sim_summary_fmt, String.valueOf(index));
+        }
+    }
+
     private static class AdapterEntry {
         public final CharSequence mLabel;
         public final int mChoiceResourceId;
-        public final int mSubscriptionId;
+        public final SimCard mSim;
 
-        public AdapterEntry(CharSequence label, int resId, int subId) {
+        public AdapterEntry(CharSequence label, int resId, SimCard sim) {
             mLabel = label;
             mChoiceResourceId = resId;
-            mSubscriptionId = subId;
+            mSim = sim;
         }
 
         public AdapterEntry(String label, int resId) {
             // Store a nonsense value for mSubscriptionId. If this constructor is used,
             // the mSubscriptionId value should not be read later.
-            this(label, resId, /* subId = */ -1);
+            this(label, resId, /* subId = */ null);
         }
     }
 }
diff --git a/src/com/android/contacts/interactions/ImportDialogFragment.java b/src/com/android/contacts/interactions/ImportDialogFragment.java
index 41b1c71..6335cfe 100644
--- a/src/com/android/contacts/interactions/ImportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ImportDialogFragment.java
@@ -35,6 +35,7 @@
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.contacts.R;
 import com.android.contacts.activities.SimImportActivity;
@@ -113,7 +114,7 @@
 
         // Start loading the accounts. This is done in onResume in case they were refreshed.
         mAccountsFuture = AccountTypeManager.getInstance(getActivity()).filterAccountsAsync(
-                AccountTypeManager.writableFilter());
+                AccountTypeManager.AccountFilter.CONTACTS_WRITABLE_WITHOUT_SIM);
     }
 
     @Override
@@ -259,6 +260,11 @@
      * Handle "import from SD".
      */
     private void handleImportRequest(int resId, int subscriptionId) {
+        //if the accounts is not initial complete, give a toast here.
+        if (mAccountsFuture == null) {
+            Toast.makeText(getActivity(), R.string.vcard_import_failed, Toast.LENGTH_SHORT).show();
+            return;
+        }
         // Get the accounts. Because this only happens after a user action this should pretty
         // much never block since it will usually be at least several seconds before the user
         // interacts with the view
@@ -277,7 +283,7 @@
             args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
             SelectAccountDialogFragment.show(
                     getFragmentManager(), R.string.dialog_new_contact_account,
-                    AccountTypeManager.AccountFilter.CONTACTS_WRITABLE, args);
+                    AccountTypeManager.AccountFilter.CONTACTS_WRITABLE_WITHOUT_SIM, args);
         } else {
             AccountSelectionUtil.doImport(getActivity(), resId,
                     (size == 1 ? accountList.get(0) : null),
diff --git a/src/com/android/contacts/lettertiles/LetterTileDrawable.java b/src/com/android/contacts/lettertiles/LetterTileDrawable.java
index b80fd4f..a8631f7 100644
--- a/src/com/android/contacts/lettertiles/LetterTileDrawable.java
+++ b/src/com/android/contacts/lettertiles/LetterTileDrawable.java
@@ -16,6 +16,8 @@
 
 package com.android.contacts.lettertiles;
 
+import android.accounts.Account;
+import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -29,7 +31,11 @@
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
+import android.telephony.TelephonyManager;
 
+import com.android.contacts.ContactUtils;
+import com.android.contacts.SimContactsConstants;
+import com.android.contacts.model.account.SimAccountType;
 import com.android.contacts.R;
 
 import com.google.common.base.Preconditions;
@@ -52,6 +58,9 @@
     private static Bitmap DEFAULT_PERSON_AVATAR;
     private static Bitmap DEFAULT_BUSINESS_AVATAR;
     private static Bitmap DEFAULT_VOICEMAIL_AVATAR;
+    private static Bitmap DEFAULT_SIM_PERSON_AVATAR;
+    private static Bitmap[] DEFAULT_CUSTOMIZE_SIM_PERSON_AVATAR =
+            new Bitmap[ContactUtils.IC_SIM_PICTURE.length];
 
     /** Reusable components to avoid new allocations */
     private static final Paint sPaint = new Paint();
@@ -74,8 +83,13 @@
 
     private int mColor;
     private Character mLetter = null;
+    private Account mAccount;
 
     public LetterTileDrawable(final Resources res) {
+        this(res, null);
+    }
+
+    public LetterTileDrawable(final Resources res, final Account account) {
         if (sColors == null) {
             sColors = res.obtainTypedArray(R.array.letter_tile_colors);
             sDefaultColor = res.getColor(R.color.letter_tile_default_color);
@@ -87,6 +101,12 @@
                     R.drawable.ic_business_white_120dp);
             DEFAULT_VOICEMAIL_AVATAR = BitmapFactory.decodeResource(res,
                     R.drawable.ic_voicemail_avatar);
+            DEFAULT_SIM_PERSON_AVATAR = BitmapFactory.decodeResource(res,
+                    R.drawable.ic_contact_picture_sim);
+            for (int i = 0; i < ContactUtils.IC_SIM_PICTURE.length; i++) {
+                DEFAULT_CUSTOMIZE_SIM_PERSON_AVATAR[i] = BitmapFactory
+                        .decodeResource(res, ContactUtils.IC_SIM_PICTURE[i]);
+            }
             sPaint.setTypeface(Typeface.create(
                     res.getString(R.string.letter_tile_letter_font_family), Typeface.NORMAL));
             sPaint.setTextAlign(Align.CENTER);
@@ -96,6 +116,7 @@
         mPaint.setFilterBitmap(true);
         mPaint.setDither(true);
         mColor = sDefaultColor;
+        mAccount = account;
     }
 
     @Override
@@ -151,7 +172,8 @@
 
         // Draw letter/digit only if the first character is an english letter or there's a override
 
-        if (mLetter != null) {
+        if (mLetter != null && (mAccount == null || (mAccount != null && !mAccount.type
+                .equals(SimAccountType.ACCOUNT_TYPE)))) {
             // Draw letter or digit.
             sFirstChar[0] = mLetter;
 
@@ -169,7 +191,7 @@
                     sPaint);
         } else {
             // Draw the default image if there is no letter/digit to be drawn
-            final Bitmap bitmap = getBitmapForContactType(mContactType);
+            final Bitmap bitmap = getBitmapForContactType(mContactType, mAccount);
             drawBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(),
                     canvas);
         }
@@ -193,7 +215,17 @@
         return sColors.getColor(color, sDefaultColor);
     }
 
-    private static Bitmap getBitmapForContactType(int contactType) {
+    private static Bitmap getBitmapForContactType(int contactType, Account account) {
+        if (account != null && SimAccountType.ACCOUNT_TYPE.equals(account.type)) {
+            if (account.name.equals(SimContactsConstants.SIM_NAME))
+                return DEFAULT_SIM_PERSON_AVATAR;
+            final int slot = ContactUtils.getSubscription(
+                    SimAccountType.ACCOUNT_TYPE, account.name);
+            if (slot < 0 || slot > ContactUtils.IC_SIM_PICTURE.length) {
+                return DEFAULT_PERSON_AVATAR;
+            }
+            return DEFAULT_CUSTOMIZE_SIM_PERSON_AVATAR[slot];
+        }
         switch (contactType) {
             case TYPE_PERSON:
                 return DEFAULT_PERSON_AVATAR;
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index b508dda..41d617a 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.list;
 
+import android.accounts.Account;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.res.Resources;
@@ -705,12 +706,18 @@
      */
     protected void bindQuickContact(final ContactListItemView view, int partitionIndex,
             Cursor cursor, int photoIdColumn, int photoUriColumn, int contactIdColumn,
-            int lookUpKeyColumn, int displayNameColumn) {
+            int lookUpKeyColumn, int displayNameColumn, int accountTypeColume,
+            int accountNameColume) {
         long photoId = 0;
         if (!cursor.isNull(photoIdColumn)) {
             photoId = cursor.getLong(photoIdColumn);
         }
-
+        Account account = null;
+        if (!cursor.isNull(accountTypeColume) && !cursor.isNull(accountNameColume)) {
+            final String accountType = cursor.getString(accountTypeColume);
+            final String accountName = cursor.getString(accountNameColume);
+            account = new Account(accountName, accountType);
+        }
         QuickContactBadge quickContact = view.getQuickContact();
         quickContact.assignContactUri(
                 getContactUri(partitionIndex, cursor, contactIdColumn, lookUpKeyColumn));
@@ -722,8 +729,8 @@
         }
 
         if (photoId != 0 || photoUriColumn == -1) {
-            getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme, mCircularPhotos,
-                    null);
+            getPhotoLoader().loadThumbnail(quickContact, photoId, account, mDarkTheme,
+                    mCircularPhotos, null);
         } else {
             final String photoUriString = cursor.getString(photoUriColumn);
             final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
@@ -732,8 +739,8 @@
                 request = getDefaultImageRequestFromCursor(cursor, displayNameColumn,
                         lookUpKeyColumn);
             }
-            getPhotoLoader().loadPhoto(quickContact, photoUri, -1, mDarkTheme, mCircularPhotos,
-                    request);
+            getPhotoLoader().loadPhoto(quickContact, photoUri, account, -1,
+                    mDarkTheme, mCircularPhotos, request);
         }
 
     }
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index cb63f53..1ecfe34 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.list;
 
+import android.accounts.Account;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
@@ -23,6 +24,7 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
 import android.provider.ContactsContract.SearchSnippets;
+import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
 import android.view.ViewGroup;
 import android.widget.ListView;
@@ -50,7 +52,9 @@
             Contacts.PHOTO_THUMBNAIL_URI,           // 5
             Contacts.LOOKUP_KEY,                    // 6
             Contacts.PHONETIC_NAME,                 // 7
-            Contacts.STARRED,                       // 9
+            Contacts.STARRED,                       // 8
+            RawContacts.ACCOUNT_TYPE,               // 9
+            RawContacts.ACCOUNT_NAME,               // 10
         };
 
         private static final String[] CONTACT_PROJECTION_ALTERNATIVE = new String[] {
@@ -63,6 +67,8 @@
             Contacts.LOOKUP_KEY,                    // 6
             Contacts.PHONETIC_NAME,                 // 7
             Contacts.STARRED,                       // 8
+            RawContacts.ACCOUNT_TYPE,               // 9
+            RawContacts.ACCOUNT_NAME,               // 10
         };
 
         private static final String[] FILTER_PROJECTION_PRIMARY = new String[] {
@@ -75,7 +81,9 @@
             Contacts.LOOKUP_KEY,                    // 6
             Contacts.PHONETIC_NAME,                 // 7
             Contacts.STARRED,                       // 8
-            SearchSnippets.SNIPPET,                 // 9
+            RawContacts.ACCOUNT_TYPE,               // 9
+            RawContacts.ACCOUNT_NAME,               // 10
+            SearchSnippets.SNIPPET,                 // 11
         };
 
         private static final String[] FILTER_PROJECTION_ALTERNATIVE = new String[] {
@@ -88,7 +96,9 @@
             Contacts.LOOKUP_KEY,                    // 6
             Contacts.PHONETIC_NAME,                 // 7
             Contacts.STARRED,                       // 8
-            SearchSnippets.SNIPPET,                 // 9
+            RawContacts.ACCOUNT_TYPE,               // 9
+            RawContacts.ACCOUNT_NAME,               // 10
+            SearchSnippets.SNIPPET,                 // 11
         };
 
         public static final int CONTACT_ID               = 0;
@@ -100,7 +110,9 @@
         public static final int CONTACT_LOOKUP_KEY       = 6;
         public static final int CONTACT_PHONETIC_NAME    = 7;
         public static final int CONTACT_STARRED          = 8;
-        public static final int CONTACT_SNIPPET          = 9;
+        public static final int CONTACT_ACCOUNT_TYPE     = 9;
+        public static final int CONTACT_ACCOUNT_NAME     = 10;
+        public static final int CONTACT_SNIPPET          = 11;
     }
 
     private CharSequence mUnknownNameText;
@@ -252,9 +264,15 @@
         if (!cursor.isNull(ContactQuery.CONTACT_PHOTO_ID)) {
             photoId = cursor.getLong(ContactQuery.CONTACT_PHOTO_ID);
         }
-
+        Account account = null;
+        if (!cursor.isNull(ContactQuery.CONTACT_ACCOUNT_TYPE)
+                && !cursor.isNull(ContactQuery.CONTACT_ACCOUNT_NAME)) {
+            final String accountType = cursor.getString(ContactQuery.CONTACT_ACCOUNT_TYPE);
+            final String accountName = cursor.getString(ContactQuery.CONTACT_ACCOUNT_NAME);
+            account = new Account(accountName, accountType);
+        }
         if (photoId != 0) {
-            getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false,
+            getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, account, false,
                     getCircularPhotos(), null);
         } else {
             final String photoUriString = cursor.getString(ContactQuery.CONTACT_PHOTO_URI);
@@ -265,7 +283,7 @@
                         ContactQuery.CONTACT_DISPLAY_NAME,
                         ContactQuery.CONTACT_LOOKUP_KEY);
             }
-            getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false,
+            getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, account, false,
                     getCircularPhotos(), request);
         }
     }
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
index 32e4b9c..5f93a0a 100644
--- a/src/com/android/contacts/list/ContactListFilter.java
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -45,6 +45,7 @@
     public static final int FILTER_TYPE_SINGLE_CONTACT = -6;
     public static final int FILTER_TYPE_GROUP_MEMBERS = -7;
     public static final int FILTER_TYPE_DEVICE_CONTACTS = -8;
+    public static final int FILTER_TYPE_ALL_WITHOUT_SIM = -9;
 
     public static final int FILTER_TYPE_ACCOUNT = 0;
 
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index 504828f..a1a460a 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -136,7 +136,7 @@
             HeaderEntryContactListAdapter adapter
                     = new HeaderEntryContactListAdapter(getActivity());
             adapter.setFilter(ContactListFilter.createFilterWithType(
-                    ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+                    ContactListFilter.FILTER_TYPE_ALL_WITHOUT_SIM));
             adapter.setSectionHeaderDisplayEnabled(true);
             adapter.setDisplayPhotos(true);
             adapter.setQuickContactEnabled(false);
diff --git a/src/com/android/contacts/list/ContactsFragment.java b/src/com/android/contacts/list/ContactsFragment.java
new file mode 100644
index 0000000..778cd6d
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsFragment.java
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.contacts.list;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.ListFragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.AsyncQueryHandler;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.AbsListView;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+import android.widget.ImageView;
+
+import com.android.contacts.activities.MultiPickContactsActivity;
+import com.android.contacts.ContactPhotoManager;
+import com.android.contacts.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.format.TextHighlighter;
+import com.android.contacts.list.AccountFilterActivity;
+import com.android.contacts.list.ContactsSectionIndexer;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.SimContactsConstants;
+import com.android.contacts.util.UriUtils;
+import com.android.contacts.list.ContactsPickMode;
+import com.android.contacts.list.OnCheckListActionListener;
+import com.android.contacts.model.account.AccountWithDataSet;
+import com.android.contacts.R;
+
+import java.util.ArrayList;
+
+public class ContactsFragment extends ListFragment {
+    private final static String TAG = "ContactsFragment";
+    private final static boolean DEBUG = true;
+    private static final String SORT_ORDER = " desc";
+
+    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
+            Contacts._ID, // 0
+            Contacts.NAME_RAW_CONTACT_ID, // 1
+            Contacts.LOOKUP_KEY, // 2
+            Contacts.DISPLAY_NAME_PRIMARY, // 3
+            Contacts.PHOTO_ID, // 4
+            Contacts.PHOTO_THUMBNAIL_URI, // 5
+            RawContacts.ACCOUNT_TYPE, // 6
+            RawContacts.ACCOUNT_NAME, // 7
+    };
+    static final String CONTACTS_SELECTION = Contacts.IN_VISIBLE_GROUP + "=1";
+    static final String LOCAL_SELECTION = RawContacts.ACCOUNT_TYPE + " IS NULL ";
+
+    private static final String[] DATA_PROJECTION = new String[] {
+            Data._ID, // 0
+            Data.CONTACT_ID, // 1
+            Contacts.LOOKUP_KEY, // 2
+            Data.DISPLAY_NAME, // 3
+            Contacts.PHOTO_ID,// 4
+            Contacts.PHOTO_THUMBNAIL_URI, // 5
+            RawContacts.ACCOUNT_TYPE, // 6
+            RawContacts.ACCOUNT_NAME, // 7
+            Data.DATA1, // 8 Phone.NUMBER, Email.address
+            Data.DATA2, // 9 phone.type
+            Data.DATA3, // 10 Phone.LABEL
+            Data.MIMETYPE, //11
+    };
+
+    // contacts column
+    private static final int SUMMARY_ID_COLUMN_INDEX = 0;
+    private static final int SUMMARY_COLUMN_CONTACT_ID = 1;
+    private static final int SUMMARY_LOOKUP_KEY_COLUMN_INDEX = 2;
+    private static final int SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 3;
+    private static final int SUMMARY_PHOTO_ID_COLUMN_INDEX = 4;
+    private static final int SUMMARY_CONTACT_COLUMN_PHOTO_URI = 5;
+    private static final int SUMMARY_ACCOUNT_TYPE = 6;
+    private static final int SUMMARY_ACCOUNT_NAME = 7;
+
+    private static final int DATA_DATA1_COLUMN = 8;
+    private static final int DATA_DATA2_COLUMN = 9;  //phone.type
+    private static final int DATA_DATA3_COLUMN = 10; //Phone.LABEL
+    private static final int DATA_MIMETYPE_COLUMN = 11;
+
+    private static final int QUERY_TOKEN = 43;
+
+    private int subscription;
+    private QueryHandler mQueryHandler;
+    private Bundle mChoiceSet;
+    private TextView mSelectAllLabel;
+    private Intent mIntent;
+    private Context mContext;
+    private ContactsPickMode mPickMode;
+    private int mMode;
+    private OnCheckListActionListener mCheckListListener;
+    private ContactItemListAdapter mContactListAdapter;
+    private String query;
+    private View mRootView;
+    private SectionIndexer mIndexer;
+    private View mHeaderView;
+    private ContactListFilter mFilter;
+
+    /**
+     * An item view is displayed differently depending on whether it is placed at the beginning,
+     * middle or end of a section. It also needs to know the section header when it is at the
+     * beginning of a section. This object captures all this configuration.
+     */
+    public static final class Placement {
+        private int position = ListView.INVALID_POSITION;
+        public boolean firstInSection;
+        public boolean lastInSection;
+        public String sectionHeader;
+
+        public void invalidate() {
+            position = ListView.INVALID_POSITION;
+        }
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        mPickMode = ContactsPickMode.getInstance();
+        mMode = mPickMode.getMode();
+        mFilter = (ContactListFilter) mPickMode.getIntent().getParcelableExtra(
+                AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER);
+        if (mFilter == null)
+            mFilter = ContactListFilter
+            .restoreDefaultPreferences(PreferenceManager
+                    .getDefaultSharedPreferences(mContext));
+        if (mContactListAdapter == null) {
+            mContactListAdapter = new ContactItemListAdapter(mContext);
+        }
+
+        mHeaderView = new View(mContext);
+        AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(
+                AbsListView.LayoutParams.MATCH_PARENT,
+                (int)(mContext.getResources().getDimension(R.dimen.header_listview_height)));
+        mHeaderView.setLayoutParams(layoutParams);
+        getListView().addHeaderView(mHeaderView, null, false);
+        setListAdapter(mContactListAdapter);
+        mQueryHandler = new QueryHandler(mContext);
+        startQuery();
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mContext = (MultiPickContactsActivity) activity;
+    }
+
+    @Override
+    public void onResume() {
+        // ContactsPickMode is singleton, its mode may be changed by other mode.
+        // need to reset
+        mPickMode.setMode(mMode);
+        super.onResume();
+    }
+
+    @Override
+    public void onStop() {
+        mMode = mPickMode.getMode();
+        super.onStop();
+    }
+
+    public void setCheckListListener(OnCheckListActionListener checkListListener) {
+        mCheckListListener = checkListListener;
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        mCheckListListener.onHideSoftKeyboard();
+        CheckBox checkBox = (CheckBox) v.findViewById(R.id.pick_contact_check);
+        boolean isChecked = !checkBox.isChecked();
+        checkBox.setChecked(isChecked);
+        ContactItemCache cache = (ContactItemCache) v.getTag();
+        String key = String.valueOf(cache.id);
+        if (!mCheckListListener.onContainsKey(key)) {
+            String[] value = null;
+            if (mPickMode.isPickContact()) {
+                value = new String[] {
+                        cache.lookupKey, key,
+                        String.valueOf(cache.nameRawContactId),
+                        cache.photoUri == null ? null : String
+                                .valueOf(cache.photoUri), cache.name,
+                                cache.accountType, cache.accountName};
+            } else if (mPickMode.isPickPhone()) {
+                value = new String[] { cache.name, cache.number, cache.type,
+                        cache.label, cache.contact_id };
+            } else if (mPickMode.isPickEmail()) {
+                value = new String[] {cache.name, cache.email};
+            }
+            mCheckListListener.putValue(key, value);
+        } else {
+            mCheckListListener.onRemove(key);
+        }
+        mCheckListListener.exitSearch();
+        mCheckListListener.onUpdateActionBar();
+        mContactListAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        mContext = null;
+    }
+
+    @Override
+    public void onDestroy() {
+        mQueryHandler.removeCallbacksAndMessages(QUERY_TOKEN);
+
+        if (mContactListAdapter.getCursor() != null) {
+            mContactListAdapter.getCursor().close();
+        }
+        super.onDestroy();
+    }
+
+    private Uri getUriToQuery() {
+        Uri uri;
+        switch (mPickMode.getMode()) {
+            case ContactsPickMode.MODE_DEFAULT_CONTACT:
+            case ContactsPickMode.MODE_SEARCH_CONTACT:
+                uri = Contacts.CONTENT_URI;
+                break;
+            case ContactsPickMode.MODE_DEFAULT_EMAIL:
+            case ContactsPickMode.MODE_SEARCH_EMAIL:
+                uri = Email.CONTENT_URI;
+                break;
+            case ContactsPickMode.MODE_DEFAULT_PHONE:
+            case ContactsPickMode.MODE_SEARCH_PHONE:
+                uri = Phone.CONTENT_URI;
+                uri = uri.buildUpon().appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                        String.valueOf(Directory.DEFAULT))
+                        .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true")
+                        .build();
+                break;
+            default:
+                uri = Contacts.CONTENT_URI;
+        }
+        return uri.buildUpon().appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true")
+                .build();
+    }
+
+    private Uri getFilterUri() {
+        switch (mPickMode.getMode()) {
+            case ContactsPickMode.MODE_SEARCH_CONTACT:
+                return Contacts.CONTENT_FILTER_URI;
+            case ContactsPickMode.MODE_SEARCH_PHONE:
+                return Phone.CONTENT_FILTER_URI;
+            case ContactsPickMode.MODE_SEARCH_EMAIL:
+                return Email.CONTENT_FILTER_URI;
+            default:
+                log("getFilterUri: Incorrect mode: " + mPickMode.getMode());
+        }
+        return Contacts.CONTENT_FILTER_URI;
+    }
+
+    public String[] getProjectionForQuery() {
+        switch (mPickMode.getMode()) {
+            case ContactsPickMode.MODE_DEFAULT_CONTACT:
+            case ContactsPickMode.MODE_SEARCH_CONTACT:
+                return CONTACTS_SUMMARY_PROJECTION;
+            case ContactsPickMode.MODE_DEFAULT_PHONE:
+            case ContactsPickMode.MODE_SEARCH_PHONE:
+            case ContactsPickMode.MODE_DEFAULT_EMAIL:
+            case ContactsPickMode.MODE_SEARCH_EMAIL:
+                return DATA_PROJECTION;
+            default:
+                log("getProjectionForQuery: Incorrect mode: " + mPickMode.getMode());
+        }
+        return CONTACTS_SUMMARY_PROJECTION;
+    }
+
+    private String getSortOrder(String[] projection) {
+        return RawContacts.SORT_KEY_PRIMARY;
+    }
+
+    private String getSelectionForAccount() {
+        @SuppressWarnings("deprecation")
+        StringBuilder selection = new StringBuilder();
+        if(mFilter == null)
+            return null;
+        switch (mFilter.filterType) {
+            case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS:
+                return null;
+            case ContactListFilter.FILTER_TYPE_CUSTOM:
+                return CONTACTS_SELECTION;
+            case ContactListFilter.FILTER_TYPE_ACCOUNT:
+                if (mPickMode.isSearchMode())
+                    selection.append(RawContacts.ACCOUNT_TYPE).append("='")
+                        .append(mFilter.accountType).append("' AND ")
+                        .append(RawContacts.ACCOUNT_NAME).append("='")
+                        .append(mFilter.accountName).append("'");
+                return selection.toString();
+            case ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS:
+                return LOCAL_SELECTION;
+        }
+        return null;
+    }
+
+    public void startQuery() {
+        Uri uri = getUriToQuery();
+        if (mFilter != null
+                && mFilter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+            // We should exclude the invisiable contacts.
+            uri = uri.buildUpon()
+                    .appendQueryParameter(RawContacts.ACCOUNT_NAME,
+                            mFilter.accountName)
+                    .appendQueryParameter(RawContacts.ACCOUNT_TYPE,
+                            mFilter.accountType)
+                    .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+                            ContactsContract.Directory.DEFAULT + "").build();
+        }
+
+        String[] projection = getProjectionForQuery();
+        String selection = getSelectionForAccount();
+        mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, selection, null,
+                getSortOrder(projection));
+    }
+
+    public void doFilter(String s) {
+        query = s;
+        if (TextUtils.isEmpty(s)) {
+            mContactListAdapter.changeCursor(null);
+            return;
+        }
+        Uri uri = Uri.withAppendedPath(getFilterUri(), Uri.encode(query));
+        String[] projection = getProjectionForQuery();
+        String selection = getSelectionForAccount();
+        mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, selection, null,
+                getSortOrder(projection));
+    }
+
+    private class QueryHandler extends AsyncQueryHandler {
+        public QueryHandler(Context context) {
+            super(context.getContentResolver());
+        }
+
+        @Override
+        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+            if (mHeaderView != null && mPickMode.isSearchMode()) {
+                getListView().removeHeaderView(mHeaderView);
+            }
+            mContactListAdapter.changeCursor(cursor);
+        }
+    }
+
+    private class ContactItemCache {
+        long id;
+        String name;
+        String number;
+        String lookupKey;
+        String type;
+        String label;
+        String contact_id;
+        String email;
+        String accountType;
+        String accountName;
+        long nameRawContactId;
+        Uri photoUri;
+        Uri contactUri;
+        long photoId;
+    }
+
+    public class ContactItemListAdapter extends CursorAdapter
+            implements SectionIndexer {
+        protected LayoutInflater mInflater;
+        private ContactPhotoManager mContactPhotoManager;
+        private final TextHighlighter mTextHighlighter;
+        private Placement mPlacement = new Placement();
+
+        public ContactItemListAdapter(Context context) {
+            super(context, null, false);
+            mInflater = (LayoutInflater) mContext
+                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
+            mTextHighlighter = new TextHighlighter(Typeface.BOLD);
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            ContactItemCache cache = (ContactItemCache) view.getTag();
+            if (mPickMode.isPickContact()) {
+                cache.id = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
+                cache.lookupKey = cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
+                cache.name = cursor.getString(SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                cache.nameRawContactId = cursor.getLong(SUMMARY_COLUMN_CONTACT_ID);
+                cache.accountType = cursor.getString(SUMMARY_ACCOUNT_TYPE);
+                cache.accountName = cursor.getString(SUMMARY_ACCOUNT_NAME);
+                ((TextView) view.findViewById(R.id.pick_contact_name))
+                        .setText(cache.name == null ? "" : cache.name);
+                view.findViewById(R.id.pick_contact_number).setVisibility(View.GONE);
+                setPhotoView(view, cursor, cache);
+                setHeaderAndHighLightIfNeed(view, cache, cursor);
+            } else if (mPickMode.isPickPhone()) {
+                cache.id = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
+                cache.name = cursor.getString(SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                cache.number = cursor.getString(DATA_DATA1_COLUMN);
+                cache.label = cursor.getString(DATA_DATA3_COLUMN);
+                cache.type = String.valueOf(cursor.getInt(DATA_DATA2_COLUMN));
+                ((TextView) view.findViewById(R.id.pick_contact_number)).setText(cache.number);
+                setPhotoView(view, cursor, cache);
+                setItemView(view, cursor, cache);
+                setLabel(view, cursor);
+                setHeaderAndHighLightIfNeed(view, cache, cursor);
+            } else if (mPickMode.isPickEmail()) {
+                cache.id = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
+                cache.name = cursor.getString(SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                cache.email = cursor.getString(DATA_DATA1_COLUMN);
+                ((TextView) view.findViewById(R.id.pick_contact_number)).setText(cache.email);
+                setPhotoView(view, cursor, cache);
+                setItemView(view, cursor, cache);
+                setLabel(view, cursor);
+                setHeaderAndHighLightIfNeed(view, cache, cursor);
+            }
+
+            CheckBox checkBox = (CheckBox) view.findViewById(R.id.pick_contact_check);
+            if (mCheckListListener.onContainsKey(String.valueOf(cache.id))) {
+                checkBox.setChecked(true);
+            } else {
+                checkBox.setChecked(false);
+            }
+        }
+
+        private void setHeaderAndHighLightIfNeed(View view, ContactItemCache cache,
+                Cursor cursor) {
+            if (mPickMode.isSearchMode()) {
+                hideSectionHeader(view);
+                if (!TextUtils.isEmpty(query)) {
+                    setFilterHighLight(view, cache);
+                }
+            } else {
+                bindSectionHeader(view, cursor.getPosition());
+            }
+        }
+
+        private void setFilterHighLight(View view, ContactItemCache cache) {
+            TextView nameView = (TextView) view.findViewById(R.id.pick_contact_name);
+            CharSequence nameText = cache.name;
+            nameText = mTextHighlighter.applyPrefixHighlight(nameText, query.toUpperCase());
+            nameView.setText(nameText);
+            TextView numberView = (TextView) view.findViewById(R.id.pick_contact_number);
+            if (mPickMode.isPickEmail()) {
+                CharSequence emailText = cache.email;
+                emailText = mTextHighlighter.applyPrefixHighlight(emailText, query.toUpperCase());
+                numberView.setText(emailText);
+            } else {
+                CharSequence numberText = cache.number;
+                numberText = mTextHighlighter.applyPrefixHighlight(numberText, query.toUpperCase());
+                numberView.setText(numberText);
+            }
+        }
+
+        private void setLabel(View view, Cursor cursor) {
+            TextView labelView = (TextView) view.findViewById(R.id.label);
+            CharSequence label = null;
+            if (!cursor.isNull(DATA_DATA2_COLUMN)) {
+                final int type = cursor.getInt(DATA_DATA2_COLUMN);
+                final String customLabel = cursor.getString(DATA_DATA3_COLUMN);
+                label = Phone.getTypeLabel(mContext.getResources(), type, customLabel);
+            }
+            labelView.setText(label);
+        }
+
+        private void setItemView(View view, Cursor cursor, ContactItemCache cache) {
+            ImageView photoView = (ImageView) view
+                    .findViewById(R.id.pick_contact_photo);
+            boolean isFirstEntry = true;
+            if (!cursor.isNull(SUMMARY_COLUMN_CONTACT_ID)) {
+                long currentContactId = cursor.getLong(SUMMARY_COLUMN_CONTACT_ID);
+                int position = cursor.getPosition();
+                if (!cursor.isFirst() && cursor.moveToPrevious()) {
+                    if (!cursor.isNull(SUMMARY_COLUMN_CONTACT_ID)) {
+                        final long previousContactId = cursor.getLong(SUMMARY_COLUMN_CONTACT_ID);
+                        if (currentContactId == previousContactId) {
+                            isFirstEntry = false;
+                        }
+                    }
+                }
+                cursor.moveToPosition(position);
+            }
+
+            if (isFirstEntry) {
+                view.getLayoutParams().height = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.pick_contact_first_item_height);
+                photoView.setVisibility(View.VISIBLE);
+                view.findViewById(R.id.pick_contact_name).setVisibility(View.VISIBLE);
+                ((TextView) view.findViewById(R.id.pick_contact_name))
+                        .setText(cache.name == null ? "" : cache.name);
+            } else {
+                view.getLayoutParams().height = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.pick_contact_same_item_height);
+                photoView.setVisibility(View.INVISIBLE);
+                view.findViewById(R.id.pick_contact_name).setVisibility(View.GONE);
+            }
+        }
+
+        private void setPhotoView(View view, Cursor cursor, ContactItemCache cache) {
+            ImageView photoView = ((ImageView) view
+                    .findViewById(R.id.pick_contact_photo));
+            photoView.setVisibility(View.VISIBLE);
+
+            if (!cursor.isNull(SUMMARY_PHOTO_ID_COLUMN_INDEX)) {
+                cache.photoId = cursor.getLong(SUMMARY_PHOTO_ID_COLUMN_INDEX);
+            } else {
+                cache.photoId = 0;
+            }
+            if (!cursor.isNull(SUMMARY_CONTACT_COLUMN_PHOTO_URI)) {
+                cache.photoUri = UriUtils.parseUriOrNull(cursor
+                        .getString(SUMMARY_CONTACT_COLUMN_PHOTO_URI));
+            } else {
+                cache.photoUri = null;
+            }
+            Account account = null;
+            if (!cursor.isNull(SUMMARY_ACCOUNT_TYPE)
+                    && !cursor.isNull(SUMMARY_ACCOUNT_NAME)) {
+                final String accountType = cursor
+                        .getString(SUMMARY_ACCOUNT_TYPE);
+                final String accountName = cursor
+                        .getString(SUMMARY_ACCOUNT_NAME);
+                account = new Account(accountName, accountType);
+            }
+
+            if (cache.photoId != 0) {
+                mContactPhotoManager.loadThumbnail(photoView, cache.photoId, account, false, true,
+                        null);
+            } else {
+                final Uri photoUri = cache.photoUri == null ? null : cache.photoUri;
+                DefaultImageRequest request = null;
+                if (photoUri == null) {
+                    cache.lookupKey = cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
+                    request = new DefaultImageRequest(cache.name, cache.lookupKey, true);
+                }
+                mContactPhotoManager.loadDirectoryPhoto(photoView, photoUri, account, false, true,
+                        request);
+            }
+        }
+
+        @Override
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            View v = null;
+            v = mInflater.inflate(R.layout.multi_pick_contact_item, parent, false);
+            ContactItemCache dataCache = new ContactItemCache();
+            v.setTag(dataCache);
+            return v;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            View v;
+            if (!getCursor().moveToPosition(position)) {
+                throw new IllegalStateException("couldn't move cursor to position " + position);
+            }
+            if (convertView != null && convertView.getTag() != null) {
+                v = convertView;
+            } else {
+                v = newView(mContext, getCursor(), parent);
+            }
+            bindView(v, mContext, getCursor());
+            return v;
+        }
+
+        @Override
+        public void changeCursor(Cursor cursor) {
+            super.changeCursor(cursor);
+            updateIndexer(cursor);
+        }
+
+        private void bindSectionHeader(View view, int position) {
+            TextView section = (TextView) view.findViewById(R.id.section_index);
+            section.setVisibility(View.VISIBLE);
+            Placement placement = getItemPlacementInSection(position);
+            section.setText(placement.sectionHeader);
+            section.setTextAppearance(mContext, R.style.SectionHeaderStyle);
+        }
+
+        private void hideSectionHeader(View view) {
+            TextView section = (TextView) view.findViewById(R.id.section_index);
+            section.setVisibility(View.GONE);
+        }
+
+        /**
+         * Updates the indexer, which is used to produce section headers.
+         */
+        private void updateIndexer(Cursor cursor) {
+            if (cursor == null) {
+                setIndexer(null);
+                return;
+            }
+            Bundle bundle = cursor.getExtras();
+            if (bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES)
+                    && bundle.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS)) {
+                String sections[] = bundle.getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+                int counts[] = bundle.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+                setIndexer(new ContactsSectionIndexer(sections, counts));
+            } else {
+                setIndexer(null);
+            }
+        }
+
+        public void setIndexer(SectionIndexer index) {
+            mIndexer = index;
+            mPlacement.invalidate();
+        }
+
+        public int getPositionForSection(int sectionIndex) {
+            if (mIndexer != null) {
+                return mIndexer.getPositionForSection(sectionIndex);
+            }
+            return -1;
+        }
+
+        public Object[] getSections() {
+            if (mIndexer != null) {
+                return mIndexer.getSections();
+            } else {
+                return new String[] {" "};
+            }
+        }
+
+        public int getSectionForPosition(int position) {
+            if (mIndexer != null) {
+                return mIndexer.getSectionForPosition(position);
+            }
+            return -1;
+        }
+
+        /**
+         * Computes the item's placement within its section and populates the {@code placement}
+         * object accordingly. Please note that the returned object is volatile and should be copied
+         * if the result needs to be used later.
+         */
+        public Placement getItemPlacementInSection(int position) {
+            if (mPlacement.position == position) {
+                return mPlacement;
+            }
+            mPlacement.position = position;
+            int section = getSectionForPosition(position);
+            if (section != -1 && getPositionForSection(section) == position) {
+                mPlacement.firstInSection = true;
+                mPlacement.sectionHeader = (String) getSections()[section];
+            } else {
+                mPlacement.firstInSection = false;
+                mPlacement.sectionHeader = null;
+            }
+
+            mPlacement.lastInSection = (getPositionForSection(section + 1) - 1 == position);
+            return mPlacement;
+        }
+    }
+
+    protected static void log(String msg) {
+        if (DEBUG)
+            Log.d(TAG, msg);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        mRootView = inflater.inflate(R.layout.multi_pick_contacts_fragment, container, false);
+        return mRootView;
+    }
+
+    /**
+     * @param isSelectedAll isSelectedAll is true, selected all contacts
+     * isSelectedAll is False, deselected all contacts
+     */
+    public void setSelectedAll(boolean isSelectedAll) {
+        Cursor cursor = mContactListAdapter.getCursor();
+        if (cursor == null) {
+            return;
+        }
+        ContactItemCache cache = new ContactItemCache();
+        String key;
+        // selected all contacts
+        if (isSelectedAll) {
+            int count = cursor.getCount();
+            for (int i = 0; i < count; i++) {
+                cursor.moveToPosition(i);
+                key = String.valueOf(cursor.getLong(0));
+                if (!mCheckListListener.onContainsKey(key)) {
+                    String[] value = null;
+                    if (mPickMode.isPickContact()) {
+                        cache.lookupKey = cursor
+                                .getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
+                        cache.name = cursor
+                                .getString(SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                        cache.nameRawContactId = cursor
+                                .getLong(SUMMARY_COLUMN_CONTACT_ID);
+                        String photoUri = cursor.getString(SUMMARY_CONTACT_COLUMN_PHOTO_URI);
+                        cache.accountType = cursor.getString(SUMMARY_ACCOUNT_TYPE);
+                        cache.accountName = cursor.getString(SUMMARY_ACCOUNT_NAME);
+                        cache.photoUri = UriUtils.parseUriOrNull(photoUri);
+                        value = new String[] { cache.lookupKey, key,
+                                String.valueOf(cache.nameRawContactId),
+                                photoUri, cache.name, cache.accountType, cache.accountName};
+                    } else if (mPickMode.isPickPhone()) {
+                        cache.name = cursor
+                                .getString(SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                        cache.number = cursor.getString(DATA_DATA1_COLUMN);
+                        cache.label = cursor.getString(DATA_DATA3_COLUMN);
+                        cache.type = String.valueOf(cursor
+                                .getInt(DATA_DATA2_COLUMN));
+                        value = new String[] { cache.name, cache.number,
+                                cache.type, cache.label, cache.contact_id };
+                    } else if (mPickMode.isPickEmail()) {
+                        cache.name = cursor
+                                .getString(SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
+                        cache.email = cursor.getString(DATA_DATA1_COLUMN);
+                        value = new String[] { cache.name, cache.email };
+                    }
+                    mCheckListListener.putValue(key, value);
+                }
+            }
+        } else {
+            mCheckListListener.onClear();
+        }
+        // update actionbar selected button to display selected item numbers
+        mCheckListListener.onUpdateActionBar();
+        mContactListAdapter.notifyDataSetChanged();
+    }
+
+}
diff --git a/src/com/android/contacts/list/ContactsPickMode.java b/src/com/android/contacts/list/ContactsPickMode.java
new file mode 100644
index 0000000..b822513
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsPickMode.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package com.android.contacts.list;
+
+import com.android.contacts.SimContactsConstants;
+
+import android.content.Intent;
+
+import com.android.contacts.R;
+
+
+/**
+ * Parsed form of the intent sent to the Contacts application.
+ */
+public class ContactsPickMode {
+
+    private static ContactsPickMode mPickMode;
+
+    private static final String MODE = "mode";
+
+    private int mMode;
+    private Intent mIntent;
+
+    private static final int MODE_MASK_SEARCH = 0x80000000;
+
+    public static final int MODE_DEFAULT_CONTACT = 0;
+    public static final int MODE_DEFAULT_PHONE = 1;
+    public static final int MODE_DEFAULT_EMAIL = 1 << 1;
+    public static final int MODE_SEARCH_CONTACT = MODE_DEFAULT_CONTACT | MODE_MASK_SEARCH;
+    public static final int MODE_SEARCH_PHONE = MODE_DEFAULT_PHONE | MODE_MASK_SEARCH;
+    public static final int MODE_SEARCH_EMAIL = MODE_DEFAULT_EMAIL | MODE_MASK_SEARCH;
+
+    public static ContactsPickMode getInstance() {
+        if (mPickMode == null) {
+            mPickMode = new ContactsPickMode();
+        }
+        return mPickMode;
+    }
+
+    public boolean isSearchMode() {
+        return (mMode & MODE_MASK_SEARCH) == MODE_MASK_SEARCH;
+    }
+
+    public boolean isPickContact() {
+        return mMode == MODE_DEFAULT_CONTACT || mMode == MODE_SEARCH_CONTACT;
+    }
+
+    public boolean isPickPhone() {
+        return mMode == MODE_DEFAULT_PHONE || mMode == MODE_SEARCH_PHONE;
+    }
+
+    public boolean isPickEmail() {
+        return mMode == MODE_DEFAULT_EMAIL || mMode == MODE_SEARCH_EMAIL;
+    }
+
+    public void enterSearchMode() {
+        mMode |= MODE_MASK_SEARCH;
+    }
+
+    public void exitSearchMode() {
+        mMode &= ~MODE_MASK_SEARCH;
+    }
+
+    public void setMode(int mode) {
+        mMode = mode;
+    }
+
+    public int getMode() {
+        return mMode;
+    }
+
+    public void setMode(Intent intent) {
+        mIntent = intent;
+        String action = intent.getAction();
+        if (SimContactsConstants.ACTION_MULTI_PICK.equals(action)) {
+            mMode = MODE_DEFAULT_PHONE;
+        } else if (SimContactsConstants.ACTION_MULTI_PICK_EMAIL.equals(action)) {
+            mMode = MODE_DEFAULT_EMAIL;
+        } else {
+            mMode = MODE_DEFAULT_CONTACT;
+        }
+    }
+
+    public Intent getIntent() {
+        return mIntent;
+    }
+}
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 9851d2b..9905c93 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -76,6 +76,7 @@
 import com.android.contacts.util.ImplicitIntentsUtil;
 import com.android.contacts.util.SharedPreferenceUtil;
 import com.android.contacts.util.SyncUtil;
+import com.android.contacts.SimContactsConstants;
 import com.android.contactsbind.FeatureHighlightHelper;
 import com.android.contactsbind.experiments.Flags;
 import com.google.common.util.concurrent.Futures;
@@ -1014,9 +1015,10 @@
         final boolean showSelectedContactOptions = mActionBarAdapter.isSelectionMode()
                 && getSelectedContactIds().size() != 0;
         makeMenuItemVisible(menu, R.id.menu_share, showSelectedContactOptions);
-        makeMenuItemVisible(menu, R.id.menu_delete, showSelectedContactOptions);
+        makeMenuItemVisible(menu, R.id.menu_delete, !isSearchOrSelectionMode);
         final boolean showLinkContactsOptions = mActionBarAdapter.isSelectionMode()
-                && getSelectedContactIds().size() > 1;
+                && getSelectedContactIds().size() > 1
+                && getSelectedSimContact() <1;
         makeMenuItemVisible(menu, R.id.menu_join, showLinkContactsOptions);
 
         // Debug options need to be visible even in search mode.
@@ -1078,7 +1080,11 @@
             joinSelectedContacts();
             return true;
         } else if (id == R.id.menu_delete) {
-            deleteSelectedContacts();
+            final Intent intent = new Intent(SimContactsConstants.ACTION_MULTI_PICK_CONTACT,
+                    ContactsContract.Contacts.CONTENT_URI);
+            intent.putExtra(AccountFilterActivity.EXTRA_CONTACT_LIST_FILTER, getFilter());
+            intent.putExtra("delete", true);
+            startActivity(intent);
             return true;
         } else if (id == R.id.export_database) {
             final Intent intent = new Intent("com.android.providers.contacts.DUMP_DATABASE");
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 20c1a8f..19fb91b 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -27,10 +27,12 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.SearchSnippets;
 import android.text.TextUtils;
 import android.view.View;
 import com.android.contacts.compat.ContactsCompat;
+import com.android.contacts.SimContactsConstants;
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.preference.ContactsPreferences;
 import java.util.ArrayList;
@@ -89,12 +91,7 @@
         } else {
             final ContactListFilter filter = getFilter();
             configureUri(loader, directoryId, filter);
-            if (filter != null
-                    && filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) {
-                loader.setProjection(getDataProjectionForContacts(false));
-            } else {
-                loader.setProjection(getProjection(false));
-            }
+            loader.setProjection(getProjection(false));
             configureSelection(loader, directoryId, filter);
         }
 
@@ -140,8 +137,6 @@
                 } else {
                     uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, getSelectedContactId());
                 }
-            } else if (filter.filterType == ContactListFilter.FILTER_TYPE_DEVICE_CONTACTS) {
-                uri = Data.CONTENT_URI;
             }
         }
 
@@ -220,10 +215,15 @@
                         selectionArgs.add(filter.accountName);
                     }
                 } else {
-                    selection.append(AccountWithDataSet.LOCAL_ACCOUNT_SELECTION);
+                    selection.append(RawContacts.ACCOUNT_TYPE + " IS NULL");
                 }
                 break;
             }
+            case ContactListFilter.FILTER_TYPE_ALL_WITHOUT_SIM:
+                selection.append(RawContacts.ACCOUNT_TYPE + "!= '"
+                        + SimContactsConstants.ACCOUNT_TYPE_SIM + "'" + " OR "
+                        + RawContacts.ACCOUNT_TYPE + " IS NULL");
+                break;
         }
         loader.setSelection(selection.toString());
         loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
@@ -245,7 +245,8 @@
         if (isQuickContactEnabled()) {
             bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID,
                     ContactQuery.CONTACT_PHOTO_URI, ContactQuery.CONTACT_ID,
-                    ContactQuery.CONTACT_LOOKUP_KEY, ContactQuery.CONTACT_DISPLAY_NAME);
+                    ContactQuery.CONTACT_LOOKUP_KEY, ContactQuery.CONTACT_DISPLAY_NAME,
+                    ContactQuery.CONTACT_ACCOUNT_TYPE, ContactQuery.CONTACT_ACCOUNT_NAME);
         } else {
             if (getDisplayPhotos()) {
                 bindPhoto(view, partition, cursor);
diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java
index 82da6f7..143fcf4 100644
--- a/src/com/android/contacts/list/JoinContactListAdapter.java
+++ b/src/com/android/contacts/list/JoinContactListAdapter.java
@@ -24,6 +24,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Contacts.AggregationSuggestions;
 import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -31,6 +32,7 @@
 import android.widget.TextView;
 
 import com.android.contacts.R;
+import com.android.contacts.model.account.SimAccountType;
 import com.android.contacts.preference.ContactsPreferences;
 
 public class JoinContactListAdapter extends ContactListAdapter {
@@ -97,7 +99,9 @@
                 .build();
         }
         loader.setUri(allContactsUri);
-        loader.setSelection(Contacts._ID + "!=?");
+        loader.setSelection(Contacts._ID + "!=?" + " AND (" + RawContacts.ACCOUNT_TYPE + "!= '"
+                + SimAccountType.ACCOUNT_TYPE + "'" + " OR "
+                + RawContacts.ACCOUNT_TYPE + " IS NULL)");
         loader.setSelectionArgs(new String[]{ String.valueOf(mTargetContactId) });
         if (getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY) {
             loader.setSortOrder(Contacts.SORT_KEY_PRIMARY);
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index 5e7f9e8..657451f 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -44,6 +44,7 @@
 import com.android.contacts.model.account.AccountType;
 import com.android.contacts.model.account.AccountWithDataSet;
 import com.android.contacts.model.account.GoogleAccountType;
+import com.android.contacts.model.account.SimAccountType;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -67,6 +68,7 @@
     }
 
     private static final String EXTRA_KEY_SELECTED_CONTACTS = "selected_contacts";
+    private static final String EXTRA_KEY_SELECTED_SIM_CONTACT ="selected_sim";
 
     private OnCheckBoxListActionListener mCheckBoxListListener;
 
@@ -99,7 +101,10 @@
         if (savedInstanceState != null) {
             final TreeSet<Long> selectedContactIds = (TreeSet<Long>)
                     savedInstanceState.getSerializable(EXTRA_KEY_SELECTED_CONTACTS);
+            final int selectSimContact = savedInstanceState
+                    .getInt(EXTRA_KEY_SELECTED_SIM_CONTACT);
             getAdapter().setSelectedContactIds(selectedContactIds);
+            getAdapter().setSelectedSimContact(selectSimContact);
         }
     }
 
@@ -119,6 +124,10 @@
         return getAdapter().getSelectedContactIdsArray();
     }
 
+    public int getSelectedSimContact() {
+        return getAdapter().getSelectedSimContact();
+    }
+
     @Override
     protected void configureAdapter() {
         super.configureAdapter();
@@ -129,6 +138,7 @@
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putSerializable(EXTRA_KEY_SELECTED_CONTACTS, getSelectedContactIds());
+        outState.putInt(EXTRA_KEY_SELECTED_SIM_CONTACT, getSelectedSimContact());
     }
 
     public void displayCheckBoxes(boolean displayCheckBoxes) {
@@ -149,11 +159,12 @@
         final int previouslySelectedCount = getAdapter().getSelectedContactIds().size();
         final long contactId = getContactId(position);
         final int partition = getAdapter().getPartitionForPosition(position);
+        final boolean simContact = isSimContact(position);
         if (contactId >= 0 && partition == ContactsContract.Directory.DEFAULT) {
             if (mCheckBoxListListener != null) {
                 mCheckBoxListListener.onStartDisplayingCheckBoxes();
             }
-            getAdapter().toggleSelectionOfContactId(contactId);
+            getAdapter().toggleSelectionOfContactId(contactId, simContact);
             Logger.logListEvent(ActionType.SELECT, getListType(),
                     /* count */ getAdapter().getCount(), /* clickedIndex */ position,
                     /* numSelected */ 1);
@@ -178,11 +189,12 @@
     @Override
     protected void onItemClick(int position, long id) {
         final long contactId = getContactId(position);
+        final boolean simContact = isSimContact(position);
         if (contactId < 0) {
             return;
         }
         if (getAdapter().isDisplayingCheckBoxes()) {
-            getAdapter().toggleSelectionOfContactId(contactId);
+            getAdapter().toggleSelectionOfContactId(contactId, simContact);
         }
         if (mCheckBoxListListener != null && getAdapter().getSelectedContactIds().size() == 0) {
             mCheckBoxListListener.onStopDisplayingCheckBoxes();
@@ -203,6 +215,20 @@
         return -1;
     }
 
+    private boolean isSimContact(int position) {
+        final Cursor cursor = (Cursor) getAdapter().getItem(position);
+        final int accountTypeColumnIndex = cursor
+                .getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE);
+        if (cursor != null && accountTypeColumnIndex >= 0) {
+            if ((cursor.getColumnCount() > accountTypeColumnIndex)) {
+                final String accountType = cursor.getString(accountTypeColumnIndex);
+                return accountType != null
+                        && accountType.equals(SimAccountType.ACCOUNT_TYPE);
+            }
+        }
+        return false;
+    }
+
     /**
      * Returns the state of the search results currently presented to the user.
      */
diff --git a/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java b/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java
index a0b4f3c..99aa056 100644
--- a/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java
+++ b/src/com/android/contacts/list/MultiSelectEntryContactListAdapter.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.list;
 
+import android.accounts.Account;
 import android.content.Context;
 import android.database.Cursor;
 import android.provider.ContactsContract;
@@ -38,6 +39,8 @@
     private TreeSet<Long> mSelectedContactIds = new TreeSet<>();
     private boolean mDisplayCheckBoxes;
     private final int mContactIdColumnIndex;
+    //indicate how many sim contact has been selected
+    private int mSimContactSelected = 0;
 
     public interface SelectedContactsListener {
         void onSelectedContactsChanged();
@@ -89,6 +92,14 @@
         return mSelectedContactIds.size() > 0;
     }
 
+    public int getSelectedSimContact() {
+        return mSimContactSelected;
+    }
+
+    public void setSelectedSimContact(int selectedCount) {
+        this.mSimContactSelected = selectedCount;
+    }
+
     /**
      * Returns the selected contacts as an array.
      */
@@ -130,10 +141,18 @@
      * Toggle the checkbox beside the contact for {@param contactId}.
      */
     public void toggleSelectionOfContactId(long contactId) {
+        toggleSelectionOfContactId(contactId, false);
+    }
+
+    public void toggleSelectionOfContactId(long contactId, boolean simContact) {
         if (mSelectedContactIds.contains(contactId)) {
             mSelectedContactIds.remove(contactId);
+            if(simContact)
+                mSimContactSelected --;
         } else {
             mSelectedContactIds.add(contactId);
+            if(simContact)
+                mSimContactSelected ++;
         }
         notifyDataSetChanged();
         if (mSelectedContactsListener != null) {
@@ -165,15 +184,28 @@
       * @param displayNameColumn Index of the display name column
       */
     protected void bindPhoto(final ContactListItemView view, final Cursor cursor,
-           final int photoIdColumn, final int lookUpKeyColumn, final int displayNameColumn) {
+            final int photoIdColumn, final int lookUpKeyColumn, final int displayNameColumn) {
+        bindPhoto(view, cursor, photoIdColumn, lookUpKeyColumn, displayNameColumn, -1, -1);
+    }
+
+    protected void bindPhoto(final ContactListItemView view, final Cursor cursor,
+           final int photoIdColumn, final int lookUpKeyColumn, final int displayNameColumn
+           , final int accountTypeColumn, final int accountNameColumn) {
         final long photoId = cursor.isNull(photoIdColumn)
             ? 0 : cursor.getLong(photoIdColumn);
+        Account account = null;
+        if (!cursor.isNull(accountTypeColumn)
+                && !cursor.isNull(accountNameColumn)) {
+            final String accountType = cursor.getString(accountTypeColumn);
+            final String accountName = cursor.getString(accountNameColumn);
+            account = new Account(accountName, accountType);
+        }
         final ContactPhotoManager.DefaultImageRequest imageRequest = photoId == 0
             ? getDefaultImageRequestFromCursor(cursor, displayNameColumn,
             lookUpKeyColumn)
             : null;
-        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
-                imageRequest);
+        getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, account, false,
+                getCircularPhotos(), imageRequest);
     }
 
     private void bindCheckBox(ContactListItemView view, Cursor cursor, boolean isLocalDirectory) {
diff --git a/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java b/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java
index 30d5429..d06abfe 100644
--- a/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java
+++ b/src/com/android/contacts/list/MultiSelectPhoneNumbersListAdapter.java
@@ -23,6 +23,7 @@
 import android.net.Uri.Builder;
 import android.os.Bundle;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.RawContacts;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.text.TextUtils;
 import android.view.View;
@@ -45,6 +46,8 @@
                 Phone.PHOTO_ID,                     // 6
                 Phone.DISPLAY_NAME_PRIMARY,         // 7
                 Phone.PHOTO_THUMBNAIL_URI,          // 8
+                RawContacts.ACCOUNT_TYPE,           // 9
+                RawContacts.ACCOUNT_NAME,           // 10
         };
 
         public static final String[] PROJECTION_ALTERNATIVE = new String[] {
@@ -57,6 +60,8 @@
                 Phone.PHOTO_ID,                     // 6
                 Phone.DISPLAY_NAME_ALTERNATIVE,     // 7
                 Phone.PHOTO_THUMBNAIL_URI,          // 8
+                RawContacts.ACCOUNT_TYPE,           // 9
+                RawContacts.ACCOUNT_NAME,           // 10
         };
 
         public static final int PHONE_ID                = 0;
@@ -68,6 +73,8 @@
         public static final int PHOTO_ID                = 6;
         public static final int DISPLAY_NAME            = 7;
         public static final int PHOTO_URI               = 8;
+        public static final int ACCOUNT_TYPE            = 9;
+        public static final int ACCOUNT_NAME            = 10;
     }
 
     private final CharSequence mUnknownNameText;
@@ -160,7 +167,7 @@
         if (isFirstEntry) {
             bindName(view, cursor);
             bindPhoto(view, cursor, PhoneQuery.PHOTO_ID, PhoneQuery.LOOKUP_KEY,
-                PhoneQuery.DISPLAY_NAME);
+                PhoneQuery.DISPLAY_NAME, PhoneQuery.ACCOUNT_TYPE, PhoneQuery.ACCOUNT_NAME);
         } else {
             unbindName(view);
             view.removePhotoView(true, false);
diff --git a/src/com/android/contacts/list/OnCheckListActionListener.java b/src/com/android/contacts/list/OnCheckListActionListener.java
new file mode 100644
index 0000000..0586df8
--- /dev/null
+++ b/src/com/android/contacts/list/OnCheckListActionListener.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.contacts.list;
+
+import java.util.List;
+
+public interface OnCheckListActionListener {
+
+    /**
+     * judge mChoiceSet contain key
+     *
+     * @key represent data id, contact id
+     */
+    boolean onContainsKey(String key);
+
+    /**
+     * put value to mChoiceSet
+     */
+    void putValue(String key, String[] value);
+
+    /**
+     * remove value from mChoiceSet
+     */
+    void onRemove(String key);
+
+    /**
+     * clear mChoiceSet
+     */
+    void onClear();
+
+    /**
+     * hide softkeyboard
+     */
+    void onHideSoftKeyboard();
+
+    /**
+     * update action bar
+     */
+    void onUpdateActionBar();
+
+    /**
+     * exit search mode
+     */
+    void exitSearch();
+
+
+}
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index d459d47..550bea5 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.list;
 
+import android.accounts.Account;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.CursorLoader;
@@ -28,6 +29,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -106,6 +108,8 @@
             Phone.PHOTO_ID,                     // 6
             Phone.DISPLAY_NAME_PRIMARY,         // 7
             Phone.PHOTO_THUMBNAIL_URI,          // 8
+            RawContacts.ACCOUNT_TYPE,           // 9
+            RawContacts.ACCOUNT_NAME,           // 10
         };
 
         public static final String[] PROJECTION_PRIMARY;
@@ -128,6 +132,8 @@
             Phone.PHOTO_ID,                     // 6
             Phone.DISPLAY_NAME_ALTERNATIVE,     // 7
             Phone.PHOTO_THUMBNAIL_URI,          // 8
+            RawContacts.ACCOUNT_TYPE,           // 9
+            RawContacts.ACCOUNT_NAME,           // 10
         };
 
         public static final String[] PROJECTION_ALTERNATIVE;
@@ -149,7 +155,9 @@
         public static final int PHOTO_ID                = 6;
         public static final int DISPLAY_NAME            = 7;
         public static final int PHOTO_URI               = 8;
-        public static final int CARRIER_PRESENCE        = 9;
+        public static final int ACCOUNT_TYPE            = 9;
+        public static final int ACCOUNT_NAME            = 10;
+        public static final int CARRIER_PRESENCE        = 11;
     }
 
     private static final String IGNORE_NUMBER_TOO_LONG_CLAUSE =
@@ -447,7 +455,8 @@
             if (isQuickContactEnabled()) {
                 bindQuickContact(view, partition, cursor, PhoneQuery.PHOTO_ID,
                         PhoneQuery.PHOTO_URI, PhoneQuery.CONTACT_ID,
-                        PhoneQuery.LOOKUP_KEY, PhoneQuery.DISPLAY_NAME);
+                        PhoneQuery.LOOKUP_KEY, PhoneQuery.DISPLAY_NAME,
+                        PhoneQuery.ACCOUNT_TYPE, PhoneQuery.ACCOUNT_NAME);
             } else {
                 if (getDisplayPhotos()) {
                     bindPhoto(view, partition, cursor);
@@ -540,9 +549,15 @@
         if (!cursor.isNull(PhoneQuery.PHOTO_ID)) {
             photoId = cursor.getLong(PhoneQuery.PHOTO_ID);
         }
-
+        Account account = null;
+        if (!cursor.isNull(PhoneQuery.ACCOUNT_TYPE)
+                && !cursor.isNull(PhoneQuery.ACCOUNT_NAME)) {
+            final String accountType = cursor.getString(PhoneQuery.ACCOUNT_TYPE);
+            final String accountName = cursor.getString(PhoneQuery.ACCOUNT_NAME);
+            account = new Account(accountName, accountType);
+        }
         if (photoId != 0) {
-            getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false,
+            getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, account, false,
                     getCircularPhotos(), null);
         } else {
             final String photoUriString = cursor.getString(PhoneQuery.PHOTO_URI);
@@ -554,7 +569,7 @@
                 final String lookupKey = cursor.getString(PhoneQuery.LOOKUP_KEY);
                 request = new DefaultImageRequest(displayName, lookupKey, getCircularPhotos());
             }
-            getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, false,
+            getPhotoLoader().loadDirectoryPhoto(view.getPhotoView(), photoUri, account, false,
                     getCircularPhotos(), request);
         }
     }
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index f67f074..8593e24 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -99,6 +99,13 @@
             public boolean apply(@Nullable AccountInfo input) {
                 return input != null && input.getType().isGroupMembershipEditable();
             }
+        },
+        CONTACTS_WRITABLE_WITHOUT_SIM {
+            @Override
+            public boolean apply(@Nullable AccountInfo input) {
+                return input != null && !input.getType().isSimAccount()
+                        && input.getType().areContactsWritable();
+            }
         };
     }
 
@@ -190,6 +197,12 @@
                 Futures.getUnchecked(filterAccountsAsync(AccountFilter.CONTACTS_WRITABLE)));
     }
 
+    public List<AccountWithDataSet> blockForWritableAccountsWithoutSim() {
+        return AccountInfo.extractAccounts(
+                Futures.getUnchecked(filterAccountsAsync(AccountFilter
+                        .CONTACTS_WRITABLE_WITHOUT_SIM)));
+    }
+
     /**
      * Loads accounts in background and returns future that will complete with list of all accounts
      */
diff --git a/src/com/android/contacts/model/Contact.java b/src/com/android/contacts/model/Contact.java
index 8266123..58ac76a 100644
--- a/src/com/android/contacts/model/Contact.java
+++ b/src/com/android/contacts/model/Contact.java
@@ -81,6 +81,9 @@
     private String mDirectoryAccountName;
     private int mDirectoryExportSupport;
 
+    private String mAccountType;
+    private String mAccountName;
+
     private ImmutableList<GroupMetaData> mGroups;
 
     private byte[] mPhotoBinaryData;
@@ -417,6 +420,14 @@
         return mThumbnailPhotoBinaryData;
     }
 
+    public String getAccountType() {
+        return mAccountType;
+    }
+
+    public String getAccountName() {
+        return mAccountName;
+    }
+
     public ArrayList<ContentValues> getContentValues() {
         if (mRawContacts.size() != 1) {
             throw new IllegalStateException(
@@ -493,4 +504,9 @@
     /* package */ void setGroupMetaData(ImmutableList<GroupMetaData> groups) {
         mGroups = groups;
     }
+
+    public void setAccountInfo(String accountType, String accountName) {
+        mAccountType = accountType;
+        mAccountName = accountName;
+    }
 }
diff --git a/src/com/android/contacts/model/ContactLoader.java b/src/com/android/contacts/model/ContactLoader.java
index bf1ac74..ea439e7 100644
--- a/src/com/android/contacts/model/ContactLoader.java
+++ b/src/com/android/contacts/model/ContactLoader.java
@@ -479,6 +479,13 @@
                     currentRawContactId = rawContactId;
                     rawContact = new RawContact(loadRawContactValues(cursor));
                     rawContactsBuilder.add(rawContact);
+                    if (currentRawContactId == contact.getNameRawContactId()) {
+                        final String accountType = cursor
+                                .getString(ContactQuery.ACCOUNT_TYPE);
+                        final String accountName = cursor
+                                .getString(ContactQuery.ACCOUNT_NAME);
+                        contact.setAccountInfo(accountType, accountName);
+                    }
                 }
                 if (!cursor.isNull(ContactQuery.DATA_ID)) {
                     ContentValues data = loadDataValues(cursor);
diff --git a/src/com/android/contacts/model/RawContactDelta.java b/src/com/android/contacts/model/RawContactDelta.java
index 6ee31ba..8bb36d4 100644
--- a/src/com/android/contacts/model/RawContactDelta.java
+++ b/src/com/android/contacts/model/RawContactDelta.java
@@ -27,12 +27,18 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Profile;
 import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.contacts.SimContactsConstants;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.compat.CompatUtils;
 import com.android.contacts.model.account.AccountType;
 import com.android.contacts.model.account.AccountWithDataSet;
-
+import com.android.contacts.model.account.SimAccountType;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -87,11 +93,14 @@
      * starting point; the "before" snapshot.
      */
     public static RawContactDelta fromBefore(RawContact before) {
+        String account = before.getAccountTypeString();
+        boolean isSimContact = (account != null && account
+                .equals(SimAccountType.ACCOUNT_TYPE)) ? true : false;
         final RawContactDelta rawContactDelta = new RawContactDelta();
         rawContactDelta.mValues = ValuesDelta.fromBefore(before.getValues());
         rawContactDelta.mValues.setIdColumn(RawContacts._ID);
         for (final ContentValues values : before.getContentValues()) {
-            rawContactDelta.addEntry(ValuesDelta.fromBefore(values));
+            rawContactDelta.addEntry(ValuesDelta.fromBefore(values, isSimContact));
         }
         return rawContactDelta;
     }
@@ -446,6 +455,119 @@
         return builder;
     }
 
+    public ContentValues buildSimDiff() {
+        ContentValues values = new ContentValues();
+        ArrayList<ValuesDelta> names = getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+        ArrayList<ValuesDelta> phones = getMimeEntries(Phone.CONTENT_ITEM_TYPE);
+        ArrayList<ValuesDelta> emails = getMimeEntries(Email.CONTENT_ITEM_TYPE);
+        ValuesDelta nameValuesDelta = null;
+        ValuesDelta emailValuesDelta = null;
+
+        if (names != null && names.size() > 0)
+            nameValuesDelta = names.get(0);
+        if (emails != null && emails.size() > 0) {
+            emailValuesDelta = emails.get(0);
+        }
+
+        String name = null;
+        String number = null;
+        String newName = null;
+        String newNumber = null;
+        StringBuilder email = new StringBuilder();
+        StringBuilder anr = new StringBuilder();
+        StringBuilder newEmail = new StringBuilder();
+        StringBuilder newAnr = new StringBuilder();
+
+        if (nameValuesDelta != null) {
+            if (isContactInsert()) {
+                name = nameValuesDelta.getAsString(StructuredName.GIVEN_NAME);
+            } else {
+                if (nameValuesDelta.getBefore() != null) {
+                    name = nameValuesDelta.getBefore()
+                        .getAsString(StructuredName.GIVEN_NAME);
+                }
+                if (nameValuesDelta.getAfter() != null) {
+                    newName = nameValuesDelta.getAfter()
+                        .getAsString(StructuredName.GIVEN_NAME);
+                }
+            }
+        }
+        if (isContactInsert() && phones != null) {
+            for (ValuesDelta valuesDelta : phones) {
+                if (valuesDelta.getAfter() != null
+                        && valuesDelta.getAfter().size() != 0) {
+                    if (Phone.TYPE_MOBILE == valuesDelta.getAfter().getAsLong(Phone.TYPE)) {
+                        number = valuesDelta.getAfter().getAsString(Phone.NUMBER);
+                    } else {
+                        anr.append(valuesDelta.getAfter().getAsString(Phone.NUMBER));
+                        anr.append(SimContactsConstants.ANR_SEP);
+                    }
+                }
+            }
+        } else if (phones != null) {
+            for (ValuesDelta valuesDelta : phones) {
+                if (valuesDelta.getBefore() != null
+                        && valuesDelta.getBefore().size() != 0) {
+                    if (Phone.TYPE_MOBILE == valuesDelta.getBefore().getAsLong(Phone.TYPE) ) {
+                        number = valuesDelta.getBefore().getAsString(Phone.NUMBER);
+                    } else {
+                        anr.append(valuesDelta.getBefore().getAsString(Phone.NUMBER));
+                        anr.append(SimContactsConstants.ANR_SEP);
+                    }
+                }
+                if (valuesDelta.getAfter() != null
+                        && valuesDelta.getAfter().size() != 0) {
+                    if (Phone.TYPE_MOBILE == valuesDelta.getAsLong(Phone.TYPE)) {
+                        newNumber = valuesDelta.getAfter().getAsString(Phone.NUMBER);
+                    } else {
+                        newAnr.append(valuesDelta.getAfter().getAsString(Phone.NUMBER));
+                        newAnr.append(SimContactsConstants.ANR_SEP);
+                    }
+                }
+            }
+        }
+
+        if (isContactInsert() && emails != null) {
+            for (ValuesDelta valuesDelta : emails) {
+                if (valuesDelta.getAfter() != null
+                        && valuesDelta.getAfter().size() != 0) {
+                    email.append(valuesDelta.getAfter().getAsString(Email.DATA));
+                    email.append(SimContactsConstants.EMAIL_SEP);
+                }
+            }
+        } else if (emails != null) {
+            for (ValuesDelta valuesDelta : emails) {
+                if (valuesDelta.getBefore() != null
+                        && valuesDelta.getBefore().size() != 0) {
+                    email.append(valuesDelta.getBefore().getAsString(Email.DATA));
+                    email.append(SimContactsConstants.EMAIL_SEP);
+                }
+                if (valuesDelta.getAfter() != null
+                        && valuesDelta.getAfter().size() != 0) {
+                        newEmail.append(valuesDelta.getAfter().getAsString(Email.DATA));
+                        newEmail.append(SimContactsConstants.EMAIL_SEP);
+                }
+            }
+        }
+
+        if (isContactInsert()) {
+            values.put(SimContactsConstants.STR_TAG, name);
+            values.put(SimContactsConstants.STR_NUMBER, number);
+            values.put(SimContactsConstants.STR_EMAILS, email.toString());
+            values.put(SimContactsConstants.STR_ANRS, anr.toString());
+        } else {
+            values.put(SimContactsConstants.STR_TAG, name);
+            values.put(SimContactsConstants.STR_NUMBER, number);
+            values.put(SimContactsConstants.STR_EMAILS, email.toString());
+            values.put(SimContactsConstants.STR_ANRS, anr.toString());
+            values.put(SimContactsConstants.STR_NEW_TAG, newName);
+            values.put(SimContactsConstants.STR_NEW_NUMBER, newNumber);
+            values.put(SimContactsConstants.STR_NEW_EMAILS, newEmail.toString());
+            values.put(SimContactsConstants.STR_NEW_ANRS, newAnr.toString());
+        }
+        return values;
+    }
+
     /**
      * Build a list of {@link ContentProviderOperation} that will transform the
      * current "before" {@link Entity} state into the modified state which this
diff --git a/src/com/android/contacts/model/RawContactModifier.java b/src/com/android/contacts/model/RawContactModifier.java
old mode 100644
new mode 100755
index 789bd10..171a810
--- a/src/com/android/contacts/model/RawContactModifier.java
+++ b/src/com/android/contacts/model/RawContactModifier.java
@@ -1038,10 +1038,30 @@
             return;
         }
 
+        boolean supportPrefix = false;
+        boolean supportFamilyName = false;
+        boolean supportMiddleName = false;
+        boolean supportGivenName = false;
+        boolean supportSuffix = false;
         boolean supportPhoneticFamilyName = false;
         boolean supportPhoneticMiddleName = false;
         boolean supportPhoneticGivenName = false;
         for (EditField editField : newDataKind.fieldList) {
+            if (StructuredName.PREFIX.equals(editField.column)) {
+                supportPrefix = true;
+            }
+            if (StructuredName.FAMILY_NAME.equals(editField.column)) {
+                supportFamilyName = true;
+            }
+            if (StructuredName.MIDDLE_NAME.equals(editField.column)) {
+                supportMiddleName = true;
+            }
+            if (StructuredName.GIVEN_NAME.equals(editField.column)) {
+                supportGivenName = true;
+            }
+            if (StructuredName.SUFFIX.equals(editField.column)) {
+                supportSuffix = true;
+            }
             if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) {
                 supportPhoneticFamilyName = true;
             }
@@ -1053,6 +1073,21 @@
             }
         }
 
+        if (!supportPrefix) {
+            values.remove(StructuredName.PREFIX);
+        }
+        if (!supportFamilyName) {
+            values.remove(StructuredName.FAMILY_NAME);
+        }
+        if (!supportMiddleName) {
+            values.remove(StructuredName.MIDDLE_NAME);
+        }
+        if (!supportGivenName) {
+            values.remove(StructuredName.GIVEN_NAME);
+        }
+        if (!supportSuffix) {
+            values.remove(StructuredName.SUFFIX);
+        }
         if (!supportPhoneticFamilyName) {
             values.remove(StructuredName.PHONETIC_FAMILY_NAME);
         }
diff --git a/src/com/android/contacts/model/SimContact.java b/src/com/android/contacts/model/SimContact.java
index 7babe27..d6cc13e 100644
--- a/src/com/android/contacts/model/SimContact.java
+++ b/src/com/android/contacts/model/SimContact.java
@@ -47,20 +47,26 @@
     private final String mName;
     private final String mPhone;
     private final String[] mEmails;
+    private final String[] mAnrs;
 
     public SimContact(long id, String name, String phone) {
         this(id, name, phone, null);
     }
 
     public SimContact(long id, String name, String phone, String[] emails) {
+        this(id, name, phone, emails, null);
+    }
+
+    public SimContact(long id, String name, String phone, String[] emails, String[] anrs) {
         mId = id;
         mName = name;
         mPhone = phone == null ? "" : phone.trim();
         mEmails = emails;
+        mAnrs = anrs;
     }
 
     public SimContact(SimContact other) {
-        this(other.mId, other.mName, other.mPhone, other.mEmails);
+        this(other.mId, other.mName, other.mPhone, other.mEmails, other.mAnrs);
     }
 
     public long getId() {
@@ -79,10 +85,14 @@
         return mEmails;
     }
 
+    public String[] getAnrs() {
+        return mAnrs;
+    }
+
     public void appendCreateContactOperations(List<ContentProviderOperation> ops,
             AccountWithDataSet targetAccount) {
         // There is nothing to save so skip it.
-        if (!hasName() && !hasPhone() && !hasEmails()) return;
+        if (!hasName() && !hasPhone() && !hasEmails() && !hasAnrs()) return;
 
         final int rawContactOpIndex = ops.size();
         ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
@@ -105,6 +115,12 @@
                         Email.ADDRESS, email));
             }
         }
+        if (mAnrs != null) {
+            for (String anr : mAnrs) {
+                ops.add(createInsertOp(rawContactOpIndex, Phone.CONTENT_ITEM_TYPE,
+                        Phone.NUMBER, anr));
+            }
+        }
     }
 
     private ContentProviderOperation createInsertOp(int rawContactOpIndex, String mimeType,
@@ -134,6 +150,10 @@
         return mEmails != null && mEmails.length > 0;
     }
 
+    public boolean hasAnrs() {
+        return mAnrs != null && mAnrs.length > 0;
+    }
+
     /**
      * Generate a "fake" lookup key. This is needed because
      * {@link ContactPhotoManager} will only generate a letter avatar
@@ -167,7 +187,8 @@
         final SimContact that = (SimContact) o;
 
         return mId == that.mId && Objects.equals(mName, that.mName) &&
-                Objects.equals(mPhone, that.mPhone) && Arrays.equals(mEmails, that.mEmails);
+                Objects.equals(mPhone, that.mPhone) && Arrays.equals(mEmails, that.mEmails)
+                && Arrays.equals(mAnrs, that.mAnrs);
     }
 
     @Override
@@ -176,6 +197,7 @@
         result = 31 * result + (mName != null ? mName.hashCode() : 0);
         result = 31 * result + (mPhone != null ? mPhone.hashCode() : 0);
         result = 31 * result + Arrays.hashCode(mEmails);
+        result = 31 * result + Arrays.hashCode(mAnrs);
         return result;
     }
 
@@ -190,6 +212,7 @@
         dest.writeString(mName);
         dest.writeString(mPhone);
         dest.writeStringArray(mEmails);
+        dest.writeStringArray(mAnrs);
     }
 
     public static final Creator<SimContact> CREATOR = new Creator<SimContact>() {
@@ -199,7 +222,8 @@
             final String name = source.readString();
             final String phone = source.readString();
             final String[] emails = source.createStringArray();
-            return new SimContact(id, name, phone, emails);
+            final String[] anrs = source.createStringArray();
+            return new SimContact(id, name, phone, emails, anrs);
         }
 
         @Override
diff --git a/src/com/android/contacts/model/ValuesDelta.java b/src/com/android/contacts/model/ValuesDelta.java
index 120c205..bdeaf0d 100644
--- a/src/com/android/contacts/model/ValuesDelta.java
+++ b/src/com/android/contacts/model/ValuesDelta.java
@@ -59,9 +59,30 @@
      * "before" state, usually from an {@link Entity}.
      */
     public static ValuesDelta fromBefore(ContentValues before) {
+        return fromBefore(before, false);
+    }
+
+    public static ValuesDelta fromBefore(ContentValues before, boolean simContact) {
         final ValuesDelta entry = new ValuesDelta();
         entry.mBefore = before;
         entry.mAfter = new ContentValues();
+        if (simContact) {
+            // init data1 to mAfter map for sim contacts
+            if (before.containsKey(ContactsContract.Data.DATA1)) {
+                String contactInfo = before
+                        .getAsString(ContactsContract.Data.DATA1);
+                if (null != contactInfo && !"".equals(contactInfo)) {
+                    entry.mAfter.put(ContactsContract.Data.DATA1, contactInfo);
+                }
+            }
+            if (before.containsKey(ContactsContract.Data.DATA2)) {
+                String contactInfo = before
+                        .getAsString(ContactsContract.Data.DATA2);
+                if (null != contactInfo && !"".equals(contactInfo)) {
+                    entry.mAfter.put(ContactsContract.Data.DATA2, contactInfo);
+                }
+            }
+        }
         return entry;
     }
 
diff --git a/src/com/android/contacts/model/account/AccountType.java b/src/com/android/contacts/model/account/AccountType.java
index c3e0eb5..9d9bf1f 100644
--- a/src/com/android/contacts/model/account/AccountType.java
+++ b/src/com/android/contacts/model/account/AccountType.java
@@ -289,6 +289,10 @@
         }
     }
 
+    public boolean isSimAccount() {
+        return false;
+    }
+
     /**
      * Whether or not groups created under this account type have editable membership lists.
      */
diff --git a/src/com/android/contacts/model/account/AccountTypeProvider.java b/src/com/android/contacts/model/account/AccountTypeProvider.java
index 4f83ec6..2657f29 100644
--- a/src/com/android/contacts/model/account/AccountTypeProvider.java
+++ b/src/com/android/contacts/model/account/AccountTypeProvider.java
@@ -178,6 +178,8 @@
                         + ", packageName=" + auth.packageName);
             }
             accountType = mLocalAccountTypeFactory.getAccountType(type);
+        } else if (SimAccountType.ACCOUNT_TYPE.equals(type)) {
+            accountType = new SimAccountType(mContext, auth.packageName);
         } else {
             if (Log.isLoggable(TAG, Log.DEBUG)) {
                 Log.d(TAG, "Registering external account type=" + type
diff --git a/src/com/android/contacts/model/account/SimAccountType.java b/src/com/android/contacts/model/account/SimAccountType.java
index 360e944..9910005 100644
--- a/src/com/android/contacts/model/account/SimAccountType.java
+++ b/src/com/android/contacts/model/account/SimAccountType.java
@@ -17,6 +17,8 @@
 
 import android.accounts.AuthenticatorDescription;
 import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Nickname;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 
@@ -32,19 +34,22 @@
  */
 public class SimAccountType extends BaseAccountType {
 
+    public static final String ACCOUNT_TYPE = "com.android.sim";
+
     public SimAccountType(Context context) {
-        this.titleRes = R.string.account_sim;
-        this.iconRes = R.drawable.quantum_ic_sim_card_vd_theme_24;
+        this(context, null);
+    }
+
+    public SimAccountType(Context context, String resPackageName) {
+        this.accountType = ACCOUNT_TYPE;
+        this.resourcePackageName = resPackageName;
+        this.syncAdapterPackageName = resPackageName;
 
         try {
             addDataKindStructuredName(context);
             addDataKindName(context);
-            final DataKind phoneKind = addDataKindPhone(context);
-            phoneKind.typeOverallMax = 1;
-            // SIM card contacts don't necessarily support separate types (based on data exposed
-            // in Samsung and LG Contacts Apps.
-            phoneKind.typeList = Collections.emptyList();
-
+            addDataKindPhone(context);
+            addDataKindEmail(context);
             mIsInitialized = true;
         } catch (DefinitionException e) {
             // Just fail fast. Because we're explicitly adding the fields in this class this
@@ -64,8 +69,14 @@
     }
 
     @Override
+    public boolean isSimAccount() {
+        return true;
+    }
+
+    @Override
     public void initializeFieldsFromAuthenticator(AuthenticatorDescription authenticator) {
         // Do nothing. We want to use our local icon and title
+        super.initializeFieldsFromAuthenticator(authenticator);
     }
 
     @Override
@@ -75,14 +86,9 @@
         kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
         kind.actionBody = new SimpleInflater(Nickname.NAME);
         kind.typeOverallMax = 1;
-
-
         kind.fieldList = Lists.newArrayList();
-        kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
+        kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.nameLabelsGroup,
                 FLAGS_PERSON_NAME));
-        kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
-                FLAGS_PERSON_NAME));
-
         return kind;
     }
 
@@ -98,18 +104,33 @@
                 context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
 
         kind.fieldList = Lists.newArrayList();
-        if (!displayOrderPrimary) {
-            kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
-                    FLAGS_PERSON_NAME));
-            kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
-                    FLAGS_PERSON_NAME));
-        } else {
-            kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
-                    FLAGS_PERSON_NAME));
-            kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
-                    FLAGS_PERSON_NAME));
-        }
+        kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME,
+                R.string.nameLabelsGroup, FLAGS_PERSON_NAME));
+        return kind;
+    }
 
+    @Override
+    protected DataKind addDataKindPhone(Context context) throws DefinitionException {
+        final DataKind kind = super.addDataKindPhone(context);
+        kind.typeOverallMax = 2;
+        kind.typeColumn = Phone.TYPE;
+        kind.typeList = Lists.newArrayList();
+        kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+        kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));// This is used to save ANR records
+        kind.fieldList = Lists.newArrayList();
+        kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+        return kind;
+    }
+
+    @Override
+    protected DataKind addDataKindEmail(Context context) throws DefinitionException {
+        final DataKind kind = super.addDataKindEmail(context);
+
+        kind.typeOverallMax = 1;
+        kind.typeList =  Collections.emptyList();
+        kind.fieldList = Lists.newArrayList();
+        kind.fieldList.add(new EditField(Email.ADDRESS, R.string.emailLabelsGroup, FLAGS_EMAIL));
         return kind;
     }
 
@@ -118,7 +139,7 @@
         // Use the "SIM" type label for the name as well because on OEM phones the "name" is
         // not always user-friendly
         return new AccountInfo(
-                new AccountDisplayInfo(account, getDisplayLabel(context), getDisplayLabel(context),
+                new AccountDisplayInfo(account, account.name, getDisplayLabel(context),
                         getDisplayIcon(context), true), this);
     }
 }
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index fcbfba9..ebc5f39 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -37,6 +37,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
@@ -52,6 +53,8 @@
 import android.os.Bundle;
 import android.os.Trace;
 import android.provider.CalendarContract;
+import android.os.Handler;
+import android.os.Message;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Event;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
@@ -79,6 +82,8 @@
 import android.text.SpannableString;
 import android.text.TextDirectionHeuristics;
 import android.text.TextUtils;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
@@ -113,6 +118,9 @@
 import com.android.contacts.compat.CompatUtils;
 import com.android.contacts.compat.EventCompat;
 import com.android.contacts.compat.MultiWindowCompat;
+import com.android.contacts.model.RawContactDelta;
+import com.android.contacts.ContactUtils;
+import com.android.contacts.SimContactsConstants;
 import com.android.contacts.detail.ContactDisplayUtils;
 import com.android.contacts.dialog.CallSubjectDialog;
 import com.android.contacts.editor.ContactEditorFragment;
@@ -132,6 +140,7 @@
 import com.android.contacts.model.ContactLoader;
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.account.AccountType;
+import com.android.contacts.model.account.SimAccountType;
 import com.android.contacts.model.dataitem.CustomDataItem;
 import com.android.contacts.model.dataitem.DataItem;
 import com.android.contacts.model.dataitem.DataKind;
@@ -316,6 +325,7 @@
 
     private final ImageViewDrawableSetter mPhotoSetter = new ImageViewDrawableSetter();
 
+    private TelephonyManager tm;
     /**
      * {@link #LEADING_MIMETYPES} is used to sort MIME-types.
      *
@@ -351,6 +361,8 @@
             QuickContactActivity.class.getCanonicalName() + ".KEY_LOADER_EXTRA_SIP_NUMBERS";
 
     private static final String FRAGMENT_TAG_SELECT_ACCOUNT = "select_account_fragment";
+    private boolean simOneLoadComplete = false;
+    private boolean simTwoLoadComplete = false;
 
     final OnClickListener mEntryClickHandler = new OnClickListener() {
         @Override
@@ -657,6 +669,7 @@
         if (CompatUtils.isLollipopCompatible()) {
             getWindow().setStatusBarColor(Color.TRANSPARENT);
         }
+        tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
 
         processIntent(getIntent());
 
@@ -2238,8 +2251,13 @@
             if (displayName == null) {
                 displayName = getString(R.string.missing_name);
             }
+            Account account = null;
+            if (mContactData.getAccountName() != null && mContactData.getAccountType() != null) {
+                account = new Account(mContactData.getAccountName(),
+                        mContactData.getAccountType());
+            }
             final ShortcutInfo shortcutInfo = shortcuts.getQuickContactShortcutInfo(
-                    mContactData.getId(), mContactData.getLookupKey(), displayName);
+                    mContactData.getId(), mContactData.getLookupKey(), displayName, account);
             if (shortcutInfo != null) {
                 shortcutManager.requestPinShortcut(shortcutInfo, null);
             }
@@ -2311,7 +2329,17 @@
             ContactDisplayUtils.configureStarredMenuItem(starredMenuItem,
                     mContactData.isDirectoryEntry(), mContactData.isUserProfile(),
                     mContactData.getStarred());
-
+            if (!simOneLoadComplete) {
+                simOneLoadComplete = (ContactUtils.getAdnRecordsCapacity(this,
+                        SimContactsConstants.SLOT1)[0] > 0) ? true : false;
+            }
+            if (!simTwoLoadComplete) {
+                simTwoLoadComplete = (ContactUtils.getAdnRecordsCapacity(this,
+                        SimContactsConstants.SLOT2)[0] > 0)? true : false;
+            }
+            final String accountType = mContactData.getAccountType();
+            boolean simContact = accountType != null && accountType
+                    .equals(SimAccountType.ACCOUNT_TYPE);
             // Configure edit MenuItem
             final MenuItem editMenuItem = menu.findItem(R.id.menu_edit);
             editMenuItem.setVisible(true);
@@ -2330,7 +2358,8 @@
             final MenuItem joinMenuItem = menu.findItem(R.id.menu_join);
             joinMenuItem.setVisible(!InvisibleContactUtil.isInvisibleAndAddable(mContactData, this)
                     && isContactEditable() && !mContactData.isUserProfile()
-                    && !mContactData.isMultipleRawContacts());
+                    && !mContactData.isMultipleRawContacts()
+                    && !simContact);
 
             // Viewing linked contacts can only happen if there are multiple raw contacts and
             // the link menu isn't available.
@@ -2363,6 +2392,70 @@
             final MenuItem helpMenu = menu.findItem(R.id.menu_help);
             helpMenu.setVisible(HelpUtils.isHelpAndFeedbackAvailable());
 
+            final RawContact rawContact = mContactData.getRawContacts().get(0);
+            String accoutName = rawContact.getAccountName();
+            String accoutType = rawContact.getAccountTypeString();
+
+            final MenuItem copyToPhoneMenu = menu.findItem(R.id.menu_copy_to_phone);
+            if (copyToPhoneMenu != null) {
+                copyToPhoneMenu.setVisible(false);
+            }
+
+            final MenuItem copyToSim1Menu = menu.findItem(R.id.menu_copy_to_sim1);
+            if (copyToSim1Menu != null) {
+                copyToSim1Menu.setVisible(false);
+            }
+
+            final MenuItem copyToSim2Menu = menu.findItem(R.id.menu_copy_to_sim2);
+            if (copyToSim2Menu != null) {
+                copyToSim2Menu.setVisible(false);
+            }
+
+            if (!TextUtils.isEmpty(accoutType)) {
+                if (SimContactsConstants.ACCOUNT_TYPE_SIM.equals(accoutType)) {
+                    copyToPhoneMenu.setVisible(true);
+                    copyToPhoneMenu.setTitle(getString(R.string.menu_copyTo)
+                            + getString(R.string.phoneLabelsGroup));
+                    if (tm.getPhoneCount() > 1) {
+                        if (SimContactsConstants.SIM_NAME_1.equals(accoutName)
+                                && simTwoLoadComplete) {
+                            copyToSim2Menu.setTitle(getString(R.string.menu_copyTo)
+                                    + ContactUtils.getAcount( QuickContactActivity.this,
+                                            SimContactsConstants.SLOT2).name);
+                            copyToSim2Menu.setVisible(true);
+                        }
+                        if (SimContactsConstants.SIM_NAME_2.equals(accoutName)
+                                && simOneLoadComplete) {
+                            copyToSim1Menu.setTitle(getString(R.string.menu_copyTo)
+                                    + ContactUtils.getAcount(QuickContactActivity.this,
+                                            SimContactsConstants.SLOT1).name);
+                            copyToSim1Menu.setVisible(true);
+                        }
+                    }
+                }
+            } else {
+                copyToPhoneMenu.setVisible(false);
+                if (tm.getPhoneCount() > 1) {
+                    if (simOneLoadComplete) {
+                        copyToSim1Menu.setTitle(getString(R.string.menu_copyTo)
+                                + ContactUtils.getAcount(
+                                        this, SimContactsConstants.SLOT1).name);
+                        copyToSim1Menu.setVisible(true);
+                    }
+                    if (simTwoLoadComplete) {
+                        copyToSim2Menu.setTitle(getString(R.string.menu_copyTo)
+                                + ContactUtils.getAcount(
+                                        this, SimContactsConstants.SLOT2).name);
+                        copyToSim2Menu.setVisible(true);
+                    }
+                } else {
+                    if (simOneLoadComplete) {
+                        copyToSim1Menu.setTitle(getString(R.string.menu_copyTo)
+                                + SimContactsConstants.SIM_NAME);
+                        copyToSim1Menu.setVisible(true);
+                    }
+                }
+            }
             return true;
         }
         return false;
@@ -2479,6 +2572,18 @@
             Logger.logQuickContactEvent(mReferrer, mContactType, CardType.UNKNOWN_CARD,
                     ActionType.HELP, /* thirdPartyAction */ null);
             HelpUtils.launchHelpAndFeedbackForContactScreen(this);
+        } else if(id == R.id.menu_copy_to_phone) {
+            if (mContactData == null) return false;
+            copyToPhone();
+            return true;
+        } else if(id == R.id.menu_copy_to_sim1) {
+            if (mContactData == null) return false;
+            copyToCard(SimContactsConstants.SLOT1);
+            return true;
+        }  else if(id == R.id.menu_copy_to_sim2) {
+            if (mContactData == null) return false;
+            copyToCard(SimContactsConstants.SLOT2);
+            return true;
         } else {
             Logger.logQuickContactEvent(mReferrer, mContactType, CardType.UNKNOWN_CARD,
                     ActionType.UNKNOWN_ACTION, /* thirdPartyAction */ null);
@@ -2487,6 +2592,254 @@
         return true;
     }
 
+    //supply phone number and email which could stored in one ADN
+    class UsimEntity {
+        private ArrayList<String> mNumberList = new ArrayList<String>();
+        private ArrayList<String> mEmailList = new ArrayList<String>();
+
+        public ArrayList<String> getEmailList() {
+            return mEmailList;
+        }
+
+        public ArrayList<String> getNumberList() {
+            return mNumberList;
+        }
+
+        public void putEmailList(ArrayList<String> list) {
+            mEmailList = list;
+        }
+
+        public void putNumberList(ArrayList<String> list) {
+            mNumberList = list;
+        }
+    }
+
+    private void copyToPhone() {
+        String name = mContactData.getDisplayName();
+        if (TextUtils.isEmpty(name)) {
+            name = "";
+        }
+        String phoneNumber = "";
+        StringBuilder anrNumber = new StringBuilder();
+        StringBuilder email = new StringBuilder();
+
+        //get phonenumber,email,anr from SIM contacts,then insert them to phone
+        for (RawContact rawContact : mContactData.getRawContacts()) {
+            for (DataItem dataItem : rawContact.getDataItems()) {
+                if (dataItem.getMimeType() == null) {
+                    continue;
+                }
+                if (dataItem instanceof PhoneDataItem) {
+                    PhoneDataItem phoneNum = (PhoneDataItem) dataItem;
+                    final String number = phoneNum.getNumber();
+                    if (!TextUtils.isEmpty(number)) {
+                        if (Phone.TYPE_MOBILE == phoneNum.getContentValues().getAsInteger(
+                                Phone.TYPE)) {
+                            phoneNumber = number;
+                        } else {
+                            if(!TextUtils.isEmpty(anrNumber.toString())) {
+                                anrNumber.append(SimContactsConstants.ANR_SEP);
+                            }
+                            anrNumber.append(number);
+                        }
+                    }
+                } else if (dataItem instanceof EmailDataItem) {
+                    EmailDataItem emailData = (EmailDataItem) dataItem;
+                    final String address = emailData.getData();
+                    if (!TextUtils.isEmpty(address)) {
+                        if(!TextUtils.isEmpty(email.toString())) {
+                            email.append(SimContactsConstants.EMAIL_SEP);
+                        }
+                        email.append(address);
+                    }
+                }
+            }
+        }
+
+        String[] value = new String[] {
+                name, phoneNumber, email.toString(), anrNumber.toString()
+        };
+        boolean success = ContactUtils
+                .insertToPhone(value, QuickContactActivity.this,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        Toast.makeText(this, success ? R.string.copy_done : R.string.copy_failure,
+                Toast.LENGTH_SHORT).show();
+    }
+
+    private Handler mHandler = null;
+
+    private void copyToCard(final int sub) {
+        final int MSG_COPY_DONE = 0;
+        final int MSG_COPY_FAILURE = 1;
+        final int MSG_CARD_NO_SPACE = 2;
+        final int MSG_NO_EMPTY_EMAIL = 3;
+        if (mHandler == null) {
+            mHandler = new Handler() {
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case MSG_COPY_DONE:
+                            Toast.makeText(QuickContactActivity.this, R.string.copy_done,
+                                    Toast.LENGTH_SHORT).show();
+                            break;
+                        case MSG_COPY_FAILURE:
+                            Toast.makeText(QuickContactActivity.this, R.string.copy_failure,
+                                    Toast.LENGTH_SHORT).show();
+                            break;
+                        case MSG_CARD_NO_SPACE:
+                            Toast.makeText(QuickContactActivity.this, R.string.card_no_space,
+                                    Toast.LENGTH_SHORT).show();
+                            break;
+                        case MSG_NO_EMPTY_EMAIL:
+                            Toast.makeText(QuickContactActivity.this,
+                                    R.string.no_empty_email_in_usim,
+                                    Toast.LENGTH_SHORT).show();
+                            break;
+                    }
+                }
+            };
+        }
+
+        new Thread(new Runnable() {
+            public void run() {
+                synchronized (this) {
+                    int adnCountInSimContact = 1;
+                    int anrCountInSimContact = ContactUtils.getOneSimAnrCount
+                                (QuickContactActivity.this, sub);
+                    int emailCountInSimContact = ContactUtils.getOneSimEmailCount
+                                (QuickContactActivity.this, sub);
+                    int totalEmptyAdn = ContactUtils.getSimFreeCount(
+                            QuickContactActivity.this, sub);
+                    int totalEmptyAnr = ContactUtils.getSpareAnrCount(
+                            QuickContactActivity.this, sub);
+                    int totalEmptyEmail = ContactUtils.getSpareEmailCount(
+                            QuickContactActivity.this, sub);
+
+                    Message msg = Message.obtain();
+                    if (totalEmptyAdn <= 0) {
+                        msg.what = MSG_CARD_NO_SPACE;
+                        mHandler.sendMessage(msg);
+                        return;
+                    }
+
+                    //to indiacate how many number in one ADN can saved to SIM card,
+                    //1 means can only save one number,2,3 ... means can save anr
+                    int numEntitySize = adnCountInSimContact + anrCountInSimContact;
+
+                    //empty number is equals to the sum of adn and anr
+                    int emptyNumTotal = totalEmptyAdn + totalEmptyAnr;
+                    int nameSource = mContactData.getDisplayNameSource();
+                    String strName = "";
+                    if (nameSource == DisplayNameSources.STRUCTURED_NAME
+                            || nameSource == DisplayNameSources.NICKNAME
+                            || nameSource == DisplayNameSources.STRUCTURED_PHONETIC_NAME)
+                    strName = mContactData.getDisplayName();
+
+                    ArrayList<String> arrayNumber = new ArrayList<String>();
+                    ArrayList<String> arrayEmail = new ArrayList<String>();
+
+                    for (RawContact rawContact : mContactData.getRawContacts()) {
+                        for (DataItem dataItem : rawContact.getDataItems()) {
+                            if (dataItem.getMimeType() == null) {
+                                continue;
+                            }
+                            if (dataItem instanceof PhoneDataItem) {
+                                // Get phone string
+                                PhoneDataItem phoneNum = (PhoneDataItem) dataItem;
+                                final String number = phoneNum.getNumber();
+                                if (!TextUtils.isEmpty(number) && emptyNumTotal-- > 0) {
+                                    arrayNumber.add(number);
+                                }
+                            } else if (dataItem instanceof EmailDataItem) {
+                                // Get email string
+                                EmailDataItem emailData = (EmailDataItem) dataItem;
+                                final String address = emailData.getData();
+                                if (!TextUtils.isEmpty(address) && totalEmptyEmail-- > 0) {
+                                    arrayEmail.add(address);
+                                }
+                            }
+                        }
+                    }
+
+                    //calculate how many ADN needed according to the number and email,
+                    int groupNumCount = (arrayNumber.size() % numEntitySize) != 0 ? (arrayNumber
+                            .size() / numEntitySize + 1) : (arrayNumber.size() / numEntitySize);
+                    int groupEmailCount = emailCountInSimContact == 0 ? 0
+                            : ((arrayEmail.size() % emailCountInSimContact) != 0 ? (arrayEmail
+                                    .size() / emailCountInSimContact + 1)
+                                    : (arrayEmail.size() / emailCountInSimContact));
+
+                    int groupCount = Math.max(groupEmailCount, groupNumCount);
+
+                    ArrayList<UsimEntity> results = new ArrayList<UsimEntity>();
+                    for (int i = 0; i < groupCount; i++) {
+                        results.add(new UsimEntity());
+                    }
+
+                    UsimEntity value;
+                    //get the phone number for each ADN from arrayNumber,put them in UsimEntity
+                    for (int i = 0; i < groupNumCount; i++) {
+                        value = results.get(i);
+                        ArrayList<String> numberItem = new ArrayList<String>();
+                        for (int j = 0; j < numEntitySize; j++) {
+                            if ((i * numEntitySize + j) < arrayNumber.size()) {
+                                numberItem.add(arrayNumber.get(i * numEntitySize + j));
+                            }
+                        }
+                        value.putNumberList(numberItem);
+                    }
+
+                    for (int i = 0; i < groupEmailCount; i++) {
+                        value = results.get(i);
+                        ArrayList<String> emailItem = new ArrayList<String>();
+                        for (int j = 0; j < emailCountInSimContact; j++) {
+                            if ((i * emailCountInSimContact + j) < arrayEmail.size()) {
+                                emailItem.add(arrayEmail.get(i * emailCountInSimContact + j));
+                            }
+                        }
+                        value.putEmailList(emailItem);
+                    }
+
+                    Uri itemUri = null;
+                    if (totalEmptyEmail < 0 && ContactUtils.canSaveEmail(
+                            QuickContactActivity.this, sub)) {
+                        Message e_msg = Message.obtain();
+                        e_msg.what = MSG_NO_EMPTY_EMAIL;
+                        mHandler.sendMessage(e_msg);
+                    }
+
+                    //get phone number from UsimEntity,then insert to SIM card
+                    for (int i = 0; i < groupCount; i++) {
+                        value = results.get(i);
+                        arrayNumber = (ArrayList<String>) value.getNumberList();
+                        arrayEmail = (ArrayList<String>) value.getEmailList();
+                        String strNum = arrayNumber.size() > 0 ? arrayNumber.get(0) : null;
+                        StringBuilder strAnrNum = new StringBuilder();
+                        for (int j = 1; j < arrayNumber.size(); j++) {
+                            String s = arrayNumber.get(j);
+                            strAnrNum.append(s);
+                            strAnrNum.append(SimContactsConstants.ANR_SEP);
+                        }
+                        StringBuilder strEmail = new StringBuilder();
+                        for (int j = 0; j < arrayEmail.size(); j++) {
+                            String s = arrayEmail.get(j);
+                            strEmail.append(s);
+                            strEmail.append(SimContactsConstants.EMAIL_SEP);
+                        }
+                        itemUri = ContactUtils.insertToCard(QuickContactActivity.this, strName,
+                                strNum, strEmail.toString(), strAnrNum.toString(), sub);
+                    }
+                    if (itemUri != null) {
+                        msg.what = MSG_COPY_DONE;
+                        mHandler.sendMessage(msg);
+                    } else {
+                        msg.what = MSG_COPY_FAILURE;
+                        mHandler.sendMessage(msg);
+                    }
+                }
+            }
+        }).start();
+    }
+
     private boolean showRawContactPickerDialog() {
         if (mContactData == null) return false;
         startActivityForResult(EditorIntents.createViewLinkedContactsIntent(
diff --git a/src/com/android/contacts/util/AccountSelectionUtil.java b/src/com/android/contacts/util/AccountSelectionUtil.java
index bfe8a08..0a84f02 100644
--- a/src/com/android/contacts/util/AccountSelectionUtil.java
+++ b/src/com/android/contacts/util/AccountSelectionUtil.java
@@ -93,7 +93,7 @@
             DialogInterface.OnCancelListener onCancelListener) {
         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(activity);
         final List<AccountWithDataSet> writableAccountList =
-                accountTypes.blockForWritableAccounts();
+                accountTypes.blockForWritableAccountsWithoutSim();
 
         Log.i(LOG_TAG, "The number of available accounts: " + writableAccountList.size());
 
@@ -183,6 +183,8 @@
             importIntent.putExtra("account_name", account.name);
             importIntent.putExtra("account_type", account.type);
             importIntent.putExtra("data_set", account.dataSet);
+            if (account.name == null && account.type == null)
+                importIntent.putExtra("local_account", true);
         }
 
         if (mVCardShare) {
diff --git a/src/com/android/contacts/util/ImageViewDrawableSetter.java b/src/com/android/contacts/util/ImageViewDrawableSetter.java
index b90cf1f..fa5b176 100644
--- a/src/com/android/contacts/util/ImageViewDrawableSetter.java
+++ b/src/com/android/contacts/util/ImageViewDrawableSetter.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.util;
 
+import android.accounts.Account;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -30,7 +31,7 @@
 import com.android.contacts.ContactPhotoManager.DefaultImageRequest;
 import com.android.contacts.lettertiles.LetterTileDrawable;
 import com.android.contacts.model.Contact;
-
+import com.android.contacts.model.RawContact;
 import java.util.Arrays;
 
 /**
@@ -53,9 +54,16 @@
     }
 
     public Bitmap setupContactPhoto(Contact contactData, ImageView photoView) {
+        Account account = null;
         mContact = contactData;
         setTarget(photoView);
-        return setCompressedImage(contactData.getPhotoBinaryData());
+        RawContact rawContact = contactData.getRawContacts().get(0);
+        final String accountType = rawContact.getAccountTypeString();
+        final String accountName = rawContact.getAccountName();
+        if (!TextUtils.isEmpty(accountType) && !TextUtils.isEmpty(accountName)) {
+            account = new Account(accountName, accountType);
+        }
+        return setCompressedImage(contactData.getPhotoBinaryData(), account);
     }
 
     public void setTransitionDuration(int durationInMillis) {
@@ -83,7 +91,7 @@
         return mCompressed;
     }
 
-    protected Bitmap setCompressedImage(byte[] compressed) {
+    protected Bitmap setCompressedImage(byte[] compressed, Account account) {
         if (mPreviousDrawable == null) {
             // If we don't already have a drawable, skip the exit-early test
             // below; otherwise we might not end up setting the default image.
@@ -99,7 +107,7 @@
 
         Drawable newDrawable = decodedBitmapDrawable(compressed);
         if (newDrawable == null) {
-            newDrawable = defaultDrawable();
+            newDrawable = defaultDrawable(account);
         }
 
         // Remember this for next time, so that we can check if it changed.
@@ -140,7 +148,7 @@
      * retrieve a default drawable for this contact. If not, then use the name as the contact
      * identifier instead.
      */
-    private Drawable defaultDrawable() {
+    private Drawable defaultDrawable(Account account) {
         Resources resources = mTarget.getResources();
         DefaultImageRequest request;
         int contactType = ContactPhotoManager.TYPE_DEFAULT;
@@ -156,7 +164,8 @@
             request = new DefaultImageRequest(mContact.getDisplayName(), mContact.getLookupKey(),
                     contactType, false /* isCircular */);
         }
-        return ContactPhotoManager.getDefaultAvatarDrawableForContact(resources, true, request);
+        return ContactPhotoManager.getDefaultAvatarDrawableForContact(
+                resources, true, request, account);
     }
 
     private BitmapDrawable decodedBitmapDrawable(byte[] compressed) {
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 2c69cdf..95634c2 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -584,11 +584,14 @@
         String accountName = null;
         String accountType = null;
         String dataSet = null;
+        boolean localAccount = false;
         final Intent intent = getIntent();
         if (intent != null) {
             accountName = intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME);
             accountType = intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE);
             dataSet = intent.getStringExtra(SelectAccountActivity.DATA_SET);
+            localAccount = intent.getBooleanExtra(
+                    SelectAccountActivity.LOCAL_ACCOUNT, false);
         } else {
             Log.e(LOG_TAG, "intent does not exist");
         }
@@ -596,16 +599,22 @@
         if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
             mAccount = new AccountWithDataSet(accountName, accountType, dataSet);
         } else {
-            final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
-            final List<AccountWithDataSet> accountList = accountTypes.blockForWritableAccounts();
-            if (accountList.size() == 0) {
-                mAccount = null;
-            } else if (accountList.size() == 1) {
-                mAccount = accountList.get(0);
+            if (localAccount) {
+                mAccount = AccountWithDataSet.getNullAccount();
             } else {
-                startActivityForResult(new Intent(this, SelectAccountActivity.class),
-                        SELECT_ACCOUNT);
-                return;
+                final AccountTypeManager accountTypes = AccountTypeManager
+                        .getInstance(this);
+                final List<AccountWithDataSet> accountList = accountTypes
+                        .blockForWritableAccounts();
+                if (accountList.size() == 0) {
+                    mAccount = null;
+                } else if (accountList.size() == 1) {
+                    mAccount = accountList.get(0);
+                } else {
+                    startActivityForResult(new Intent(this,
+                            SelectAccountActivity.class), SELECT_ACCOUNT);
+                    return;
+                }
             }
         }
 
diff --git a/src/com/android/contacts/vcard/SelectAccountActivity.java b/src/com/android/contacts/vcard/SelectAccountActivity.java
index ac5b3eb..3dafe91 100644
--- a/src/com/android/contacts/vcard/SelectAccountActivity.java
+++ b/src/com/android/contacts/vcard/SelectAccountActivity.java
@@ -35,6 +35,7 @@
     public static final String ACCOUNT_NAME = "account_name";
     public static final String ACCOUNT_TYPE = "account_type";
     public static final String DATA_SET = "data_set";
+    public static final String LOCAL_ACCOUNT = "local_account";
 
     private class CancelListener
             implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
@@ -58,7 +59,8 @@
         // - no account -> use phone-local storage without asking the user
         final int resId = R.string.import_from_vcf_file;
         final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
-        final List<AccountWithDataSet> accountList = accountTypes.blockForWritableAccounts();
+        final List<AccountWithDataSet> accountList = accountTypes
+                .blockForWritableAccountsWithoutSim();
         if (accountList.size() == 0) {
             Log.w(LOG_TAG, "Account does not exist");
             finish();
diff --git a/tests/src/com/android/contacts/test/mocks/MockContactPhotoManager.java b/tests/src/com/android/contacts/test/mocks/MockContactPhotoManager.java
index e0bd425..4b6f6c9 100644
--- a/tests/src/com/android/contacts/test/mocks/MockContactPhotoManager.java
+++ b/tests/src/com/android/contacts/test/mocks/MockContactPhotoManager.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.test.mocks;
 
+import android.accounts.Account;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.view.View;
@@ -29,14 +30,15 @@
  */
 public class MockContactPhotoManager extends ContactPhotoManager {
     @Override
-    public void loadThumbnail(ImageView view, long photoId, boolean darkTheme, boolean isCircular,
-            DefaultImageRequest defaultImageRequest, DefaultImageProvider defaultProvider) {
+    public void loadThumbnail(ImageView view, long photoId, Account account, boolean darkTheme,
+            boolean isCircular, DefaultImageRequest defaultImageRequest,
+            DefaultImageProvider defaultProvider) {
         defaultProvider.applyDefaultImage(view, -1, darkTheme, null);
     }
 
     @Override
-    public void loadPhoto(ImageView view, Uri photoUri, int requestedExtent, boolean darkTheme,
-            boolean isCircular, DefaultImageRequest defaultImageRequest,
+    public void loadPhoto(ImageView view, Uri photoUri, Account account, int requestedExtent,
+            boolean darkTheme, boolean isCircular, DefaultImageRequest defaultImageRequest,
             DefaultImageProvider defaultProvider) {
         defaultProvider.applyDefaultImage(view, requestedExtent, darkTheme, null);
     }