merge in klp-release history after reset to klp-dev
diff --git a/InCallUI/res/drawable-hdpi/picture_dialing.png b/InCallUI/res/drawable-hdpi/picture_dialing.png
new file mode 100644
index 0000000..6311f69
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/drawable-ldrtl-hdpi/picture_dialing.png b/InCallUI/res/drawable-ldrtl-hdpi/picture_dialing.png
new file mode 100644
index 0000000..cb587a8
--- /dev/null
+++ b/InCallUI/res/drawable-ldrtl-hdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/drawable-ldrtl-mdpi/picture_dialing.png b/InCallUI/res/drawable-ldrtl-mdpi/picture_dialing.png
new file mode 100644
index 0000000..4047b22
--- /dev/null
+++ b/InCallUI/res/drawable-ldrtl-mdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/drawable-ldrtl-xhdpi/picture_dialing.png b/InCallUI/res/drawable-ldrtl-xhdpi/picture_dialing.png
new file mode 100644
index 0000000..f167ecb
--- /dev/null
+++ b/InCallUI/res/drawable-ldrtl-xhdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/picture_dialing.png b/InCallUI/res/drawable-mdpi/picture_dialing.png
new file mode 100644
index 0000000..b81b603
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/picture_dialing.png b/InCallUI/res/drawable-xhdpi/picture_dialing.png
new file mode 100644
index 0000000..792cd8e
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/picture_dialing.png b/InCallUI/res/drawable-xxhdpi/picture_dialing.png
new file mode 100644
index 0000000..17d45f5
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/picture_dialing.png
Binary files differ
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
index fd8b83a..f67c04a 100644
--- a/InCallUI/res/layout/primary_call_info.xml
+++ b/InCallUI/res/layout/primary_call_info.xml
@@ -88,7 +88,7 @@
                  for certain kinds of calls (like "Internet call" for a SIP call.) -->
             <TextView android:id="@+id/callTypeLabel"
                       android:textAppearance="?android:attr/textAppearanceSmall"
-                      android:textColor="@color/incall_call_banner_text_color"
+                      android:textColor="@color/incall_callTypeSip"
                       android:maxLines="1"
                       android:layout_column="0"
                       android:layout_row="2"
diff --git a/InCallUI/res/values-zh-rCN/strings.xml b/InCallUI/res/values-zh-rCN/strings.xml
index a12d1bd..5b8a938 100644
--- a/InCallUI/res/values-zh-rCN/strings.xml
+++ b/InCallUI/res/values-zh-rCN/strings.xml
@@ -92,21 +92,21 @@
     <string name="sum_cfu_enabled_indicator" msgid="4014187342724130197">"转接所有来电"</string>
     <string name="sum_cfu_enabled" msgid="2450052502198827927">"将所有来电转接到 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
     <string name="sum_cfu_enabled_no_number" msgid="6591985777096823616">"未提供电话号码"</string>
-    <string name="sum_cfu_disabled" msgid="3698472522160364904">"已停用"</string>
+    <string name="sum_cfu_disabled" msgid="3698472522160364904">"未启用"</string>
     <string name="labelCFB" msgid="218938523102207587">"占线时转接"</string>
     <string name="messageCFB" msgid="3711089705936187129">"占线时的转接号码"</string>
     <string name="sum_cfb_enabled" msgid="5984198104833116690">"转接到 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
-    <string name="sum_cfb_disabled" msgid="227440009979537651">"已停用"</string>
+    <string name="sum_cfb_disabled" msgid="227440009979537651">"未启用"</string>
     <string name="disable_cfb_forbidden" msgid="3506984333877998061">"您的运营商不支持在手机占线时停用来电转接功能。"</string>
     <string name="labelCFNRy" msgid="3646316323834351390">"无人接听时转接"</string>
     <string name="messageCFNRy" msgid="672317899884380374">"无人接听时的转接号码"</string>
     <string name="sum_cfnry_enabled" msgid="6955775691317662910">"转接到 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
-    <string name="sum_cfnry_disabled" msgid="8422350929957344729">"已停用"</string>
+    <string name="sum_cfnry_disabled" msgid="8422350929957344729">"未启用"</string>
     <string name="disable_cfnry_forbidden" msgid="4308233959150658058">"您的运营商不支持在手机无人接听时停用来电转接功能。"</string>
     <string name="labelCFNRc" msgid="47183615370850000">"无法接通时转接"</string>
     <string name="messageCFNRc" msgid="6380695421020295119">"无法接通时的转接号码"</string>
     <string name="sum_cfnrc_enabled" msgid="7010898346095497421">"转接到 <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
-    <string name="sum_cfnrc_disabled" msgid="7222141261321276464">"已停用"</string>
+    <string name="sum_cfnrc_disabled" msgid="7222141261321276464">"未启用"</string>
     <string name="disable_cfnrc_forbidden" msgid="5646361343094064333">"您的运营商不支持在手机无法接通时停用来电转接功能。"</string>
     <string name="updating_title" msgid="6146755386174019046">"通话设置"</string>
     <string name="error_updating_title" msgid="7970259216988931777">"通话设置出错"</string>
@@ -313,14 +313,14 @@
     <string name="manage_fdn_list" msgid="8777755791892122369">"固定拨号列表"</string>
     <string name="fdn_activation" msgid="2156479741307463576">"激活固定拨号"</string>
     <string name="fdn_enabled" msgid="5238109009915521240">"已启用固定拨号"</string>
-    <string name="fdn_disabled" msgid="4700049736675368279">"已停用固定拨号"</string>
+    <string name="fdn_disabled" msgid="4700049736675368279">"未启用固定拨号"</string>
     <string name="enable_fdn" msgid="3740191529180493851">"启用固定拨号"</string>
-    <string name="disable_fdn" msgid="7944020890722540616">"禁用固定拨号"</string>
+    <string name="disable_fdn" msgid="7944020890722540616">"停用固定拨号"</string>
     <string name="change_pin2" msgid="2153563695382176676">"更改 PIN2"</string>
-    <string name="enable_fdn_ok" msgid="7215588870329688132">"禁用固定拨号"</string>
+    <string name="enable_fdn_ok" msgid="7215588870329688132">"停用固定拨号"</string>
     <string name="disable_fdn_ok" msgid="5727046928930740173">"启用固定拨号"</string>
     <string name="sum_fdn" msgid="1959399454900272878">"管理固定拨号"</string>
-    <string name="sum_fdn_change_pin" msgid="6666549734792827932">"更改为访问固定拨号设置的 PIN"</string>
+    <string name="sum_fdn_change_pin" msgid="6666549734792827932">"更改使用固定拨号所需的 PIN 码"</string>
     <string name="sum_fdn_manage_list" msgid="8431088265332628316">"管理手机号码列表"</string>
     <string name="voice_privacy" msgid="3776841382844614716">"语音隐私权"</string>
     <string name="voice_privacy_summary" msgid="3159383389833516214">"启用增强型隐秘模式"</string>
@@ -364,7 +364,7 @@
     <string name="enable_sim_pin" msgid="5803702443844458831">"启用 SIM 卡 PIN"</string>
     <string name="enable_in_progress" msgid="3695303775100109650">"请稍候..."</string>
     <string name="enable_pin_ok" msgid="9166061915030865848">"SIM 卡 PIN 已启用。"</string>
-    <string name="disable_pin_ok" msgid="5596999379593924850">"SIM 卡 PIN 已停用。"</string>
+    <string name="disable_pin_ok" msgid="5596999379593924850">"未启用 SIM 卡 PIN 码。"</string>
     <string name="pin_failed" msgid="2298841192582192277">"键入的 PIN 有误。"</string>
     <string name="pin_changed" msgid="4365538014588501049">"已成功更改 SIM 卡 PIN。"</string>
     <string name="puk_requested" msgid="3898394204193202803">"密码不正确,SIM 已锁定!需要 PUK2。"</string>
@@ -448,8 +448,8 @@
     <string name="hac_mode_title" msgid="8740268574688743289">"助听器"</string>
     <string name="hac_mode_summary" msgid="6833851160514929341">"启用助听器兼容模式"</string>
   <string-array name="tty_mode_entries">
-    <item msgid="512950011423868021">"TTY 已停用"</item>
-    <item msgid="3971695875449640648">"TTY 已满"</item>
+    <item msgid="512950011423868021">"未启用 TTY"</item>
+    <item msgid="3971695875449640648">"TTY 完整"</item>
     <item msgid="1937509904407445684">"TTY HCO"</item>
     <item msgid="5644925873488772224">"TTY VCO"</item>
   </string-array>
@@ -507,15 +507,15 @@
     <string name="contactPhoto" msgid="4713193418046639466">"联系人照片"</string>
     <string name="goPrivate" msgid="865837794424530980">"私聊"</string>
     <string name="selectContact" msgid="781975788478987237">"选择联系人"</string>
-    <string name="sip_settings" msgid="6699045718560859691">"互联网通话设置"</string>
+    <string name="sip_settings" msgid="6699045718560859691">"互联网电话设置"</string>
     <string name="sip_accounts" msgid="2097054001288457163">"互联网电话 (SIP) 帐户"</string>
     <string name="sip_accounts_title" msgid="1212889069281054570">"帐户"</string>
     <string name="sip_receive_calls" msgid="5635685570889971559">"接听来电"</string>
     <string name="sip_receive_calls_summary" msgid="8403613238633679184">"会缩短电池待机时间"</string>
     <string name="sip_call_options_title" msgid="27433718808839672">"使用互联网电话"</string>
-    <string name="sip_call_options_wifi_only_title" msgid="145572394529090811">"使用互联网通话(仅限 WLAN)"</string>
+    <string name="sip_call_options_wifi_only_title" msgid="145572394529090811">"使用互联网电话(仅限 WLAN)"</string>
     <string name="sip_call_options_entry_1" msgid="6556668894178520760">"数据网络接通时适用于所有通话"</string>
-    <string name="sip_call_options_entry_2" msgid="6789764029719494361">"仅适用于互联网通话"</string>
+    <string name="sip_call_options_entry_2" msgid="6789764029719494361">"仅适用于互联网电话"</string>
     <string name="sip_call_options_entry_3" msgid="7474809001402649185">"每次呼叫时询问"</string>
     <string name="sip_call_options_wifi_only_entry_1" msgid="7423523864471650069">"适用于所有通话"</string>
     <string name="pick_outgoing_call_phone_type" msgid="5622916534828338675">"拨打电话"</string>
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index e53a347..a508312 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -24,7 +24,6 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.view.ViewStub;
 import android.widget.CompoundButton;
 import android.widget.ImageButton;
 import android.widget.PopupMenu;
@@ -55,7 +54,7 @@
     private View mEndCallButton;
     private View mExtraRowButton;
     private View mManageConferenceButton;
-    private View mCDMAMergeButton;
+    private View mGenericMergeButton;
 
     @Override
     CallButtonPresenter createPresenter() {
@@ -88,7 +87,13 @@
                 getPresenter().manageConferenceButtonClicked();
             }
         });
-        mCDMAMergeButton = parent.findViewById(R.id.cdmaMergeButton);
+        mGenericMergeButton = parent.findViewById(R.id.cdmaMergeButton);
+        mGenericMergeButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                getPresenter().mergeClicked();
+            }
+        });
 
         mEndCallButton = parent.findViewById(R.id.endButton);
         mEndCallButton.setOnClickListener(new View.OnClickListener() {
@@ -144,6 +149,14 @@
     }
 
     @Override
+    public void onResume() {
+        if (getPresenter() != null) {
+            getPresenter().refreshMuteState();
+        }
+        super.onResume();
+    }
+
+    @Override
     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
     }
 
@@ -502,14 +515,14 @@
     public void showManageConferenceCallButton() {
         mExtraRowButton.setVisibility(View.VISIBLE);
         mManageConferenceButton.setVisibility(View.VISIBLE);
-        mCDMAMergeButton.setVisibility(View.GONE);
+        mGenericMergeButton.setVisibility(View.GONE);
     }
 
     @Override
-    public void showCDMAMergeButton() {
+    public void showGenericMergeButton() {
         mExtraRowButton.setVisibility(View.VISIBLE);
         mManageConferenceButton.setVisibility(View.GONE);
-        mCDMAMergeButton.setVisibility(View.VISIBLE);
+        mGenericMergeButton.setVisibility(View.VISIBLE);
     }
 
     @Override
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 3171816..4de129d 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -23,6 +23,8 @@
 import com.android.services.telephony.common.Call;
 import com.android.services.telephony.common.Call.Capabilities;
 
+import android.telephony.PhoneNumberUtils;
+
 /**
  * Logic for call buttons.
  */
@@ -34,6 +36,8 @@
     private boolean mAutomaticallyMuted = false;
     private boolean mPreviousMuteState = false;
 
+    private InCallState mPreviousState = null;
+
     public CallButtonPresenter() {
     }
 
@@ -64,11 +68,22 @@
             mCall = callList.getOutgoingCall();
         } else if (state == InCallState.INCALL) {
             mCall = callList.getActiveOrBackgroundCall();
+
+            // When connected to voice mail, automatically shows the dialpad.
+            // (On previous releases we showed it when in-call shows up, before waiting for
+            // OUTGOING.  We may want to do that once we start showing "Voice mail" label on
+            // the dialpad too.)
+            if (mPreviousState == InCallState.OUTGOING
+                    && mCall != null && PhoneNumberUtils.isVoiceMailNumber(mCall.getNumber())) {
+                getUi().displayDialpad(true);
+            }
         } else {
             mCall = null;
         }
 
         updateUi(state, mCall);
+
+        mPreviousState = state;
     }
 
     @Override
@@ -138,7 +153,6 @@
             return;
         }
 
-        // TODO(klp): hook up call id.
         CallCommandClient.getInstance().disconnectCall(mCall.getCallId());
     }
 
@@ -158,7 +172,6 @@
 
         Log.d(this, "holding: " + mCall.getCallId());
 
-        // TODO(klp): use appropriate hold callId.
         CallCommandClient.getInstance().hold(mCall.getCallId(), checked);
         getUi().setHold(checked);
     }
@@ -172,7 +185,6 @@
         mAutomaticallyMuted = true;
         mPreviousMuteState = AudioModeProvider.getInstance().getMute();
         // Simulate a click on the mute button
-        getUi().setMute(true);
         muteClicked(true);
 
         CallCommandClient.getInstance().addCall();
@@ -211,8 +223,12 @@
 
             final boolean canMerge = call.can(Capabilities.MERGE_CALLS);
             final boolean canAdd = call.can(Capabilities.ADD_CALL);
+            final boolean isGenericConference = call.can(Capabilities.GENERIC_CONFERENCE);
 
-            if (canMerge) {
+            final boolean showGenericMerge = isGenericConference && canMerge;
+            final boolean showMerge = !isGenericConference && canMerge;
+
+            if (showMerge) {
                 ui.showMerge(true);
                 ui.showAddCall(false);
             } else {
@@ -221,18 +237,40 @@
                 ui.enableAddCall(canAdd);
             }
 
-            ui.showHold(call.can(Capabilities.SUPPORT_HOLD));
-            ui.setHold(call.getState() == Call.State.ONHOLD);
-            ui.enableHold(call.can(Capabilities.HOLD));
+            final boolean canHold = call.can(Capabilities.HOLD);
+            final boolean canSwap = call.can(Capabilities.SWAP_CALLS);
+            final boolean supportHold = call.can(Capabilities.SUPPORT_HOLD);
 
-            ui.showSwap(call.can(Capabilities.SWAP_CALLS));
+            if (canHold) {
+                ui.showHold(true);
+                ui.setHold(call.getState() == Call.State.ONHOLD);
+                ui.enableHold(true);
+                ui.showSwap(false);
+            } else if (canSwap) {
+                ui.showHold(false);
+                ui.showSwap(true);
+            } else {
+                // Neither "Hold" nor "Swap" is available.  This can happen for two
+                // reasons:
+                //   (1) this is a transient state on a device that *can*
+                //       normally hold or swap, or
+                //   (2) this device just doesn't have the concept of hold/swap.
+                //
+                // In case (1), show the "Hold" button in a disabled state.  In case
+                // (2), remove the button entirely.  (This means that the button row
+                // will only have 4 buttons on some devices.)
 
-            // Restore the previous mute state
-            if (mAutomaticallyMuted &&
-                    AudioModeProvider.getInstance().getMute() != mPreviousMuteState) {
-                ui.setMute(mPreviousMuteState);
-                mAutomaticallyMuted = false;
+                if (supportHold) {
+                    ui.showHold(true);
+                    ui.enableHold(false);
+                    ui.setHold(call.getState() == Call.State.ONHOLD);
+                    ui.showSwap(false);
+                } else {
+                    ui.showHold(false);
+                    ui.showSwap(false);
+                }
             }
+
             ui.enableMute(call.can(Capabilities.MUTE));
 
             // Finally, update the "extra button row": It's displayed above the
@@ -243,20 +281,18 @@
             //
             // - "Manage conference" (used only on GSM devices)
             // - "Merge" button (used only on CDMA devices)
-            // TODO(klp) Add cdma merge button
-            final boolean showCdmaMerge = false;
-//                    (phoneType == PhoneConstants.PHONE_TYPE_CDMA) && inCallControlState.canMerge;
-            final boolean showExtraButtonRow =
-                    (showCdmaMerge || call.isConferenceCall()) && !getUi().isDialpadVisible();
+
+            final boolean showManageConference = (call.isConferenceCall() && !isGenericConference);
+            final boolean showExtraButtonRow = (showGenericMerge || showManageConference) &&
+                    !getUi().isDialpadVisible();
+
+            Log.i(this, "isGeneric: " + isGenericConference);
+            Log.i(this, "showManageConference : " + showManageConference);
+            Log.i(this, "showGenericMerge: " + showGenericMerge);
             if (showExtraButtonRow) {
-                // Need to set up mCdmaMergeButton and mManageConferenceButton if this is the first
-                // time they're visible.
-                // TODO(klp) add cdma merge button
-//                if (mCdmaMergeButton == null) {
-//                    setupExtraButtons();
-//                }
-//                mCdmaMergeButton.setVisibility(showCdmaMerge ? View.VISIBLE : View.GONE);
-                if (call.isConferenceCall()) {
+                if (showGenericMerge) {
+                    getUi().showGenericMergeButton();
+                } else if (showManageConference) {
                     getUi().showManageConferenceCallButton();
                 }
             } else {
@@ -265,6 +301,18 @@
         }
     }
 
+    public void refreshMuteState() {
+        // Restore the previous mute state
+        if (mAutomaticallyMuted &&
+                AudioModeProvider.getInstance().getMute() != mPreviousMuteState) {
+            if (getUi() == null) {
+                return;
+            }
+            muteClicked(mPreviousMuteState);
+        }
+        mAutomaticallyMuted = false;
+    }
+
     public interface CallButtonUi extends Ui {
         void setVisible(boolean on);
         void setMute(boolean on);
@@ -281,7 +329,7 @@
         void setAudio(int mode);
         void setSupportedAudio(int mask);
         void showManageConferenceCallButton();
-        void showCDMAMergeButton();
+        void showGenericMergeButton();
         void hideExtraRow();
         void displayManageConferencePanel(boolean on);
     }
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 8fb2eb4..49ee786 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -45,6 +45,7 @@
     private TextView mNumberLabel;
     private TextView mPrimaryName;
     private TextView mCallStateLabel;
+    private TextView mCallTypeLabel;
     private ImageView mPhoto;
     private TextView mElapsedTime;
     private View mProviderInfo;
@@ -100,6 +101,7 @@
         mSecondaryCallInfo = (ViewStub) view.findViewById(R.id.secondary_call_info);
         mPhoto = (ImageView) view.findViewById(R.id.photo);
         mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);
+        mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel);
         mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime);
         mProviderInfo = view.findViewById(R.id.providerInfo);
         mProviderLabel = (TextView) view.findViewById(R.id.providerLabel);
@@ -166,12 +168,12 @@
 
     @Override
     public void setPrimary(String number, String name, boolean nameIsNumber, String label,
-            Drawable photo, boolean isConference) {
+            Drawable photo, boolean isConference, boolean isGeneric, boolean isSipCall) {
         Log.d(this, "Setting primary call");
 
         if (isConference) {
-            name = getView().getResources().getString(R.string.card_title_conf_call);
-            photo = getView().getResources().getDrawable(R.drawable.picture_conference);
+            name = getConferenceString(isGeneric);
+            photo = getConferencePhoto(isGeneric);
             nameIsNumber = false;
         }
 
@@ -183,17 +185,19 @@
         // Set the label (Mobile, Work, etc)
         setPrimaryLabel(label);
 
+        showInternetCallLabel(isSipCall);
+
         setDrawableToImageView(mPhoto, photo);
     }
 
     @Override
     public void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
-            Drawable photo, boolean isConference) {
+            Drawable photo, boolean isConference, boolean isGeneric) {
 
         if (show) {
             if (isConference) {
-                name = getView().getResources().getString(R.string.card_title_conf_call);
-                photo = getView().getResources().getDrawable(R.drawable.picture_conference);
+                name = getConferenceString(isGeneric);
+                photo = getConferencePhoto(isGeneric);
                 nameIsNumber = false;
             }
 
@@ -277,6 +281,17 @@
         }
     }
 
+    private void showInternetCallLabel(boolean show) {
+        if (show) {
+            final String label = getView().getContext().getString(
+                    R.string.incall_call_type_label_sip);
+            mCallTypeLabel.setVisibility(View.VISIBLE);
+            mCallTypeLabel.setText(label);
+        } else {
+            mCallTypeLabel.setVisibility(View.GONE);
+        }
+    }
+
     @Override
     public void setPrimaryCallElapsedTime(boolean show, String callTimeElapsed) {
         if (show) {
@@ -301,10 +316,22 @@
             AnimationUtils.Fade.show(view);
         } else {
             AnimationUtils.startCrossFade(view, current, photo);
-            mPhoto.setVisibility(View.VISIBLE);
+            view.setVisibility(View.VISIBLE);
         }
     }
 
+    private String getConferenceString(boolean isGeneric) {
+        Log.v(this, "isGenericString: " + isGeneric);
+        final int resId = isGeneric ? R.string.card_title_in_call : R.string.card_title_conf_call;
+        return getView().getResources().getString(resId);
+    }
+
+    private Drawable getConferencePhoto(boolean isGeneric) {
+        Log.v(this, "isGenericPhoto: " + isGeneric);
+        final int resId = isGeneric ? R.drawable.picture_dialing : R.drawable.picture_conference;
+        return getView().getResources().getDrawable(resId);
+    }
+
     private void setBluetoothOn(boolean onOff) {
         // Also, display a special icon (alongside the "Incoming call"
         // label) if there's an incoming call and audio will be routed
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index e1e3bd4..1830f05 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -33,6 +33,7 @@
 import com.android.incallui.InCallPresenter.IncomingCallListener;
 import com.android.services.telephony.common.AudioMode;
 import com.android.services.telephony.common.Call;
+import com.android.services.telephony.common.Call.Capabilities;
 import com.android.services.telephony.common.CallIdentification;
 import com.google.common.base.Preconditions;
 
@@ -74,15 +75,13 @@
 
             final CallIdentification identification = call.getIdentification();
 
-            // TODO(klp): Logic to determine which ui field get what data resides in
-            // contactInfoCache.
-            // It needs to be moved so it can be re-used.
-            mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext, identification,
-                    call.getState() == Call.State.INCOMING);
-
             // start processing lookups right away.
-            startContactInfoSearch(identification, true, call.isConferenceCall(),
-                    call.getState() == Call.State.INCOMING);
+            if (!call.isConferenceCall()) {
+                startContactInfoSearch(identification, true,
+                        call.getState() == Call.State.INCOMING);
+            } else {
+                updateContactEntry(null, true, true);
+            }
         }
     }
 
@@ -150,35 +149,31 @@
         Log.d(this, "Primary call: " + primary);
         Log.d(this, "Secondary call: " + secondary);
 
-        if (primary != null) {
-            if (mPrimary == null || mPrimary.getCallId() != primary.getCallId()) {
-                // primary call has changed
-                mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext,
-                        primary.getIdentification(), primary.getState() == Call.State.INCOMING);
-                updatePrimaryDisplayInfo(mPrimaryContactInfo, isConference(primary));
-                startContactInfoSearch(primary.getIdentification(), true,
-                        primary.isConferenceCall(), primary.getState() == Call.State.INCOMING);
-            }
+        final boolean primaryChanged = !areCallsSame(mPrimary, primary);
+        final boolean secondaryChanged = !areCallsSame(mSecondary, secondary);
+        mSecondary = secondary;
+        mPrimary = primary;
+
+        if (primaryChanged && mPrimary != null) {
+            // primary call has changed
+            mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext,
+                    mPrimary.getIdentification(), mPrimary.getState() == Call.State.INCOMING);
+            updatePrimaryDisplayInfo(mPrimaryContactInfo, isConference(mPrimary));
+            maybeStartSearch(mPrimary, true);
         }
 
-        if (secondary == null) {
+        if (mSecondary == null) {
             // Secondary call may have ended.  Update the ui.
             mSecondaryContactInfo = null;
             updateSecondaryDisplayInfo(false);
-        } else {
-            if (mSecondary == null || mSecondary.getCallId() != secondary.getCallId()) {
-                // secondary call has changed
-                mSecondaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext,
-                        secondary.getIdentification(), secondary.getState() == Call.State.INCOMING);
-                updateSecondaryDisplayInfo(secondary.isConferenceCall());
-                startContactInfoSearch(secondary.getIdentification(), false,
-                        secondary.isConferenceCall(), secondary.getState() == Call.State.INCOMING);
-            }
+        } else if (secondaryChanged) {
+            // secondary call has changed
+            mSecondaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext,
+                    mSecondary.getIdentification(), mSecondary.getState() == Call.State.INCOMING);
+            updateSecondaryDisplayInfo(mSecondary.isConferenceCall());
+            maybeStartSearch(mSecondary, false);
         }
 
-        mPrimary = primary;
-        mSecondary = secondary;
-
         // Start/Stop the call time update timer
         if (mPrimary != null && mPrimary.getState() == Call.State.ACTIVE) {
             Log.d(this, "Starting the calltime timer");
@@ -233,18 +228,36 @@
         }
     }
 
+    private boolean areCallsSame(Call call1, Call call2) {
+        if (call1 == null && call2 == null) {
+            return true;
+        } else if (call1 == null || call2 == null) {
+            return false;
+        }
+
+        // otherwise compare call Ids
+        return call1.getCallId() == call2.getCallId();
+    }
+
+    private void maybeStartSearch(Call call, boolean isPrimary) {
+        // no need to start search for conference calls which show generic info.
+        if (call != null && !call.isConferenceCall()) {
+            startContactInfoSearch(call.getIdentification(), isPrimary,
+                    call.getState() == Call.State.INCOMING);
+        }
+    }
+
     /**
      * Starts a query for more contact data for the save primary and secondary calls.
      */
     private void startContactInfoSearch(final CallIdentification identification,
-            final boolean isPrimary, final boolean isConference, boolean isIncoming) {
-
+            final boolean isPrimary, boolean isIncoming) {
         final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
 
         cache.findInfo(identification, isIncoming, new ContactInfoCacheCallback() {
                 @Override
                 public void onContactInfoComplete(int callId, ContactCacheEntry entry) {
-                    updateContactEntry(entry, isPrimary, isConference);
+                    updateContactEntry(entry, isPrimary, false);
                     if (entry.name != null) {
                         Log.d(TAG, "Contact found: " + entry);
                     }
@@ -266,11 +279,12 @@
             });
     }
 
-    private boolean isConference(Call call) {
-        if (call == null) {
-            return false;
-        }
-        return call.isConferenceCall();
+    private static boolean isConference(Call call) {
+        return call != null && call.isConferenceCall();
+    }
+
+    private static boolean isGenericConference(Call call) {
+        return call != null && call.can(Capabilities.GENERIC_CONFERENCE);
     }
 
     private void updateContactEntry(ContactCacheEntry entry, boolean isPrimary,
@@ -332,16 +346,15 @@
             return;
         }
 
+        final boolean isGenericConf = isGenericConference(mPrimary);
         if (entry != null) {
             final String name = getNameForCall(entry);
             final String number = getNumberForCall(entry);
             final boolean nameIsNumber = name != null && name.equals(entry.number);
-
             ui.setPrimary(number, name, nameIsNumber, entry.label,
-                    entry.photo, isConference);
+                    entry.photo, isConference, isGenericConf, entry.isSipCall);
         } else {
-            // reset to nothing (like at end of call)
-            ui.setPrimary(null, null, false, null, null, false);
+            ui.setPrimary(null, null, false, null, null, isConference, isGenericConf, false);
         }
 
     }
@@ -353,6 +366,7 @@
             return;
         }
 
+        final boolean isGenericConf = isGenericConference(mSecondary);
         if (mSecondaryContactInfo != null) {
             Log.d(TAG, "updateSecondaryDisplayInfo() " + mSecondaryContactInfo);
             final String nameForCall = getNameForCall(mSecondaryContactInfo);
@@ -360,10 +374,10 @@
             final boolean nameIsNumber = nameForCall != null && nameForCall.equals(
                     mSecondaryContactInfo.number);
             ui.setSecondary(true, nameForCall, nameIsNumber, mSecondaryContactInfo.label,
-                    mSecondaryContactInfo.photo, isConference);
+                    mSecondaryContactInfo.photo, isConference, isGenericConf);
         } else {
             // reset to nothing so that it starts off blank next time we use it.
-            ui.setSecondary(false, null, false, null, null, false);
+            ui.setSecondary(false, null, false, null, null, isConference, isGenericConf);
         }
     }
 
@@ -437,9 +451,9 @@
     public interface CallCardUi extends Ui {
         void setVisible(boolean on);
         void setPrimary(String number, String name, boolean nameIsNumber, String label,
-                Drawable photo, boolean isConference);
+                Drawable photo, boolean isConference, boolean isGeneric, boolean isSipCall);
         void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
-                Drawable photo, boolean isConference);
+                Drawable photo, boolean isConference, boolean isGeneric);
         void setSecondaryImage(Drawable image);
         void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn,
                 String gatewayLabel, String gatewayNumber);
diff --git a/InCallUI/src/com/android/incallui/CallHandlerService.java b/InCallUI/src/com/android/incallui/CallHandlerService.java
index d50635d..06b10ab 100644
--- a/InCallUI/src/com/android/incallui/CallHandlerService.java
+++ b/InCallUI/src/com/android/incallui/CallHandlerService.java
@@ -174,8 +174,9 @@
         }
 
         @Override
-        public void bringToForeground() {
-            mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_BRING_TO_FOREGROUND));
+        public void bringToForeground(boolean showDialpad) {
+            mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_BRING_TO_FOREGROUND,
+                    showDialpad ? 1 : 0, 0));
         }
 
         @Override
@@ -290,9 +291,9 @@
                 mAudioModeProvider.onSupportedAudioModeChange(msg.arg1);
                 break;
             case ON_BRING_TO_FOREGROUND:
-                Log.i(TAG, "ON_BRING_TO_FOREGROUND");
+                Log.i(TAG, "ON_BRING_TO_FOREGROUND" + msg.arg1);
                 if (mInCallPresenter != null) {
-                    mInCallPresenter.bringToForeground();
+                    mInCallPresenter.bringToForeground(msg.arg1 != 0);
                 }
                 break;
             case ON_START:
diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java
index 2c033e9..6c05650 100644
--- a/InCallUI/src/com/android/incallui/ContactInfoCache.java
+++ b/InCallUI/src/com/android/incallui/ContactInfoCache.java
@@ -25,6 +25,7 @@
 import android.os.Looper;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 
 import com.android.incallui.service.PhoneNumberService;
@@ -265,8 +266,6 @@
         Log.d(this, "Image load complete with context: ", mContext);
         // TODO: may be nice to update the image view again once the newer one
         // is available on contacts database.
-        // TODO (klp): What is this, and why does it need the write_contacts permission?
-        // CallerInfoUtils.sendViewNotificationAsync(mContext, mLoadingPersonUri);
 
         final int callId = (Integer) cookie;
         final ContactCacheEntry entry = mInfoMap.get(callId);
@@ -278,7 +277,7 @@
         }
         Log.d(this, "setting photo for entry: ", entry);
 
-        // TODO (klp): Handle conference calls
+        // Conference call icons are being handled in CallCardPresenter.
         if (photo != null) {
             Log.v(this, "direct drawable: ", photo);
             entry.photo = photo;
@@ -354,6 +353,7 @@
         String displayNumber = null;
         String displayLocation = null;
         String label = null;
+        boolean isSipCall = false;
 
             // It appears that there is a small change in behaviour with the
             // PhoneUtils' startGetCallerInfo whereby if we query with an
@@ -378,8 +378,12 @@
             // Then we could remove this hack, and instead ask the CallerInfo
             // for a "user visible" form of the SIP address.
             String number = info.phoneNumber;
-            if (number != null && number.startsWith("sip:")) {
-                number = number.substring(4);
+
+            if (!TextUtils.isEmpty(number)) {
+                isSipCall = PhoneNumberUtils.isUriNumber(number);
+                if (number.startsWith("sip:")) {
+                    number = number.substring(4);
+                }
             }
 
             if (TextUtils.isEmpty(info.name)) {
@@ -450,6 +454,7 @@
         cce.number = displayNumber;
         cce.location = displayLocation;
         cce.label = label;
+        cce.isSipCall = isSipCall;
     }
 
     /**
@@ -504,6 +509,7 @@
         public String location;
         public String label;
         public Drawable photo;
+        public boolean isSipCall;
         public Uri personUri; // Used for local photo load
 
         @Override
@@ -514,6 +520,7 @@
                     .add("location", MoreStrings.toSafeString(location))
                     .add("label", label)
                     .add("photo", photo)
+                    .add("isSipCall", isSipCall)
                     .toString();
         }
     }
diff --git a/InCallUI/src/com/android/incallui/DialpadPresenter.java b/InCallUI/src/com/android/incallui/DialpadPresenter.java
index cee80d2..8640f8a 100644
--- a/InCallUI/src/com/android/incallui/DialpadPresenter.java
+++ b/InCallUI/src/com/android/incallui/DialpadPresenter.java
@@ -50,7 +50,6 @@
      * DTMF tone (or short tone if requested), and appending the digit to the
      * EditText field that displays the DTMF digits sent so far.
      */
-    // TODO(klp) Add timedShortTone==true handling for accessibility cases.
     public final void processDtmf(char c, boolean timedShortTone) {
         Log.d(this, "Processing dtmf key " + c);
         // if it is a valid key, then update the display and send the dtmf tone.
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index 7d7ca4b..f4ddf62 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -16,6 +16,9 @@
 
 package com.android.incallui;
 
+import com.android.services.telephony.common.Call;
+import com.android.services.telephony.common.Call.State;
+
 import android.app.Activity;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -30,6 +33,9 @@
  * Phone app "in call" screen.
  */
 public class InCallActivity extends Activity {
+
+    public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad";
+
     private CallButtonFragment mCallButtonFragment;
     private CallCardFragment mCallCardFragment;
     private AnswerFragment mAnswerFragment;
@@ -37,6 +43,9 @@
     private ConferenceManagerFragment mConferenceManagerFragment;
     private boolean mIsForegroundActivity;
 
+    /** Use to pass 'showDialpad' from {@link #onNewIntent} to {@link #onResume} */
+    private boolean mShowDialpadRequested;
+
     @Override
     protected void onCreate(Bundle icicle) {
         Log.d(this, "onCreate()...  this = " + this);
@@ -78,6 +87,11 @@
 
         mIsForegroundActivity = true;
         InCallPresenter.getInstance().onUiShowing(true);
+
+        if (mShowDialpadRequested) {
+            mCallButtonFragment.displayDialpad(true);
+            mShowDialpadRequested = false;
+        }
     }
 
     // onPause is guaranteed to be called when the InCallActivity goes
@@ -236,40 +250,35 @@
             // But we do check here for one extra that can come along with the
             // ACTION_MAIN intent:
 
-            // TODO(klp): Enable this for klp
-            /*
             if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
                 // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
                 // dialpad should be initially visible.  If the extra isn't
                 // present at all, we just leave the dialpad in its previous state.
 
-                boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
-                if (VDBG) log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);
+                final boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
+                Log.d(this, "- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);
 
-                // If SHOW_DIALPAD_EXTRA is specified, that overrides whatever
-                // the previous state of inCallUiState.showDialpad was.
-                mApp.inCallUiState.showDialpad = showDialpad;
-
-                final boolean hasActiveCall = mCM.hasActiveFgCall();
-                final boolean hasHoldingCall = mCM.hasActiveBgCall();
-
-                // There's only one line in use, AND it's on hold, at which we're sure the user
-                // wants to use the dialpad toward the exact line, so un-hold the holding line.
-                if (showDialpad && !hasActiveCall && hasHoldingCall) {
-                    PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
-                }
+                relaunchedFromDialer(showDialpad);
             }
-            */
-            // ...and in onResume() we'll update the onscreen dialpad state to
-            // match the InCallUiState.
 
             return;
         }
     }
 
+    private void relaunchedFromDialer(boolean showDialpad) {
+        mShowDialpadRequested = showDialpad;
+
+        if (mShowDialpadRequested) {
+            // If there's only one line in use, AND it's on hold, then we're sure the user
+            // wants to use the dialpad toward the exact line, so un-hold the holding line.
+            final Call call = CallList.getInstance().getActiveOrBackgroundCall();
+            if (call != null && call.getState() == State.ONHOLD) {
+                CallCommandClient.getInstance().hold(call.getCallId(), false);
+            }
+        }
+    }
+
     private void initializeInCall() {
-        // TODO(klp): Make sure that this doesn't need to move back to onResume() since they are
-        // statically added fragments.
         if (mCallButtonFragment == null) {
             mCallButtonFragment = (CallButtonFragment) getFragmentManager()
                     .findFragmentById(R.id.callButtonFragment);
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index d49859a..bfff83f 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -320,7 +320,7 @@
     /**
      * Brings the app into the foreground if possible.
      */
-    public void bringToForeground() {
+    public void bringToForeground(boolean showDialpad) {
         // Before we bring the incall UI to the foreground, we check to see if:
         // 1. there is an activity
         // 2. the activity is not already in the foreground
@@ -328,7 +328,7 @@
         if (isActivityStarted() &&
                 !isShowingInCallUi() &&
                 mInCallState != InCallState.NO_CALLS) {
-            showInCall();
+            showInCall(showDialpad);
         }
     }
 
@@ -400,7 +400,7 @@
 
         if (showCallUi) {
             Log.i(this, "Start in call UI");
-            showInCall();
+            showInCall(false);
         } else if (startStartupSequence) {
             Log.i(this, "Start Full Screen in call UI");
             mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(newState, mCallList);
@@ -460,16 +460,19 @@
         }
     }
 
-    private void showInCall() {
-        mContext.startActivity(getInCallIntent());
+    private void showInCall(boolean showDialpad) {
+        mContext.startActivity(getInCallIntent(showDialpad));
     }
 
-    public Intent getInCallIntent() {
+    public Intent getInCallIntent(boolean showDialpad) {
         final Intent intent = new Intent(Intent.ACTION_MAIN, null);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                 | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
         intent.setClass(mContext, InCallActivity.class);
+        if (showDialpad) {
+            intent.putExtra(InCallActivity.SHOW_DIALPAD_EXTRA, true);
+        }
 
         return intent;
     }
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 2692ab8..b2593f9 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -506,7 +506,7 @@
 
     private PendingIntent createLaunchPendingIntent() {
 
-        final Intent intent = InCallPresenter.getInstance().getInCallIntent();
+        final Intent intent = InCallPresenter.getInstance().getInCallIntent(/*showdialpad=*/false);
 
         // PendingIntent that can be used to launch the InCallActivity.  The
         // system fires off this intent if the user pulls down the windowshade