Merge "Rename Conference#setConnectionElapsedTime to clarity."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4baec2d..ae4ed14 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -153,8 +153,9 @@
     <uses-permission android:name="android.permission.BIND_CARRIER_SERVICES" />
     <!-- BIND_CARRIER_MESSAGING_SERVICE has been deprecated in favor of BIND_CARRIER_SERVICES. -->
     <uses-permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE" />
-    <uses-permission android:name="com.android.permission.BIND_EUICC_SERVICE" />
-    <uses-permission android:name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
+    <uses-permission android:name="android.permission.BIND_EUICC_SERVICE" />
+    <uses-permission android:name="com.android.permission.BIND_TELEPHONY_NETWORK_SERVICE" />
+    <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
@@ -179,6 +180,7 @@
     <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+    <uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
 
     <application android:name="PhoneApp"
             android:persistent="true"
@@ -238,105 +240,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="OutgoingCallBroadcaster"
-                android:enabled="false"
-                android:theme="@style/OutgoingCallBroadcasterTheme"
-                android:permission="android.permission.CALL_PHONE"
-                android:screenOrientation="nosensor"
-                android:configChanges="orientation|screenSize|keyboardHidden"
-                android:excludeFromRecents="true">
-            <!-- CALL action intent filters, for the various ways
-                 of initiating an outgoing call. -->
-            <intent-filter>
-                <action android:name="android.intent.action.CALL" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="tel" />
-            </intent-filter>
-            <intent-filter android:icon="@drawable/ic_launcher_sip_call">
-                <action android:name="android.intent.action.CALL" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="sip" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.CALL" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="voicemail" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.CALL" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/phone" />
-                <data android:mimeType="vnd.android.cursor.item/phone_v2" />
-                <data android:mimeType="vnd.android.cursor.item/person" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias android:name="EmergencyOutgoingCallBroadcaster"
-                android:enabled="false"
-                android:targetActivity="OutgoingCallBroadcaster"
-                android:permission="android.permission.CALL_PRIVILEGED">
-            <intent-filter android:priority="1000">
-                <action android:name="android.intent.action.CALL_EMERGENCY" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="tel" />
-            </intent-filter>
-            <intent-filter android:icon="@drawable/ic_launcher_sip_call"
-                    android:priority="1000">
-                <action android:name="android.intent.action.CALL_EMERGENCY" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="sip" />
-            </intent-filter>
-            <intent-filter android:priority="1000">
-                <action android:name="android.intent.action.CALL_EMERGENCY" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="voicemail" />
-            </intent-filter>
-            <intent-filter android:priority="1000">
-                <action android:name="android.intent.action.CALL_EMERGENCY" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/phone" />
-                <data android:mimeType="vnd.android.cursor.item/person" />
-            </intent-filter>
-        </activity-alias>
-
-        <activity-alias android:name="PrivilegedOutgoingCallBroadcaster"
-                android:enabled="false"
-                android:targetActivity="OutgoingCallBroadcaster"
-                android:screenOrientation="nosensor"
-                android:permission="android.permission.CALL_PRIVILEGED">
-            <intent-filter android:priority="1000">
-                <action android:name="android.intent.action.CALL_PRIVILEGED" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="tel" />
-            </intent-filter>
-            <intent-filter android:icon="@drawable/ic_launcher_sip_call"
-                    android:priority="1000">
-                <action android:name="android.intent.action.CALL_PRIVILEGED" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="sip" />
-            </intent-filter>
-            <intent-filter android:priority="1000">
-                <action android:name="android.intent.action.CALL_PRIVILEGED" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="voicemail" />
-            </intent-filter>
-            <intent-filter android:priority="1000">
-                <action android:name="android.intent.action.CALL_PRIVILEGED" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="vnd.android.cursor.item/phone" />
-                <data android:mimeType="vnd.android.cursor.item/phone_v2" />
-                <data android:mimeType="vnd.android.cursor.item/person" />
-            </intent-filter>
-        </activity-alias>
-
-        <receiver android:name="ProcessOutgoingCallTest" android:exported="false"
-            android:enabled="false">
-            <intent-filter android:priority="1">
-                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </receiver>
-
         <!-- "Mobile network settings" screen, used on both
              non-voice-capable tablets and regular phone devices. -->
         <activity android:name="MobileNetworkSettings"
@@ -383,6 +286,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="GsmUmtsCallBarringOptions"
+                android:label="@string/labelCallBarring"
+                android:configChanges="orientation|screenSize|keyboardHidden"
+                android:theme="@style/CallSettingsWithoutDividerTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="GsmUmtsAdditionalCallOptions"
                 android:label="@string/labelGSMMore"
                 android:configChanges="orientation|screenSize|keyboardHidden"
@@ -572,13 +484,6 @@
 
         <!-- End SIP -->
 
-        <activity android:name="ErrorDialogActivity"
-                android:configChanges="orientation|screenSize|keyboardHidden"
-                android:excludeFromRecents="true"
-                android:launchMode="singleInstance"
-                android:theme="@style/Empty">
-        </activity>
-
         <activity android:name="MMIDialogActivity"
                 android:configChanges="orientation|screenSize|keyboardHidden"
                 android:excludeFromRecents="true"
@@ -620,6 +525,7 @@
                 <action android:name="android.telecom.ConnectionService" />
             </intent-filter>
         </service>
+
         <provider
                 android:name="PhoneSearchIndexablesProvider"
                 android:authorities="com.android.phone"
@@ -655,5 +561,19 @@
         <service
             android:name="com.android.phone.vvm.RemoteVvmTaskManager"
             android:exported="false"/>
+        <service android:name="com.android.internal.telephony.dataconnection.CellularDataService"
+            android:permission="android.permission.BIND_TELEPHONY_DATA_SERVICE" >
+            <intent-filter>
+                <action android:name="android.telephony.data.DataService" />
+            </intent-filter>
+        </service>
+
+        <service android:name="com.android.internal.telephony.CellularNetworkService"
+            android:permission="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" >
+            <intent-filter>
+                <action android:name="android.telephony.NetworkService" />
+            </intent-filter>
+        </service>
+
     </application>
 </manifest>
diff --git a/OWNERS b/OWNERS
index e6fad06..f190411 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,5 +6,6 @@
 rgreenwalt@google.com
 tgunn@google.com
 refuhoo@google.com
-sanketpadawe@google.com
-mpq@google.com
\ No newline at end of file
+mpq@google.com
+jminjie@google.com
+shuoq@google.com
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 61c5f97..fca8acf 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -50,6 +50,21 @@
         </attr>
     </declare-styleable>
 
+    <declare-styleable name="CallBarringEditPreference">
+        <!-- AO: All outgoing, CommandsInterface.CB_FACILITY_BAOC. -->
+        <!-- OI: Outgoing international, CommandsInterface.CB_FACILITY_BAOIC. -->
+        <!-- OX: Outgoing international roaming, CommandsInterface.CB_FACILITY_BAOICxH. -->
+        <!-- AI: All incoming, CommandsInterface.CB_FACILITY_BAIC. -->
+        <!-- IR: Incoming international roaming, CommandsInterface.CB_FACILITY_BAICr. -->
+        <!-- BA: Disable all, CommandsInterface.CB_FACILITY_BA_ALL -->
+        <attr name="facility" format="string" />
+
+        <!-- Message when password is not in use, and call barring is enabled -->
+        <attr name="dialogMessageEnabledNoPwd" format="string" />
+        <!-- Message when password is not in use, and call barring is disabled -->
+        <attr name="dialogMessageDisabledNoPwd" format="string" />
+    </declare-styleable>
+
     <attr name="preferenceBackgroundColor" format="color" />
     <attr name="emergencyButtonBackgroundColor" format="color" />
     <attr name="dialpadTheme" format="reference" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 1b612c9..855fa92 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -241,4 +241,10 @@
 
     <!-- Whether the cellular radio is allowed to be power down when the Bluetooth can provide the data/call capabilities -->
     <bool name="config_allowRadioPowerDownOnBluetooth">false</bool>
+
+    <!-- Whether the device supports the AudioManager Telephony audio device and output onto this
+         device using {@link AudioDeviceInfo#TYPE_TELEPHONY}.
+         When this is true, the Telephony stack is able to add additional audio to the outgoing
+         audio stream which the remote party will be able to hear. -->
+    <bool name="config_support_telephony_audio_device">false</bool>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c79bacc..8c450a0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1599,4 +1599,123 @@
     <string name="clh_callFailed_protocol_Error_unspecified_txt">Protocol error, unspecified</string>
     <!-- In-call screen: call failure reason (Cause Number 127) -->
     <string name="clh_callFailed_interworking_unspecified_txt">Interworking, unspecified</string>
+    <!-- Call settings screen, setting option name -->
+    <string name="labelCallBarring">Call barring</string>
+    <!-- Call barring settings screen, setting summary text when a call barring option is activated -->
+    <string name="sum_call_barring_enabled">On</string>
+    <!-- Call barring settings screen, setting summary text when a call barring option is deactivated -->
+    <string name="sum_call_barring_disabled">Off</string>
+    <!-- Call barring settings screen, setting option name -->
+    <string name="call_barring_baoc">All outgoing</string>
+    <!-- Call barring settings screen, Disable blocking of all outgoing calls -->
+    <string name="call_barring_baoc_enabled">Disable blocking of all outgoing calls?</string>
+    <!-- Call barring settings screen, Block all outgoing calls -->
+    <string name="call_barring_baoc_disabled">Block all outgoing calls?</string>
+    <!-- Call barring settings screen, setting option name -->
+    <string name="call_barring_baoic">Outgoing international</string>
+    <!-- Call barring settings screen, Disable blocking of outgoing international calls -->
+    <string name="call_barring_baoic_enabled">Disable blocking of outgoing international calls?</string>
+    <!-- Call barring settings screen, Block outgoing international calls-->
+    <string name="call_barring_baoic_disabled">Block outgoing international calls?</string>
+    <!-- Call barring settings screen, setting option name -->
+    <string name="call_barring_baoicr">Outgoing international roaming</string>
+    <!-- Call barring settings screen, Disable blocking of outgoing international roaming -->
+    <string name="call_barring_baoicr_enabled">Disable blocking of outgoing international roaming?</string>
+    <!-- Call barring settings screen, Block outgoing international roaming -->
+    <string name="call_barring_baoicr_disabled">Block outgoing international roaming?</string>
+    <!-- Call barring settings screen, setting option name -->
+    <string name="call_barring_baic">All incoming</string>
+    <!-- Call barring settings screen, Disable blocking of all incoming calls -->
+    <string name="call_barring_baic_enabled">Disable blocking of all incoming calls?</string>
+    <!-- Call barring settings screen, Block all incoming calls -->
+    <string name="call_barring_baic_disabled">Block all incoming calls?</string>
+    <!-- Call barring settings screen, setting option name -->
+    <string name="call_barring_baicr">Incoming international roaming</string>
+    <!-- Call barring settings screen, Disable blocking of all incoming international roaming -->
+    <string name="call_barring_baicr_enabled">Disable blocking of all incoming international roaming?</string>
+    <!-- Call barring settings screen, Block incoming international roaming-->
+    <string name="call_barring_baicr_disabled">Block incoming international roaming?</string>
+    <!-- Call barring settings screen, setting option name -->
+    <string name="call_barring_deactivate_all">Deactivate all</string>
+    <!-- Call barring settings screen, setting summary text when BAAll check box is selected -->
+    <string name="call_barring_deactivate_all_description">Deactivate all call barring settings</string>
+    <!-- Call barring settings screen, deactivate all successfully -->
+    <string name="call_barring_deactivate_success">Call barring deactivated</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_change_pwd">Change password</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_change_pwd_description">Change call barring password</string>
+    <!-- Call barring settings screen, not possible to change call barring password -->
+    <string name="call_barring_change_pwd_description_disabled">Cannot change call barring password.</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_pwd_not_match">Passwords do not match</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_right_pwd_number">Enter a password with 4 numbers</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_change_pwd_success">Password changed</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_old_pwd">Old password</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_new_pwd">New password</string>
+    <!-- Call barring settings screen, change password -->
+    <string name="call_barring_confirm_pwd">Confirm password</string>
+    <!-- Call forwarding dialog box, text field label -->
+    <string name="messageCallBarring">Enter password</string>
+    <!-- Call barring settings screen, section heading -->
+    <string name="call_barring_settings">Call barring settings</string>
+    <!-- Call barring settings screen, deactivate all call barring settings -->
+    <string name="call_barring_deactivate_all_no_password">Deactivate all call barring settings?</string>
+    <!-- Message displayed to the user when an outgoing call is deflected.  This means that the
+         party the user is calling has chosen to send the call to another phone number. -->
+    <string name="supp_service_notification_call_deflected">Call deflected.</string>
+    <!-- Message displayed to the user when an outgoing call is forwarded to another number.
+         This happens because the party the user is calling has call forwarding active. -->
+    <string name="supp_service_notification_call_forwarded">Call forwarded.</string>
+    <!-- Message displayed to the user when an outgoing call is waiting.  This happens when the
+         party the user is calling is already in another call. -->
+    <string name="supp_service_notification_call_waiting">Call is waiting.</string>
+    <!-- Message displayed to the user when they have chosen to block their phone number for an
+         outgoing call, but the network has rejected that request. -->
+    <string name="supp_service_clir_suppression_rejected">Number blocking is rejected.</string>
+    <!-- Message displayed to the user to inform them that the call is to or from a number which is
+         part of a closed user group.  A closed user group is a network feature which restricts
+         calls on a device to members of the closed user group. -->
+    <string name="supp_service_closed_user_group_call">Closed user group call.</string>
+    <!-- Message displayed to the user when incoming call barring is active.  This means that the
+         user has enabled the network feature which prevents all incoming calls. -->
+    <string name="supp_service_incoming_calls_barred">Incoming calls barred.</string>
+    <!-- Message displayed to the user when outgoing call barring is active.  This means that the
+         user has enabled the network feature which prevents all outgoing calls. -->
+    <string name="supp_service_outgoing_calls_barred">Outgoing calls barred.</string>
+    <!-- Message displayed to the user to indicate that call forwarding is active. -->
+    <string name="supp_service_call_forwarding_active">Call forwarding active.</string>
+    <!-- Message displayed to the user when they receive multiple incoming calls at the same time
+         and one of them is forwarded to the network.  Phones can't handle multiple incoming calls
+         so the network will typically forward one of the calls to voicemail or another number
+         defined by the user. -->
+    <string name="supp_service_additional_call_forwarded">Additional call forwarded.</string>
+    <!-- Message displayed to the user to indicate that a call has been successfully transferred
+         to another phone number. -->
+    <string name="supp_service_additional_ect_connected">Explicit call transfer complete.</string>
+    <!-- Message displayed to the user to indicate that the call is in the process of being
+         transferred to another phone number.-->
+    <string name="supp_service_additional_ect_connecting">Explicit call transfer in progress.</string>
+    <!-- Message displayed to the user to indicate that the remote party has put the user
+         on hold. -->
+    <string name="supp_service_call_on_hold">Call on hold.</string>
+    <!-- Message displayed to the user to indicate that the remote party has taken the user
+         off hold. -->
+    <string name="supp_service_call_resumed">Call resumed.</string>
+    <!-- Message displayed to the user to indicate that an incoming call was deflected from another
+         number.  This means that the call originated as a result of the original caller choosing
+         to forward the call to the current user rather than answering it themselves. -->
+    <string name="supp_service_deflected_call">Call was deflected.</string>
+    <!-- Message displayed to the user to indicate that an incoming call was forwarded from another
+         number. -->
+    <string name="supp_service_forwarded_call">Forwarded call.</string>
+    <!-- Message displayed to the user to indicate that they are joining a conference call. -->
+    <string name="supp_service_conference_call">Joining conference call.</string>
+    <!-- Message displayed to the user to indicate that a held call has been released /
+         disconnected. -->
+    <string name="supp_service_held_call_released">Held call has been released.</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f8cd9ec..2b893e5 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -304,4 +304,7 @@
         <item name="android:backgroundDimEnabled">false</item>
     </style>
 
+    <style name="CallSettingsWithoutDividerTheme" parent="SettingsLight">
+        <item name="android:listDivider">@null</item>
+    </style>
 </resources>
diff --git a/res/xml/callbarring_options.xml b/res/xml/callbarring_options.xml
new file mode 100644
index 0000000..6f2c48a
--- /dev/null
+++ b/res/xml/callbarring_options.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+     xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+     android:title="@string/call_barring_settings">
+
+    <!-- Note for all com.android.phone.EditPinPreference objects
+
+         The last several attributes are for use with the EditText field
+         in the dialog. These attributes are forwarded to that field
+         when the edittext is created. The attributes include:
+           1. android:singleLine
+           2. android:autoText -->
+
+    <!-- All outgoing -->
+    <com.android.phone.CallBarringEditPreference
+        android:key="button_baoc_key"
+        android:title="@string/call_barring_baoc"
+        android:persistent="false"
+        android:summaryOn="@string/sum_call_barring_enabled"
+        android:summaryOff="@string/sum_call_barring_disabled"
+        android:dialogTitle="@string/call_barring_baoc"
+        phone:dialogMessageEnabledNoPwd="@string/call_barring_baoc_enabled"
+        phone:dialogMessageDisabledNoPwd="@string/call_barring_baoc_disabled"
+        phone:facility="AO"
+        android:singleLine="true"
+        android:autoText="false"/>
+
+    <!-- Outgoing international -->
+    <com.android.phone.CallBarringEditPreference
+        android:key="button_baoic_key"
+        android:title="@string/call_barring_baoic"
+        android:persistent="false"
+        android:summaryOn="@string/sum_call_barring_enabled"
+        android:summaryOff="@string/sum_call_barring_disabled"
+        android:dialogTitle="@string/call_barring_baoic"
+        phone:dialogMessageEnabledNoPwd="@string/call_barring_baoic_enabled"
+        phone:dialogMessageDisabledNoPwd="@string/call_barring_baoic_disabled"
+        phone:facility="OI"
+        android:dependency="button_baoc_key"
+        android:singleLine="true"
+        android:autoText="false"/>
+
+    <!-- Outgoing international roaming -->
+    <com.android.phone.CallBarringEditPreference
+        android:key="button_baoicxh_key"
+        android:title="@string/call_barring_baoicr"
+        android:persistent="false"
+        android:summaryOn="@string/sum_call_barring_enabled"
+        android:summaryOff="@string/sum_call_barring_disabled"
+        android:dialogTitle="@string/call_barring_baoicr"
+        phone:dialogMessageEnabledNoPwd="@string/call_barring_baoicr_enabled"
+        phone:dialogMessageDisabledNoPwd="@string/call_barring_baoicr_disabled"
+        phone:facility="OX"
+        android:dependency="button_baoc_key"
+        android:singleLine="true"
+        android:autoText="false"/>
+
+    <!-- All incoming -->
+    <com.android.phone.CallBarringEditPreference
+        android:key="button_baic_key"
+        android:title="@string/call_barring_baic"
+        android:persistent="false"
+        android:summaryOn="@string/sum_call_barring_enabled"
+        android:summaryOff="@string/sum_call_barring_disabled"
+        android:dialogTitle="@string/call_barring_baic"
+        phone:dialogMessageEnabledNoPwd="@string/call_barring_baic_enabled"
+        phone:dialogMessageDisabledNoPwd="@string/call_barring_baic_disabled"
+        phone:facility="AI"
+        android:singleLine="true"
+        android:autoText="false"/>
+
+    <!-- Incoming international roaming -->
+    <com.android.phone.CallBarringEditPreference
+        android:key="button_baicr_key"
+        android:title="@string/call_barring_baicr"
+        android:persistent="false"
+        android:summaryOn="@string/sum_call_barring_enabled"
+        android:summaryOff="@string/sum_call_barring_disabled"
+        android:dialogTitle="@string/call_barring_baicr"
+        phone:dialogMessageEnabledNoPwd="@string/call_barring_baicr_enabled"
+        phone:dialogMessageDisabledNoPwd="@string/call_barring_baicr_disabled"
+        phone:facility="IR"
+        android:dependency="button_baic_key"
+        android:singleLine="true"
+        android:autoText="false"/>
+
+    <!-- Disable all -->
+    <com.android.phone.CallBarringDeselectAllPreference
+        android:key="button_ba_all_key"
+        android:title="@string/call_barring_deactivate_all"
+        android:persistent="false"
+        android:dialogTitle="@string/call_barring_deactivate_all"
+        android:summary="@string/call_barring_deactivate_all_description"/>
+
+    <!-- Change password -->
+    <com.android.phone.settings.fdn.EditPinPreference
+        android:key="button_change_pw_key"
+        android:title="@string/call_barring_change_pwd"
+        android:dialogTitle="@string/call_barring_change_pwd"
+        android:summary="@string/call_barring_change_pwd_description"
+        android:persistent="false"/>
+</PreferenceScreen>
diff --git a/res/xml/gsm_umts_call_options.xml b/res/xml/gsm_umts_call_options.xml
index 5f3dfe4..774aec8 100644
--- a/res/xml/gsm_umts_call_options.xml
+++ b/res/xml/gsm_umts_call_options.xml
@@ -10,6 +10,11 @@
         android:persistent="false" />
 
     <PreferenceScreen
+         android:key="call_barring_key"
+         android:title="@string/labelCallBarring"
+         android:persistent="false" />
+
+    <PreferenceScreen
         android:key="additional_gsm_call_settings_key"
         android:title="@string/additional_gsm_call_settings"
         android:persistent="false" />
diff --git a/src/com/android/phone/CLIRListPreference.java b/src/com/android/phone/CLIRListPreference.java
index 939caf0..f5d14b8 100644
--- a/src/com/android/phone/CLIRListPreference.java
+++ b/src/com/android/phone/CLIRListPreference.java
@@ -52,6 +52,7 @@
         mPhone = phone;
         mTcpListener = listener;
         if (!skipReading) {
+            Log.i(LOG_TAG, "init: requesting CLIR");
             mPhone.getOutgoingCallerIdDisplay(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CLIR,
                     MyHandler.MESSAGE_GET_CLIR, MyHandler.MESSAGE_GET_CLIR));
             if (mTcpListener != null) {
@@ -135,20 +136,19 @@
             }
             clirArray = null;
             if (ar.exception != null) {
-                if (DBG) Log.d(LOG_TAG, "handleGetCLIRResponse: ar.exception="+ar.exception);
+                Log.i(LOG_TAG, "handleGetCLIRResponse: ar.exception=" + ar.exception);
                 mTcpListener.onException(CLIRListPreference.this, (CommandException) ar.exception);
             } else if (ar.userObj instanceof Throwable) {
+                Log.i(LOG_TAG, "handleGetCLIRResponse: ar.throwable=" + ar.userObj);
                 mTcpListener.onError(CLIRListPreference.this, RESPONSE_ERROR);
             } else {
                 int clirArray[] = (int[]) ar.result;
                 if (clirArray.length != 2) {
                     mTcpListener.onError(CLIRListPreference.this, RESPONSE_ERROR);
                 } else {
-                    if (DBG) {
-                        Log.d(LOG_TAG, "handleGetCLIRResponse: CLIR successfully queried,"
+                    Log.i(LOG_TAG, "handleGetCLIRResponse: CLIR successfully queried,"
                                 + " clirArray[0]=" + clirArray[0]
                                 + ", clirArray[1]=" + clirArray[1]);
-                    }
                     handleGetCLIRResult(clirArray);
                 }
             }
diff --git a/src/com/android/phone/CallBarringDeselectAllPreference.java b/src/com/android/phone/CallBarringDeselectAllPreference.java
new file mode 100644
index 0000000..153bc0c
--- /dev/null
+++ b/src/com/android/phone/CallBarringDeselectAllPreference.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.telephony.ServiceState;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.settings.fdn.EditPinPreference;
+
+/**
+ * This preference represents the status of disable all barring option.
+ */
+public class CallBarringDeselectAllPreference extends EditPinPreference {
+    private static final String LOG_TAG = "CallBarringDeselectAllPreference";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    private boolean mShowPassword;
+    private Phone mPhone;
+
+    /**
+     * CallBarringDeselectAllPreference constructor.
+     *
+     * @param context The context of view.
+     * @param attrs The attributes of the XML tag that is inflating EditTextPreference.
+     */
+    public CallBarringDeselectAllPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void showDialog(Bundle state) {
+        // Finds out if the password field should be shown or not.
+        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+        mShowPassword = !(imsPhone != null
+                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                        || imsPhone.isUtEnabled()));
+
+        // Selects dialog message depending on if the password field is shown or not.
+        setDialogMessage(getContext().getString(mShowPassword
+                ? R.string.messageCallBarring : R.string.call_barring_deactivate_all_no_password));
+
+        if (DBG) {
+            Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword);
+        }
+
+        super.showDialog(state);
+    }
+
+    void init(Phone phone) {
+        if (DBG) {
+            Log.d(LOG_TAG, "init: phoneId = " + phone.getPhoneId());
+        }
+        mPhone = phone;
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+
+        final EditText editText = (EditText) view.findViewById(android.R.id.edit);
+        if (editText != null) {
+            // Hide the input-text-line if the password is not shown.
+            editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    @Override
+    protected boolean needInputMethod() {
+        // Input method should only be displayed if the password-field is shown.
+        return mShowPassword;
+    }
+
+    /**
+     * Returns whether the password field is shown.
+     */
+    boolean isPasswordShown() {
+        return mShowPassword;
+    }
+}
diff --git a/src/com/android/phone/CallBarringEditPreference.java b/src/com/android/phone/CallBarringEditPreference.java
new file mode 100644
index 0000000..72b3ea5
--- /dev/null
+++ b/src/com/android/phone/CallBarringEditPreference.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.ServiceState;
+import android.text.method.DigitsKeyListener;
+import android.text.method.PasswordTransformationMethod;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.settings.fdn.EditPinPreference;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This preference represents the status of call barring options, enabling/disabling
+ * the call barring option will prompt the user for the current password.
+ */
+public class CallBarringEditPreference extends EditPinPreference {
+    private static final String LOG_TAG = "CallBarringEditPreference";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    private String mFacility;
+    boolean mIsActivated = false;
+    private CharSequence mEnableText;
+    private CharSequence mDisableText;
+    private CharSequence mSummaryOn;
+    private CharSequence mSummaryOff;
+    private CharSequence mDialogMessageEnabled;
+    private CharSequence mDialogMessageDisabled;
+    private int mButtonClicked;
+    private boolean mShowPassword;
+    private final MyHandler mHandler = new MyHandler(this);
+    private Phone mPhone;
+    private TimeConsumingPreferenceListener mTcpListener;
+
+    private static final int PW_LENGTH = 4;
+
+    /**
+     * CallBarringEditPreference constructor.
+     *
+     * @param context The context of view.
+     * @param attrs The attributes of the XML tag that is inflating EditTextPreference.
+     */
+    public CallBarringEditPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        // Get the summary settings, use CheckBoxPreference as the standard.
+        TypedArray typedArray = context.obtainStyledAttributes(attrs,
+                android.R.styleable.CheckBoxPreference, 0, 0);
+        mSummaryOn = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOn);
+        mSummaryOff = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOff);
+        mDisableText = context.getText(R.string.disable);
+        mEnableText = context.getText(R.string.enable);
+        typedArray.recycle();
+
+        // Get default phone
+        mPhone = PhoneFactory.getDefaultPhone();
+
+        typedArray = context.obtainStyledAttributes(attrs,
+                R.styleable.CallBarringEditPreference, 0, R.style.EditPhoneNumberPreference);
+        mFacility = typedArray.getString(R.styleable.CallBarringEditPreference_facility);
+        mDialogMessageEnabled = typedArray.getString(
+                R.styleable.CallBarringEditPreference_dialogMessageEnabledNoPwd);
+        mDialogMessageDisabled = typedArray.getString(
+                R.styleable.CallBarringEditPreference_dialogMessageDisabledNoPwd);
+        typedArray.recycle();
+    }
+
+    /**
+     * CallBarringEditPreference constructor.
+     *
+     * @param context The context of view.
+     */
+    public CallBarringEditPreference(Context context) {
+        this(context, null);
+    }
+
+    void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
+        if (DBG) {
+            Log.d(LOG_TAG, "init: phone id = " + phone.getPhoneId());
+        }
+        mPhone = phone;
+
+        mTcpListener = listener;
+        if (!skipReading) {
+            // Query call barring status
+            mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
+                    MyHandler.MESSAGE_GET_CALL_BARRING), 0);
+            if (mTcpListener != null) {
+                mTcpListener.onStarted(this, true);
+            }
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        super.onClick(dialog, which);
+        mButtonClicked = which;
+    }
+
+    @Override
+    protected boolean needInputMethod() {
+        // Input method should only be displayed if the password-field is shown.
+        return mShowPassword;
+    }
+
+    void setInputMethodNeeded(boolean needed) {
+        mShowPassword = needed;
+    }
+
+    @Override
+    protected void showDialog(Bundle state) {
+        setShowPassword();
+        if (mShowPassword) {
+            setDialogMessage(getContext().getString(R.string.messageCallBarring));
+        } else {
+            setDialogMessage(mIsActivated ? mDialogMessageEnabled : mDialogMessageDisabled);
+        }
+
+        if (DBG) {
+            Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword
+                    + ", mIsActivated: " + mIsActivated);
+        }
+
+        super.showDialog(state);
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+
+        // Sync the summary view
+        TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
+        if (summaryView != null) {
+            CharSequence sum;
+            int vis;
+
+            // Set summary depending upon mode
+            if (mIsActivated) {
+                sum = (mSummaryOn == null) ? getSummary() : mSummaryOn;
+            } else {
+                sum = (mSummaryOff == null) ? getSummary() : mSummaryOff;
+            }
+
+            if (sum != null) {
+                summaryView.setText(sum);
+                vis = View.VISIBLE;
+            } else {
+                vis = View.GONE;
+            }
+
+            if (vis != summaryView.getVisibility()) {
+                summaryView.setVisibility(vis);
+            }
+        }
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+        builder.setPositiveButton(null, null);
+        builder.setNeutralButton(mIsActivated ? mDisableText : mEnableText, this);
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+        // Default the button clicked to be the cancel button.
+        mButtonClicked = DialogInterface.BUTTON_NEGATIVE;
+
+        final EditText editText = (EditText) view.findViewById(android.R.id.edit);
+        if (editText != null) {
+            editText.setSingleLine(true);
+            editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+            editText.setKeyListener(DigitsKeyListener.getInstance());
+
+            // Hide the input-text-line if the password is not shown.
+            editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        super.onDialogClosed(positiveResult);
+        if (DBG) {
+            Log.d(LOG_TAG, "onDialogClosed: mButtonClicked=" + mButtonClicked + ", positiveResult="
+                    + positiveResult);
+        }
+        if (mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+            String password = null;
+            if (mShowPassword) {
+                password = getEditText().getText().toString();
+
+                // Check if the password is valid.
+                if (password == null || password.length() != PW_LENGTH) {
+                    Toast.makeText(getContext(),
+                            getContext().getString(R.string.call_barring_right_pwd_number),
+                            Toast.LENGTH_SHORT).show();
+                    return;
+                }
+            }
+
+            if (DBG) {
+                Log.d(LOG_TAG, "onDialogClosed: password=" + password);
+            }
+            // Send set call barring message to RIL layer.
+            mPhone.setCallBarring(mFacility, !mIsActivated, password,
+                    mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING), 0);
+            if (mTcpListener != null) {
+                mTcpListener.onStarted(this, false);
+            }
+        }
+    }
+
+    void handleCallBarringResult(boolean status) {
+        mIsActivated = status;
+        if (DBG) {
+            Log.d(LOG_TAG, "handleCallBarringResult: mIsActivated=" + mIsActivated);
+        }
+    }
+
+    void updateSummaryText() {
+        notifyChanged();
+        notifyDependencyChange(shouldDisableDependents());
+    }
+
+    private void setShowPassword() {
+        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+        mShowPassword = !(imsPhone != null
+                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                        || imsPhone.isUtEnabled()));
+    }
+
+    @Override
+    public boolean shouldDisableDependents() {
+        return mIsActivated;
+    }
+
+    // Message protocol:
+    // what: get vs. set
+    // arg1: action -- register vs. disable
+    // arg2: get vs. set for the preceding request
+    private static class MyHandler extends Handler {
+        private static final int MESSAGE_GET_CALL_BARRING = 0;
+        private static final int MESSAGE_SET_CALL_BARRING = 1;
+
+        private final WeakReference<CallBarringEditPreference> mCallBarringEditPreference;
+
+        private MyHandler(CallBarringEditPreference callBarringEditPreference) {
+            mCallBarringEditPreference =
+                    new WeakReference<CallBarringEditPreference>(callBarringEditPreference);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_GET_CALL_BARRING:
+                    handleGetCallBarringResponse(msg);
+                    break;
+                case MESSAGE_SET_CALL_BARRING:
+                    handleSetCallBarringResponse(msg);
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        // Handle the response message for query CB status.
+        private void handleGetCallBarringResponse(Message msg) {
+            final CallBarringEditPreference pref = mCallBarringEditPreference.get();
+            if (pref == null) {
+                return;
+            }
+
+            if (DBG) {
+                Log.d(LOG_TAG, "handleGetCallBarringResponse: done");
+            }
+
+            AsyncResult ar = (AsyncResult) msg.obj;
+
+            if (msg.arg2 == MESSAGE_SET_CALL_BARRING) {
+                pref.mTcpListener.onFinished(pref, false);
+            } else {
+                pref.mTcpListener.onFinished(pref, true);
+                ImsPhone imsPhone = pref.mPhone != null
+                        ? (ImsPhone) pref.mPhone.getImsPhone() : null;
+                if (!pref.mShowPassword && (imsPhone == null || !imsPhone.isUtEnabled())) {
+                    // Re-enable password when rejected from NW and modem would perform CSFB
+                    pref.mShowPassword = true;
+                    if (DBG) {
+                        Log.d(LOG_TAG,
+                                "handleGetCallBarringResponse: mShowPassword changed for CSFB");
+                    }
+                }
+            }
+
+            // Unsuccessful query for call barring.
+            if (ar.exception != null) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallBarringResponse: ar.exception=" + ar.exception);
+                }
+                pref.mTcpListener.onException(pref, (CommandException) ar.exception);
+            } else {
+                if (ar.userObj instanceof Throwable) {
+                    pref.mTcpListener.onError(pref, RESPONSE_ERROR);
+                }
+                int[] ints = (int[]) ar.result;
+                if (ints.length == 0) {
+                    if (DBG) {
+                        Log.d(LOG_TAG, "handleGetCallBarringResponse: ar.result.length==0");
+                    }
+                    pref.setEnabled(false);
+                    pref.mTcpListener.onError(pref, RESPONSE_ERROR);
+                } else {
+                    pref.handleCallBarringResult(ints[0] != 0);
+                    if (DBG) {
+                        Log.d(LOG_TAG,
+                                "handleGetCallBarringResponse: CB state successfully queried: "
+                                        + ints[0]);
+                    }
+                }
+            }
+            // Update call barring status.
+            pref.updateSummaryText();
+        }
+
+        // Handle the response message for CB settings.
+        private void handleSetCallBarringResponse(Message msg) {
+            final CallBarringEditPreference pref = mCallBarringEditPreference.get();
+            if (pref == null) {
+                return;
+            }
+
+            AsyncResult ar = (AsyncResult) msg.obj;
+
+            if (ar.exception != null || ar.userObj instanceof Throwable) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleSetCallBarringResponse: ar.exception=" + ar.exception);
+                }
+            }
+            if (DBG) {
+                Log.d(LOG_TAG, "handleSetCallBarringResponse: re-get call barring option");
+            }
+            pref.mPhone.getCallBarring(
+                    pref.mFacility,
+                    "",
+                    obtainMessage(MESSAGE_GET_CALL_BARRING, 0, MESSAGE_SET_CALL_BARRING,
+                            ar.exception),
+                    0);
+        }
+    }
+}
diff --git a/src/com/android/phone/CallController.java b/src/com/android/phone/CallController.java
deleted file mode 100644
index a5d340c..0000000
--- a/src/com/android/phone/CallController.java
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.phone.CallGatewayManager.RawGatewayInfo;
-import com.android.phone.Constants.CallStatusCode;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemProperties;
-import android.provider.CallLog.Calls;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.ServiceState;
-import android.util.Log;
-
-/**
- * Phone app module in charge of "call control".
- *
- * This is a singleton object which acts as the interface to the telephony layer
- * (and other parts of the Android framework) for all user-initiated telephony
- * functionality, like making outgoing calls.
- *
- * This functionality includes things like:
- *   - actually running the placeCall() method and handling errors or retries
- *   - running the whole "emergency call in airplane mode" sequence
- *   - running the state machine of MMI sequences
- *   - restoring/resetting mute and speaker state when a new call starts
- *   - updating the prox sensor wake lock state
- *   - resolving what the voicemail: intent should mean (and making the call)
- *
- * The single CallController instance stays around forever; it's not tied
- * to the lifecycle of any particular Activity (like the InCallScreen).
- * There's also no implementation of onscreen UI here (that's all in InCallScreen).
- *
- * Note that this class does not handle asynchronous events from the telephony
- * layer, like reacting to an incoming call; see CallNotifier for that.  This
- * class purely handles actions initiated by the user, like outgoing calls.
- */
-public class CallController extends Handler {
-    private static final String TAG = "CallController";
-    private static final boolean DBG =
-            (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
-    // Do not check in with VDBG = true, since that may write PII to the system log.
-    private static final boolean VDBG = false;
-
-    /** The singleton CallController instance. */
-    private static CallController sInstance;
-
-    final private PhoneGlobals mApp;
-    final private CallManager mCM;
-    final private CallLogger mCallLogger;
-    final private CallGatewayManager mCallGatewayManager;
-
-    /** Helper object for emergency calls in some rare use cases.  Created lazily. */
-    private EmergencyCallHelper mEmergencyCallHelper;
-
-
-    //
-    // Message codes; see handleMessage().
-    //
-
-    private static final int THREEWAY_CALLERINFO_DISPLAY_DONE = 1;
-
-
-    //
-    // Misc constants.
-    //
-
-    // Amount of time the UI should display "Dialing" when initiating a CDMA
-    // 3way call.  (See comments on the THRWAY_ACTIVE case in
-    // placeCallInternal() for more info.)
-    private static final int THREEWAY_CALLERINFO_DISPLAY_TIME = 3000; // msec
-
-
-    /**
-     * Initialize the singleton CallController instance.
-     *
-     * This is only done once, at startup, from PhoneApp.onCreate().
-     * From then on, the CallController instance is available via the
-     * PhoneApp's public "callController" field, which is why there's no
-     * getInstance() method here.
-     */
-    /* package */ static CallController init(PhoneGlobals app, CallLogger callLogger,
-            CallGatewayManager callGatewayManager) {
-        synchronized (CallController.class) {
-            if (sInstance == null) {
-                sInstance = new CallController(app, callLogger, callGatewayManager);
-            } else {
-                Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
-            }
-            return sInstance;
-        }
-    }
-
-    /**
-     * Private constructor (this is a singleton).
-     * @see init()
-     */
-    private CallController(PhoneGlobals app, CallLogger callLogger,
-            CallGatewayManager callGatewayManager) {
-        if (DBG) log("CallController constructor: app = " + app);
-        mApp = app;
-        mCM = app.mCM;
-        mCallLogger = callLogger;
-        mCallGatewayManager = callGatewayManager;
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        if (VDBG) log("handleMessage: " + msg);
-        switch (msg.what) {
-
-            case THREEWAY_CALLERINFO_DISPLAY_DONE:
-                if (DBG) log("THREEWAY_CALLERINFO_DISPLAY_DONE...");
-
-                if (mApp.cdmaPhoneCallState.getCurrentCallState()
-                    == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
-                    // Reset the mThreeWayCallOrigStateDialing state
-                    mApp.cdmaPhoneCallState.setThreeWayCallOrigState(false);
-
-                    // TODO: Remove this code.
-                    //mApp.getCallModeler().setCdmaOutgoing3WayCall(null);
-                }
-                break;
-
-            default:
-                Log.wtf(TAG, "handleMessage: unexpected code: " + msg);
-                break;
-        }
-    }
-
-    //
-    // Outgoing call sequence
-    //
-
-    /**
-     * Initiate an outgoing call.
-     *
-     * Here's the most typical outgoing call sequence:
-     *
-     *  (1) OutgoingCallBroadcaster receives a CALL intent and sends the
-     *      NEW_OUTGOING_CALL broadcast
-     *
-     *  (2) The broadcast finally reaches OutgoingCallReceiver, which stashes
-     *      away a copy of the original CALL intent and launches
-     *      SipCallOptionHandler
-     *
-     *  (3) SipCallOptionHandler decides whether this is a PSTN or SIP call (and
-     *      in some cases brings up a dialog to let the user choose), and
-     *      ultimately calls CallController.placeCall() (from the
-     *      setResultAndFinish() method) with the stashed-away intent from step
-     *      (2) as the "intent" parameter.
-     *
-     *  (4) Here in CallController.placeCall() we read the phone number or SIP
-     *      address out of the intent and actually initiate the call, and
-     *      simultaneously launch the InCallScreen to display the in-call UI.
-     *
-     *  (5) We handle various errors by directing the InCallScreen to
-     *      display error messages or dialogs (via the InCallUiState
-     *      "pending call status code" flag), and in some cases we also
-     *      sometimes continue working in the background to resolve the
-     *      problem (like in the case of an emergency call while in
-     *      airplane mode).  Any time that some onscreen indication to the
-     *      user needs to change, we update the "status dialog" info in
-     *      the inCallUiState and (re)launch the InCallScreen to make sure
-     *      it's visible.
-     */
-    public void placeCall(Intent intent) {
-        log("placeCall()...  intent = " + intent);
-        if (VDBG) log("                extras = " + intent.getExtras());
-
-        // TODO: Do we need to hold a wake lock while this method runs?
-        //       Or did we already acquire one somewhere earlier
-        //       in this sequence (like when we first received the CALL intent?)
-
-        if (intent == null) {
-            Log.wtf(TAG, "placeCall: called with null intent");
-            throw new IllegalArgumentException("placeCall: called with null intent");
-        }
-
-        String action = intent.getAction();
-        Uri uri = intent.getData();
-        if (uri == null) {
-            Log.wtf(TAG, "placeCall: intent had no data");
-            throw new IllegalArgumentException("placeCall: intent had no data");
-        }
-
-        String scheme = uri.getScheme();
-        String number = PhoneNumberUtils.getNumberFromIntent(intent, mApp);
-        if (VDBG) {
-            log("- action: " + action);
-            log("- uri: " + uri);
-            log("- scheme: " + scheme);
-            log("- number: " + number);
-        }
-
-        // This method should only be used with the various flavors of CALL
-        // intents.  (It doesn't make sense for any other action to trigger an
-        // outgoing call!)
-        if (!(Intent.ACTION_CALL.equals(action)
-              || Intent.ACTION_CALL_EMERGENCY.equals(action)
-              || Intent.ACTION_CALL_PRIVILEGED.equals(action))) {
-            Log.wtf(TAG, "placeCall: unexpected intent action " + action);
-            throw new IllegalArgumentException("Unexpected action: " + action);
-        }
-
-        CallStatusCode status = placeCallInternal(intent);
-
-        switch (status) {
-            // Call was placed successfully:
-            case SUCCESS:
-            case EXITED_ECM:
-                if (DBG) log("==> placeCall(): success from placeCallInternal(): " + status);
-                break;
-
-            default:
-                // Any other status code is a failure.
-                log("==> placeCall(): failure code from placeCallInternal(): " + status);
-                // Handle the various error conditions that can occur when
-                // initiating an outgoing call, typically by directing the
-                // InCallScreen to display a diagnostic message (via the
-                // "pending call status code" flag.)
-                handleOutgoingCallError(status);
-                break;
-        }
-
-        // Finally, regardless of whether we successfully initiated the
-        // outgoing call or not, force the InCallScreen to come to the
-        // foreground.
-        //
-        // (For successful calls the the user will just see the normal
-        // in-call UI.  Or if there was an error, the InCallScreen will
-        // notice the InCallUiState pending call status code flag and display an
-        // error indication instead.)
-    }
-
-    /**
-     * Actually make a call to whomever the intent tells us to.
-     *
-     * Note that there's no need to explicitly update (or refresh) the
-     * in-call UI at any point in this method, since a fresh InCallScreen
-     * instance will be launched automatically after we return (see
-     * placeCall() above.)
-     *
-     * @param intent the CALL intent describing whom to call
-     * @return CallStatusCode.SUCCESS if we successfully initiated an
-     *    outgoing call.  If there was some kind of failure, return one of
-     *    the other CallStatusCode codes indicating what went wrong.
-     */
-    private CallStatusCode placeCallInternal(Intent intent) {
-        if (DBG) log("placeCallInternal()...  intent = " + intent);
-
-        // TODO: This method is too long.  Break it down into more
-        // manageable chunks.
-
-        final Uri uri = intent.getData();
-        final String scheme = (uri != null) ? uri.getScheme() : null;
-        String number;
-        Phone phone = null;
-
-        // Check the current ServiceState to make sure it's OK
-        // to even try making a call.
-        CallStatusCode okToCallStatus = checkIfOkToInitiateOutgoingCall(
-                mCM.getServiceState());
-
-        // TODO: Streamline the logic here.  Currently, the code is
-        // unchanged from its original form in InCallScreen.java.  But we
-        // should fix a couple of things:
-        // - Don't call checkIfOkToInitiateOutgoingCall() more than once
-        // - Wrap the try/catch for VoiceMailNumberMissingException
-        //   around *only* the call that can throw that exception.
-
-        try {
-            number = PhoneUtils.getInitialNumber(intent);
-            if (VDBG) log("- actual number to dial: '" + number + "'");
-
-            // find the phone first
-            // TODO Need a way to determine which phone to place the call
-            // It could be determined by SIP setting, i.e. always,
-            // or by number, i.e. for international,
-            // or by user selection, i.e., dialog query,
-            // or any of combinations
-            String sipPhoneUri = intent.getStringExtra(
-                    OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI);
-            ComponentName thirdPartyCallComponent = (ComponentName) intent.getParcelableExtra(
-                    OutgoingCallBroadcaster.EXTRA_THIRD_PARTY_CALL_COMPONENT);
-            phone = PhoneUtils.pickPhoneBasedOnNumber(mCM, scheme, number, sipPhoneUri,
-                    thirdPartyCallComponent);
-            if (VDBG) log("- got Phone instance: " + phone + ", class = " + phone.getClass());
-
-            // update okToCallStatus based on new phone
-            okToCallStatus = checkIfOkToInitiateOutgoingCall(
-                    phone.getServiceState().getState());
-
-        } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
-            // If the call status is NOT in an acceptable state, it
-            // may effect the way the voicemail number is being
-            // retrieved.  Mask the VoiceMailNumberMissingException
-            // with the underlying issue of the phone state.
-            if (okToCallStatus != CallStatusCode.SUCCESS) {
-                if (DBG) log("Voicemail number not reachable in current SIM card state.");
-                return okToCallStatus;
-            }
-            if (DBG) log("VoiceMailNumberMissingException from getInitialNumber()");
-            return CallStatusCode.VOICEMAIL_NUMBER_MISSING;
-        }
-
-        if (number == null) {
-            Log.w(TAG, "placeCall: couldn't get a phone number from Intent " + intent);
-            return CallStatusCode.NO_PHONE_NUMBER_SUPPLIED;
-        }
-
-
-        // Sanity-check that ACTION_CALL_EMERGENCY is used if and only if
-        // this is a call to an emergency number
-        // (This is just a sanity-check; this policy *should* really be
-        // enforced in OutgoingCallBroadcaster.onCreate(), which is the
-        // main entry point for the CALL and CALL_* intents.)
-        boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mApp, number);
-        boolean isPotentialEmergencyNumber =
-                PhoneNumberUtils.isPotentialLocalEmergencyNumber(mApp, number);
-        boolean isEmergencyIntent = Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction());
-
-        if (isPotentialEmergencyNumber && !isEmergencyIntent) {
-            Log.e(TAG, "Non-CALL_EMERGENCY Intent " + intent
-                    + " attempted to call potential emergency number " + number
-                    + ".");
-            return CallStatusCode.CALL_FAILED;
-        } else if (!isPotentialEmergencyNumber && isEmergencyIntent) {
-            Log.e(TAG, "Received CALL_EMERGENCY Intent " + intent
-                    + " with non-potential-emergency number " + number
-                    + " -- failing call.");
-            return CallStatusCode.CALL_FAILED;
-        }
-
-        // If we're trying to call an emergency number, then it's OK to
-        // proceed in certain states where we'd otherwise bring up
-        // an error dialog:
-        // - If we're in EMERGENCY_ONLY mode, then (obviously) you're allowed
-        //   to dial emergency numbers.
-        // - If we're OUT_OF_SERVICE, we still attempt to make a call,
-        //   since the radio will register to any available network.
-
-        if (isEmergencyNumber
-            && ((okToCallStatus == CallStatusCode.EMERGENCY_ONLY)
-                || (okToCallStatus == CallStatusCode.OUT_OF_SERVICE))) {
-            if (DBG) log("placeCall: Emergency number detected with status = " + okToCallStatus);
-            okToCallStatus = CallStatusCode.SUCCESS;
-            if (DBG) log("==> UPDATING status to: " + okToCallStatus);
-        }
-
-        if (okToCallStatus != CallStatusCode.SUCCESS) {
-            // If this is an emergency call, launch the EmergencyCallHelperService
-            // to turn on the radio and retry the call.
-            if (isEmergencyNumber && (okToCallStatus == CallStatusCode.POWER_OFF)) {
-                Log.i(TAG, "placeCall: Trying to make emergency call while POWER_OFF!");
-
-                // If needed, lazily instantiate an EmergencyCallHelper instance.
-                synchronized (this) {
-                    if (mEmergencyCallHelper == null) {
-                        mEmergencyCallHelper = new EmergencyCallHelper(this);
-                    }
-                }
-
-                // ...and kick off the "emergency call from airplane mode" sequence.
-                mEmergencyCallHelper.startEmergencyCallFromAirplaneModeSequence(number);
-
-                // Finally, return CallStatusCode.SUCCESS right now so
-                // that the in-call UI will remain visible (in order to
-                // display the progress indication.)
-                // TODO: or maybe it would be more clear to return a whole
-                // new CallStatusCode called "TURNING_ON_RADIO" here.
-                // That way, we'd update inCallUiState.progressIndication from
-                // the handleOutgoingCallError() method, rather than here.
-                return CallStatusCode.SUCCESS;
-            } else {
-                // Otherwise, just return the (non-SUCCESS) status code
-                // back to our caller.
-                if (DBG) log("==> placeCallInternal(): non-success status: " + okToCallStatus);
-
-                // Log failed call.
-                // Note: Normally, many of these values we gather from the Connection object but
-                // since no such object is created for unconnected calls, we have to build them
-                // manually.
-                // TODO: Try to restructure code so that we can handle failure-
-                // condition call logging in a single place (placeCall()) that also has access to
-                // the number we attempted to dial (not placeCall()).
-                mCallLogger.logCall(null /* callerInfo */, number, 0 /* presentation */,
-                        Calls.OUTGOING_TYPE, System.currentTimeMillis(), 0 /* duration */);
-
-                return okToCallStatus;
-            }
-        }
-
-        // We have a valid number, so try to actually place a call:
-        // make sure we pass along the intent's URI which is a
-        // reference to the contact. We may have a provider gateway
-        // phone number to use for the outgoing call.
-        Uri contactUri = intent.getData();
-
-        // If a gateway is used, extract the data here and pass that into placeCall.
-        final RawGatewayInfo rawGatewayInfo = mCallGatewayManager.getRawGatewayInfo(intent, number);
-
-        // Watch out: PhoneUtils.placeCall() returns one of the
-        // CALL_STATUS_* constants, not a CallStatusCode enum value.
-        int callStatus = PhoneUtils.placeCall(mApp,
-                                              phone,
-                                              number,
-                                              contactUri,
-                                              (isEmergencyNumber || isEmergencyIntent),
-                                              rawGatewayInfo,
-                                              mCallGatewayManager);
-
-        switch (callStatus) {
-            case PhoneUtils.CALL_STATUS_DIALED:
-                if (VDBG) log("placeCall: PhoneUtils.placeCall() succeeded for regular call '"
-                             + number + "'.");
-
-
-                // TODO(OTASP): still need more cleanup to simplify the mApp.cdma*State objects:
-                // - Rather than checking inCallUiState.inCallScreenMode, the
-                //   code here could also check for
-                //   app.getCdmaOtaInCallScreenUiState() returning NORMAL.
-                // - But overall, app.inCallUiState.inCallScreenMode and
-                //   app.cdmaOtaInCallScreenUiState.state are redundant.
-                //   Combine them.
-
-                boolean voicemailUriSpecified = scheme != null &&
-                    scheme.equals(PhoneAccount.SCHEME_VOICEMAIL);
-                // Check for an obscure ECM-related scenario: If the phone
-                // is currently in ECM (Emergency callback mode) and we
-                // dial a non-emergency number, that automatically
-                // *cancels* ECM.  So warn the user about it.
-                // (See InCallScreen.showExitingECMDialog() for more info.)
-                boolean exitedEcm = false;
-                if (PhoneUtils.isPhoneInEcm(phone) && !isEmergencyNumber) {
-                    Log.i(TAG, "About to exit ECM because of an outgoing non-emergency call");
-                    exitedEcm = true;  // this will cause us to return EXITED_ECM from this method
-                }
-
-                if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
-                    // Start the timer for 3 Way CallerInfo
-                    if (mApp.cdmaPhoneCallState.getCurrentCallState()
-                            == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
-
-                        // This is a "CDMA 3-way call", which means that you're dialing a
-                        // 2nd outgoing call while a previous call is already in progress.
-                        //
-                        // Due to the limitations of CDMA this call doesn't actually go
-                        // through the DIALING/ALERTING states, so we can't tell for sure
-                        // when (or if) it's actually answered.  But we want to show
-                        // *some* indication of what's going on in the UI, so we "fake it"
-                        // by displaying the "Dialing" state for 3 seconds.
-
-                        // Set the mThreeWayCallOrigStateDialing state to true
-                        mApp.cdmaPhoneCallState.setThreeWayCallOrigState(true);
-
-                        // Schedule the "Dialing" indication to be taken down in 3 seconds:
-                        sendEmptyMessageDelayed(THREEWAY_CALLERINFO_DISPLAY_DONE,
-                                                THREEWAY_CALLERINFO_DISPLAY_TIME);
-                    }
-                }
-
-                // Success!
-                if (exitedEcm) {
-                    return CallStatusCode.EXITED_ECM;
-                } else {
-                    return CallStatusCode.SUCCESS;
-                }
-
-            case PhoneUtils.CALL_STATUS_DIALED_MMI:
-                if (DBG) log("placeCall: specified number was an MMI code: '" + number + "'.");
-                // The passed-in number was an MMI code, not a regular phone number!
-                // This isn't really a failure; the Dialer may have deliberately
-                // fired an ACTION_CALL intent to dial an MMI code, like for a
-                // USSD call.
-                //
-                // Presumably an MMI_INITIATE message will come in shortly
-                // (and we'll bring up the "MMI Started" dialog), or else
-                // an MMI_COMPLETE will come in (which will take us to a
-                // different Activity; see PhoneUtils.displayMMIComplete()).
-                return CallStatusCode.DIALED_MMI;
-
-            case PhoneUtils.CALL_STATUS_FAILED:
-                Log.w(TAG, "placeCall: PhoneUtils.placeCall() FAILED for number '"
-                      + number + "'.");
-                // We couldn't successfully place the call; there was some
-                // failure in the telephony layer.
-
-                // Log failed call.
-                mCallLogger.logCall(null /* callerInfo */, number, 0 /* presentation */,
-                        Calls.OUTGOING_TYPE, System.currentTimeMillis(), 0 /* duration */);
-
-                return CallStatusCode.CALL_FAILED;
-
-            default:
-                Log.wtf(TAG, "placeCall: unknown callStatus " + callStatus
-                        + " from PhoneUtils.placeCall() for number '" + number + "'.");
-                return CallStatusCode.SUCCESS;  // Try to continue anyway...
-        }
-    }
-
-    /**
-     * Checks the current ServiceState to make sure it's OK
-     * to try making an outgoing call to the specified number.
-     *
-     * @return CallStatusCode.SUCCESS if it's OK to try calling the specified
-     *    number.  If not, like if the radio is powered off or we have no
-     *    signal, return one of the other CallStatusCode codes indicating what
-     *    the problem is.
-     */
-    private CallStatusCode checkIfOkToInitiateOutgoingCall(int state) {
-        if (VDBG) log("checkIfOkToInitiateOutgoingCall: ServiceState = " + state);
-
-        switch (state) {
-            case ServiceState.STATE_IN_SERVICE:
-                // Normal operation.  It's OK to make outgoing calls.
-                return CallStatusCode.SUCCESS;
-
-            case ServiceState.STATE_POWER_OFF:
-                // Radio is explictly powered off.
-                return CallStatusCode.POWER_OFF;
-
-            case ServiceState.STATE_EMERGENCY_ONLY:
-                // The phone is registered, but locked. Only emergency
-                // numbers are allowed.
-                // Note that as of Android 2.0 at least, the telephony layer
-                // does not actually use ServiceState.STATE_EMERGENCY_ONLY,
-                // mainly since there's no guarantee that the radio/RIL can
-                // make this distinction.  So in practice the
-                // CallStatusCode.EMERGENCY_ONLY state and the string
-                // "incall_error_emergency_only" are totally unused.
-                return CallStatusCode.EMERGENCY_ONLY;
-
-            case ServiceState.STATE_OUT_OF_SERVICE:
-                // No network connection.
-                return CallStatusCode.OUT_OF_SERVICE;
-
-            default:
-                throw new IllegalStateException("Unexpected ServiceState: " + state);
-        }
-    }
-
-
-
-    /**
-     * Handles the various error conditions that can occur when initiating
-     * an outgoing call.
-     *
-     * Most error conditions are "handled" by simply displaying an error
-     * message to the user.
-     *
-     * @param status one of the CallStatusCode error codes.
-     */
-    private void handleOutgoingCallError(CallStatusCode status) {
-        if (DBG) log("handleOutgoingCallError(): status = " + status);
-        final Intent intent = new Intent(mApp, ErrorDialogActivity.class);
-        int errorMessageId = -1;
-        switch (status) {
-            case SUCCESS:
-                // This case shouldn't happen; you're only supposed to call
-                // handleOutgoingCallError() if there was actually an error!
-                Log.wtf(TAG, "handleOutgoingCallError: SUCCESS isn't an error");
-                break;
-
-            case CALL_FAILED:
-                // We couldn't successfully place the call; there was some
-                // failure in the telephony layer.
-                // TODO: Need UI spec for this failure case; for now just
-                // show a generic error.
-                errorMessageId = R.string.incall_error_call_failed;
-                break;
-            case POWER_OFF:
-                // Radio is explictly powered off, presumably because the
-                // device is in airplane mode.
-                //
-                // TODO: For now this UI is ultra-simple: we simply display
-                // a message telling the user to turn off airplane mode.
-                // But it might be nicer for the dialog to offer the option
-                // to turn the radio on right there (and automatically retry
-                // the call once network registration is complete.)
-                errorMessageId = R.string.incall_error_power_off;
-                break;
-            case EMERGENCY_ONLY:
-                // Only emergency numbers are allowed, but we tried to dial
-                // a non-emergency number.
-                // (This state is currently unused; see comments above.)
-                errorMessageId = R.string.incall_error_emergency_only;
-                break;
-            case OUT_OF_SERVICE:
-                // No network connection.
-                errorMessageId = R.string.incall_error_out_of_service;
-                break;
-            case NO_PHONE_NUMBER_SUPPLIED:
-                // The supplied Intent didn't contain a valid phone number.
-                // (This is rare and should only ever happen with broken
-                // 3rd-party apps.) For now just show a generic error.
-                errorMessageId = R.string.incall_error_no_phone_number_supplied;
-                break;
-
-            case VOICEMAIL_NUMBER_MISSING:
-                // Bring up the "Missing Voicemail Number" dialog, which
-                // will ultimately take us to some other Activity (or else
-                // just bail out of this activity.)
-
-                // Send a request to the InCallScreen to display the
-                // "voicemail missing" dialog when it (the InCallScreen)
-                // comes to the foreground.
-                intent.putExtra(ErrorDialogActivity.SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA, true);
-                break;
-
-            case DIALED_MMI:
-                // Our initial phone number was actually an MMI sequence.
-                // There's no real "error" here, but we do bring up the
-                // a Toast (as requested of the New UI paradigm).
-                //
-                // In-call MMIs do not trigger the normal MMI Initiate
-                // Notifications, so we should notify the user here.
-                // Otherwise, the code in PhoneUtils.java should handle
-                // user notifications in the form of Toasts or Dialogs.
-                //
-                // TODO: Rather than launching a toast from here, it would
-                // be cleaner to just set a pending call status code here,
-                // and then let the InCallScreen display the toast...
-                final Intent mmiIntent = new Intent(mApp, MMIDialogActivity.class);
-                mmiIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                        Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                mApp.startActivity(mmiIntent);
-                return;
-            default:
-                Log.wtf(TAG, "handleOutgoingCallError: unexpected status code " + status);
-                // Show a generic "call failed" error.
-                errorMessageId = R.string.incall_error_call_failed;
-                break;
-        }
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        if (errorMessageId != -1) {
-            intent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
-        }
-        mApp.startActivity(intent);
-    }
-
-    //
-    // Debugging
-    //
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index a3b0892..5a228a8 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -349,7 +349,7 @@
         }
 
         try {
-            if (mImsMgr.getImsServiceStatus() != ImsFeature.STATE_READY) {
+            if (mImsMgr.getImsServiceState() != ImsFeature.STATE_READY) {
                 log("Feature state not ready so remove vt and wfc settings for "
                         + " phone =" + mPhone.getPhoneId());
                 prefSet.removePreference(wifiCallingSettings);
diff --git a/src/com/android/phone/EmergencyCallHelper.java b/src/com/android/phone/EmergencyCallHelper.java
deleted file mode 100644
index 74ce088..0000000
--- a/src/com/android/phone/EmergencyCallHelper.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.DisconnectCause;
-import android.telephony.ServiceState;
-import android.util.Log;
-
-
-/**
- * Helper class for the {@link CallController} that implements special
- * behavior related to emergency calls.  Specifically, this class handles
- * the case of the user trying to dial an emergency number while the radio
- * is off (i.e. the device is in airplane mode), by forcibly turning the
- * radio back on, waiting for it to come up, and then retrying the
- * emergency call.
- *
- * This class is instantiated lazily (the first time the user attempts to
- * make an emergency call from airplane mode) by the the
- * {@link CallController} singleton.
- */
-public class EmergencyCallHelper extends Handler {
-    private static final String TAG = "EmergencyCallHelper";
-    private static final boolean DBG = false;
-
-    // Number of times to retry the call, and time between retry attempts.
-    public static final int MAX_NUM_RETRIES = 6;
-    public static final long TIME_BETWEEN_RETRIES = 5000;  // msec
-
-    // Timeout used with our wake lock (just as a safety valve to make
-    // sure we don't hold it forever).
-    public static final long WAKE_LOCK_TIMEOUT = 5 * 60 * 1000;  // 5 minutes in msec
-
-    // Handler message codes; see handleMessage()
-    private static final int START_SEQUENCE = 1;
-    private static final int SERVICE_STATE_CHANGED = 2;
-    private static final int DISCONNECT = 3;
-    private static final int RETRY_TIMEOUT = 4;
-
-    private CallController mCallController;
-    private PhoneGlobals mApp;
-    private CallManager mCM;
-    private String mNumber;  // The emergency number we're trying to dial
-    private int mNumRetriesSoFar;
-
-    // Wake lock we hold while running the whole sequence
-    private PowerManager.WakeLock mPartialWakeLock;
-
-    public EmergencyCallHelper(CallController callController) {
-        if (DBG) log("EmergencyCallHelper constructor...");
-        mCallController = callController;
-        mApp = PhoneGlobals.getInstance();
-        mCM =  mApp.mCM;
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-        switch (msg.what) {
-            case START_SEQUENCE:
-                startSequenceInternal(msg);
-                break;
-            case SERVICE_STATE_CHANGED:
-                onServiceStateChanged(msg);
-                break;
-            case DISCONNECT:
-                onDisconnect(msg);
-                break;
-            case RETRY_TIMEOUT:
-                onRetryTimeout();
-                break;
-            default:
-                Log.wtf(TAG, "handleMessage: unexpected message: " + msg);
-                break;
-        }
-    }
-
-    /**
-     * Starts the "emergency call from airplane mode" sequence.
-     *
-     * This is the (single) external API of the EmergencyCallHelper class.
-     * This method is called from the CallController placeCall() sequence
-     * if the user dials a valid emergency number, but the radio is
-     * powered-off (presumably due to airplane mode.)
-     *
-     * This method kicks off the following sequence:
-     * - Power on the radio
-     * - Listen for the service state change event telling us the radio has come up
-     * - Then launch the emergency call
-     * - Retry if the call fails with an OUT_OF_SERVICE error
-     * - Retry if we've gone 5 seconds without any response from the radio
-     * - Finally, clean up any leftover state (progress UI, wake locks, etc.)
-     *
-     * This method is safe to call from any thread, since it simply posts
-     * a message to the EmergencyCallHelper's handler (thus ensuring that
-     * the rest of the sequence is entirely serialized, and runs only on
-     * the handler thread.)
-     *
-     * This method does *not* force the in-call UI to come up; our caller
-     * is responsible for doing that (presumably by calling
-     * PhoneApp.displayCallScreen().)
-     */
-    public void startEmergencyCallFromAirplaneModeSequence(String number) {
-        if (DBG) log("startEmergencyCallFromAirplaneModeSequence('" + number + "')...");
-        Message msg = obtainMessage(START_SEQUENCE, number);
-        sendMessage(msg);
-    }
-
-    /**
-     * Actual implementation of startEmergencyCallFromAirplaneModeSequence(),
-     * guaranteed to run on the handler thread.
-     * @see #startEmergencyCallFromAirplaneModeSequence
-     */
-    private void startSequenceInternal(Message msg) {
-        if (DBG) log("startSequenceInternal(): msg = " + msg);
-
-        // First of all, clean up any state (including mPartialWakeLock!)
-        // left over from a prior emergency call sequence.
-        // This ensures that we'll behave sanely if another
-        // startEmergencyCallFromAirplaneModeSequence() comes in while
-        // we're already in the middle of the sequence.
-        cleanup();
-
-        mNumber = (String) msg.obj;
-        if (DBG) log("- startSequenceInternal: Got mNumber: '" + mNumber + "'");
-
-        mNumRetriesSoFar = 0;
-
-        // Wake lock to make sure the processor doesn't go to sleep midway
-        // through the emergency call sequence.
-        PowerManager pm = (PowerManager) mApp.getSystemService(Context.POWER_SERVICE);
-        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        // Acquire with a timeout, just to be sure we won't hold the wake
-        // lock forever even if a logic bug (in this class) causes us to
-        // somehow never call cleanup().
-        if (DBG) log("- startSequenceInternal: acquiring wake lock");
-        mPartialWakeLock.acquire(WAKE_LOCK_TIMEOUT);
-
-        // No need to check the current service state here, since the only
-        // reason the CallController would call this method in the first
-        // place is if the radio is powered-off.
-        //
-        // So just go ahead and turn the radio on.
-
-        powerOnRadio();  // We'll get an onServiceStateChanged() callback
-                         // when the radio successfully comes up.
-
-        // Next step: when the SERVICE_STATE_CHANGED event comes in,
-        // we'll retry the call; see placeEmergencyCall();
-        // But also, just in case, start a timer to make sure we'll retry
-        // the call even if the SERVICE_STATE_CHANGED event never comes in
-        // for some reason.
-        startRetryTimer();
-
-        // (Our caller is responsible for calling mApp.displayCallScreen().)
-    }
-
-    /**
-     * Handles the SERVICE_STATE_CHANGED event.
-     *
-     * (Normally this event tells us that the radio has finally come
-     * up.  In that case, it's now safe to actually place the
-     * emergency call.)
-     */
-    private void onServiceStateChanged(Message msg) {
-        ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
-        if (DBG) log("onServiceStateChanged()...  new state = " + state);
-
-        // Possible service states:
-        // - STATE_IN_SERVICE        // Normal operation
-        // - STATE_OUT_OF_SERVICE    // Still searching for an operator to register to,
-        //                           // or no radio signal
-        // - STATE_EMERGENCY_ONLY    // Phone is locked; only emergency numbers are allowed
-        // - STATE_POWER_OFF         // Radio is explicitly powered off (airplane mode)
-
-        // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY,
-        // it's finally OK to place the emergency call.
-        boolean okToCall = (state.getState() == ServiceState.STATE_IN_SERVICE)
-                || (state.getState() == ServiceState.STATE_EMERGENCY_ONLY);
-
-        if (okToCall) {
-            // Woo hoo!  It's OK to actually place the call.
-            if (DBG) log("onServiceStateChanged: ok to call!");
-
-            // Deregister for the service state change events.
-            unregisterForServiceStateChanged();
-
-            placeEmergencyCall();
-        } else {
-            // The service state changed, but we're still not ready to call yet.
-            // (This probably was the transition from STATE_POWER_OFF to
-            // STATE_OUT_OF_SERVICE, which happens immediately after powering-on
-            // the radio.)
-            //
-            // So just keep waiting; we'll probably get to either
-            // STATE_IN_SERVICE or STATE_EMERGENCY_ONLY very shortly.
-            // (Or even if that doesn't happen, we'll at least do another retry
-            // when the RETRY_TIMEOUT event fires.)
-            if (DBG) log("onServiceStateChanged: not ready to call yet, keep waiting...");
-        }
-    }
-
-    /**
-     * Handles a DISCONNECT event from the telephony layer.
-     *
-     * Even after we successfully place an emergency call (after powering
-     * on the radio), it's still possible for the call to fail with the
-     * disconnect cause OUT_OF_SERVICE.  If so, schedule a retry.
-     */
-    private void onDisconnect(Message msg) {
-        Connection conn = (Connection) ((AsyncResult) msg.obj).result;
-        int cause = conn.getDisconnectCause();
-        if (DBG) log("onDisconnect: connection '" + conn
-                     + "', addr '" + conn.getAddress()
-                     + "', cause = " + DisconnectCause.toString(cause));
-
-        if (cause == DisconnectCause.OUT_OF_SERVICE) {
-            // Wait a bit more and try again (or just bail out totally if
-            // we've had too many failures.)
-            if (DBG) log("- onDisconnect: OUT_OF_SERVICE, need to retry...");
-            scheduleRetryOrBailOut();
-        } else {
-            // Any other disconnect cause means we're done.
-            // Either the emergency call succeeded *and* ended normally,
-            // or else there was some error that we can't retry.  In either
-            // case, just clean up our internal state.)
-
-            if (DBG) log("==> Disconnect event; clean up...");
-            cleanup();
-
-            // Nothing else to do here.  If the InCallScreen was visible,
-            // it would have received this disconnect event too (so it'll
-            // show the "Call ended" state and finish itself without any
-            // help from us.)
-        }
-    }
-
-    /**
-     * Handles the retry timer expiring.
-     */
-    private void onRetryTimeout() {
-        PhoneConstants.State phoneState = mCM.getState();
-        int serviceState = mCM.getDefaultPhone().getServiceState().getState();
-        if (DBG) log("onRetryTimeout():  phone state " + phoneState
-                     + ", service state " + serviceState
-                     + ", mNumRetriesSoFar = " + mNumRetriesSoFar);
-
-        // - If we're actually in a call, we've succeeded.
-        //
-        // - Otherwise, if the radio is now on, that means we successfully got
-        //   out of airplane mode but somehow didn't get the service state
-        //   change event.  In that case, try to place the call.
-        //
-        // - If the radio is still powered off, try powering it on again.
-
-        if (phoneState == PhoneConstants.State.OFFHOOK) {
-            if (DBG) log("- onRetryTimeout: Call is active!  Cleaning up...");
-            cleanup();
-            return;
-        }
-
-        if (serviceState != ServiceState.STATE_POWER_OFF) {
-            // Woo hoo -- we successfully got out of airplane mode.
-
-            // Deregister for the service state change events; we don't need
-            // these any more now that the radio is powered-on.
-            unregisterForServiceStateChanged();
-
-            placeEmergencyCall();  // If the call fails, placeEmergencyCall()
-                                   // will schedule a retry.
-        } else {
-            // Uh oh; we've waited the full TIME_BETWEEN_RETRIES and the
-            // radio is still not powered-on.  Try again...
-
-            if (DBG) log("- Trying (again) to turn on the radio...");
-            powerOnRadio();  // Again, we'll (hopefully) get an onServiceStateChanged()
-                             // callback when the radio successfully comes up.
-
-            // ...and also set a fresh retry timer (or just bail out
-            // totally if we've had too many failures.)
-            scheduleRetryOrBailOut();
-        }
-    }
-
-    /**
-     * Attempt to power on the radio (i.e. take the device out
-     * of airplane mode.)
-     *
-     * Additionally, start listening for service state changes;
-     * we'll eventually get an onServiceStateChanged() callback
-     * when the radio successfully comes up.
-     */
-    private void powerOnRadio() {
-        if (DBG) log("- powerOnRadio()...");
-
-        // We're about to turn on the radio, so arrange to be notified
-        // when the sequence is complete.
-        registerForServiceStateChanged();
-
-        // If airplane mode is on, we turn it off the same way that the
-        // Settings activity turns it off.
-        if (Settings.Global.getInt(mApp.getContentResolver(),
-                                   Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
-            if (DBG) log("==> Turning off airplane mode...");
-
-            // Change the system setting
-            Settings.Global.putInt(mApp.getContentResolver(),
-                                   Settings.Global.AIRPLANE_MODE_ON, 0);
-
-            // Post the intent
-            Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-            intent.putExtra("state", false);
-            mApp.sendBroadcastAsUser(intent, UserHandle.ALL);
-        } else {
-            // Otherwise, for some strange reason the radio is off
-            // (even though the Settings database doesn't think we're
-            // in airplane mode.)  In this case just turn the radio
-            // back on.
-            if (DBG) log("==> (Apparently) not in airplane mode; manually powering radio on...");
-            mCM.getDefaultPhone().setRadioPower(true);
-        }
-    }
-
-    /**
-     * Actually initiate the outgoing emergency call.
-     * (We do this once the radio has successfully been powered-up.)
-     *
-     * If the call succeeds, we're done.
-     * If the call fails, schedule a retry of the whole sequence.
-     */
-    private void placeEmergencyCall() {
-        if (DBG) log("placeEmergencyCall()...");
-
-        // Place an outgoing call to mNumber.
-        // Note we call PhoneUtils.placeCall() directly; we don't want any
-        // of the behavior from CallController.placeCallInternal() here.
-        // (Specifically, we don't want to start the "emergency call from
-        // airplane mode" sequence from the beginning again!)
-
-        registerForDisconnect();  // Get notified when this call disconnects
-
-        if (DBG) log("- placing call to '" + mNumber + "'...");
-        int callStatus = PhoneUtils.placeCall(mApp,
-                                              mCM.getDefaultPhone(),
-                                              mNumber,
-                                              null,  // contactUri
-                                              true); // isEmergencyCall
-        if (DBG) log("- PhoneUtils.placeCall() returned status = " + callStatus);
-
-        boolean success;
-        // Note PhoneUtils.placeCall() returns one of the CALL_STATUS_*
-        // constants, not a CallStatusCode enum value.
-        switch (callStatus) {
-            case PhoneUtils.CALL_STATUS_DIALED:
-                success = true;
-                break;
-
-            case PhoneUtils.CALL_STATUS_DIALED_MMI:
-            case PhoneUtils.CALL_STATUS_FAILED:
-            default:
-                // Anything else is a failure, and we'll need to retry.
-                Log.w(TAG, "placeEmergencyCall(): placeCall() failed: callStatus = " + callStatus);
-                success = false;
-                break;
-        }
-
-        if (success) {
-            if (DBG) log("==> Success from PhoneUtils.placeCall()!");
-            // Ok, the emergency call is (hopefully) under way.
-
-            // We're not done yet, though, so don't call cleanup() here.
-            // (It's still possible that this call will fail, and disconnect
-            // with cause==OUT_OF_SERVICE.  If so, that will trigger a retry
-            // from the onDisconnect() method.)
-        } else {
-            if (DBG) log("==> Failure.");
-            // Wait a bit more and try again (or just bail out totally if
-            // we've had too many failures.)
-            scheduleRetryOrBailOut();
-        }
-    }
-
-    /**
-     * Schedules a retry in response to some failure (either the radio
-     * failing to power on, or a failure when trying to place the call.)
-     * Or, if we've hit the retry limit, bail out of this whole sequence
-     * and display a failure message to the user.
-     */
-    private void scheduleRetryOrBailOut() {
-        mNumRetriesSoFar++;
-        if (DBG) log("scheduleRetryOrBailOut()...  mNumRetriesSoFar is now " + mNumRetriesSoFar);
-
-        if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
-            Log.w(TAG, "scheduleRetryOrBailOut: hit MAX_NUM_RETRIES; giving up...");
-            cleanup();
-        } else {
-            if (DBG) log("- Scheduling another retry...");
-            startRetryTimer();
-        }
-    }
-
-    /**
-     * Clean up when done with the whole sequence: either after
-     * successfully placing *and* ending the emergency call, or after
-     * bailing out because of too many failures.
-     *
-     * The exact cleanup steps are:
-     * - Take down any progress UI (and also ask the in-call UI to refresh itself,
-     *   if it's still visible)
-     * - Double-check that we're not still registered for any telephony events
-     * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
-     * - Make sure we're not still holding any wake locks
-     *
-     * Basically this method guarantees that there will be no more
-     * activity from the EmergencyCallHelper until the CallController
-     * kicks off the whole sequence again with another call to
-     * startEmergencyCallFromAirplaneModeSequence().
-     *
-     * Note we don't call this method simply after a successful call to
-     * placeCall(), since it's still possible the call will disconnect
-     * very quickly with an OUT_OF_SERVICE error.
-     */
-    private void cleanup() {
-        if (DBG) log("cleanup()...");
-
-        unregisterForServiceStateChanged();
-        unregisterForDisconnect();
-        cancelRetryTimer();
-
-        // Release / clean up the wake lock
-        if (mPartialWakeLock != null) {
-            if (mPartialWakeLock.isHeld()) {
-                if (DBG) log("- releasing wake lock");
-                mPartialWakeLock.release();
-            }
-            mPartialWakeLock = null;
-        }
-    }
-
-    private void startRetryTimer() {
-        removeMessages(RETRY_TIMEOUT);
-        sendEmptyMessageDelayed(RETRY_TIMEOUT, TIME_BETWEEN_RETRIES);
-    }
-
-    private void cancelRetryTimer() {
-        removeMessages(RETRY_TIMEOUT);
-    }
-
-    private void registerForServiceStateChanged() {
-        // Unregister first, just to make sure we never register ourselves
-        // twice.  (We need this because Phone.registerForServiceStateChanged()
-        // does not prevent multiple registration of the same handler.)
-        Phone phone = mCM.getDefaultPhone();
-        phone.unregisterForServiceStateChanged(this);  // Safe even if not currently registered
-        phone.registerForServiceStateChanged(this, SERVICE_STATE_CHANGED, null);
-    }
-
-    private void unregisterForServiceStateChanged() {
-        // This method is safe to call even if we haven't set mPhone yet.
-        Phone phone = mCM.getDefaultPhone();
-        if (phone != null) {
-            phone.unregisterForServiceStateChanged(this);  // Safe even if unnecessary
-        }
-        removeMessages(SERVICE_STATE_CHANGED);  // Clean up any pending messages too
-    }
-
-    private void registerForDisconnect() {
-        // Note: no need to unregister first, since
-        // CallManager.registerForDisconnect() automatically prevents
-        // multiple registration of the same handler.
-        mCM.registerForDisconnect(this, DISCONNECT, null);
-    }
-
-    private void unregisterForDisconnect() {
-        mCM.unregisterForDisconnect(this);  // Safe even if not currently registered
-        removeMessages(DISCONNECT);  // Clean up any pending messages too
-    }
-
-
-    //
-    // Debugging
-    //
-
-    private static void log(String msg) {
-        Log.d(TAG, msg);
-    }
-}
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 765c52d..9d43d60 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -17,9 +17,9 @@
 package com.android.phone;
 
 import android.app.Activity;
+import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.ProgressDialog;
-import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -28,7 +28,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.CountDownTimer;
@@ -36,12 +35,10 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
 
 /**
  * Displays dialog that enables users to exit Emergency Callback Mode
@@ -80,12 +77,12 @@
 
         mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
         // Check if phone is in Emergency Callback Mode. If not, exit.
-        final boolean isInEcm = mPhone.isInEcm();
-        Log.i(TAG, "ECMModeExitDialog launched - isInEcm: " + isInEcm + " phone:" + mPhone);
-        if (mPhone == null || !isInEcm) {
+        if (mPhone == null || !mPhone.isInEcm()) {
+            Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone);
             finish();
             return;
         }
+        Log.i(TAG, "ECMModeExitDialog launched - isInEcm: true" + " phone:" + mPhone);
 
         mHandler = new Handler();
 
diff --git a/src/com/android/phone/ErrorDialogActivity.java b/src/com/android/phone/ErrorDialogActivity.java
deleted file mode 100644
index bf09376..0000000
--- a/src/com/android/phone/ErrorDialogActivity.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.phone.settings.VoicemailSettingsActivity;
-
-/**
- * Used to display an error dialog from within the Telephony service when an outgoing call fails
- */
-public class ErrorDialogActivity extends Activity {
-    private static final String TAG = ErrorDialogActivity.class.getSimpleName();
-
-    public static final String SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA = "show_missing_voicemail";
-    public static final String ERROR_MESSAGE_ID_EXTRA = "error_message_id";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        final boolean showVoicemailDialog = getIntent().getBooleanExtra(
-                SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA, false);
-
-        if (showVoicemailDialog) {
-            showMissingVoicemailErrorDialog();
-        } else {
-            final int error = getIntent().getIntExtra(ERROR_MESSAGE_ID_EXTRA, -1);
-            if (error == -1) {
-                Log.e(TAG, "ErrorDialogActivity called with no error type extra.");
-                finish();
-            }
-            showGenericErrorDialog(error);
-        }
-    }
-
-    private void showGenericErrorDialog(int resid) {
-        final CharSequence msg = getResources().getText(resid);
-
-        final DialogInterface.OnClickListener clickListener;
-
-        final DialogInterface.OnCancelListener cancelListener;
-        clickListener = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                finish();
-            }
-        };
-        cancelListener = new DialogInterface.OnCancelListener() {
-            @Override
-            public void onCancel(DialogInterface dialog) {
-                finish();
-            }
-        };
-
-        final AlertDialog errorDialog = new AlertDialog.Builder(this)
-                .setMessage(msg).setPositiveButton(R.string.ok, clickListener)
-                        .setOnCancelListener(cancelListener).create();
-
-        errorDialog.show();
-    }
-
-    private void showMissingVoicemailErrorDialog() {
-        final AlertDialog missingVoicemailDialog = new AlertDialog.Builder(this)
-        .setTitle(R.string.no_vm_number)
-        .setMessage(R.string.no_vm_number_msg)
-        .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    dontAddVoiceMailNumber();
-                }})
-        .setNegativeButton(R.string.add_vm_number_str, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    addVoiceMailNumberPanel(dialog);
-                }})
-        .setOnCancelListener(new DialogInterface.OnCancelListener() {
-                @Override
-                public void onCancel(DialogInterface dialog) {
-                    dontAddVoiceMailNumber();
-                }}).show();
-    }
-
-
-    private void addVoiceMailNumberPanel(DialogInterface dialog) {
-        if (dialog != null) {
-            dialog.dismiss();
-        }
-
-        // navigate to the Voicemail setting in the Call Settings activity.
-        Intent intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
-        intent.setClass(this, VoicemailSettingsActivity.class);
-        startActivity(intent);
-        finish();
-    }
-
-    private void dontAddVoiceMailNumber() {
-        finish();
-    }
-}
diff --git a/src/com/android/phone/GsmUmtsCallBarringOptions.java b/src/com/android/phone/GsmUmtsCallBarringOptions.java
new file mode 100644
index 0000000..4b875ee
--- /dev/null
+++ b/src/com/android/phone/GsmUmtsCallBarringOptions.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.app.ActionBar;
+import android.app.Dialog;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.phone.settings.fdn.EditPinPreference;
+
+import java.util.ArrayList;
+
+/**
+ * Implements the preference to enable/disable calling barring options and
+ * the dialogs to change the passward.
+ */
+public class GsmUmtsCallBarringOptions extends TimeConsumingPreferenceActivity
+        implements EditPinPreference.OnPinEnteredListener {
+    private static final String LOG_TAG = "GsmUmtsCallBarringOptions";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    // String keys for preference lookup
+    // Preference is handled solely in xml.
+    // Block all outgoing calls
+    private static final String BUTTON_BAOC_KEY = "button_baoc_key";
+    // Block all outgoing international calls
+    private static final String BUTTON_BAOIC_KEY = "button_baoic_key";
+    // Block all outgoing international roaming calls
+    private static final String BUTTON_BAOICxH_KEY = "button_baoicxh_key";
+    // Block all incoming calls
+    private static final String BUTTON_BAIC_KEY = "button_baic_key";
+    // Block all incoming international roaming calls
+    private static final String BUTTON_BAICr_KEY = "button_baicr_key";
+    // Disable all barring
+    private static final String BUTTON_BA_ALL_KEY = "button_ba_all_key";
+    // Change passward
+    private static final String BUTTON_BA_CHANGE_PW_KEY = "button_change_pw_key";
+
+    private static final String PW_CHANGE_STATE_KEY = "pin_change_state_key";
+    private static final String OLD_PW_KEY = "old_pw_key";
+    private static final String NEW_PW_KEY = "new_pw_key";
+    private static final String DIALOG_MESSAGE_KEY = "dialog_message_key";
+    private static final String DIALOG_PW_ENTRY_KEY = "dialog_pw_enter_key";
+    private static final String KEY_STATUS = "toggle";
+    private static final String PREFERENCE_ENABLED_KEY = "PREFERENCE_ENABLED";
+    private static final String PREFERENCE_SHOW_PASSWORD_KEY = "PREFERENCE_SHOW_PASSWORD";
+    private static final String SAVED_BEFORE_LOAD_COMPLETED_KEY = "PROGRESS_SHOWING";
+
+    private CallBarringEditPreference mButtonBAOC;
+    private CallBarringEditPreference mButtonBAOIC;
+    private CallBarringEditPreference mButtonBAOICxH;
+    private CallBarringEditPreference mButtonBAIC;
+    private CallBarringEditPreference mButtonBAICr;
+    private CallBarringDeselectAllPreference mButtonDisableAll;
+    private EditPinPreference mButtonChangePW;
+
+    // State variables
+    private int mPwChangeState;
+    private String mOldPassword;
+    private String mNewPassword;
+    private int mPwChangeDialogStrId;
+
+    private static final int PW_CHANGE_OLD = 0;
+    private static final int PW_CHANGE_NEW = 1;
+    private static final int PW_CHANGE_REENTER = 2;
+
+    private static final int BUSY_READING_DIALOG = 100;
+    private static final int BUSY_SAVING_DIALOG = 200;
+
+    // Password change complete event
+    private static final int EVENT_PW_CHANGE_COMPLETE = 100;
+    // Disable all complete event
+    private static final int EVENT_DISABLE_ALL_COMPLETE = 200;
+
+    private static final int PW_LENGTH = 4;
+
+    private Phone mPhone;
+    private ArrayList<CallBarringEditPreference> mPreferences =
+            new ArrayList<CallBarringEditPreference>();
+    private int mInitIndex = 0;
+    private boolean mFirstResume;
+    private Bundle mIcicle;
+
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private Dialog mProgressDialog;
+
+    @Override
+    public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
+        if (preference == mButtonChangePW) {
+            updatePWChangeState(positiveResult);
+        } else if (preference == mButtonDisableAll) {
+            disableAllBarring(positiveResult);
+        }
+    }
+
+    /**
+     * Display a toast for message.
+     */
+    private void displayMessage(int strId) {
+        Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
+    }
+
+    /**
+     * Attempt to disable all for call barring settings.
+     */
+    private void disableAllBarring(boolean positiveResult) {
+        if (!positiveResult) {
+            // Return on cancel
+            return;
+        }
+
+        String password = null;
+        if (mButtonDisableAll.isPasswordShown()) {
+            password = mButtonDisableAll.getText();
+            // Validate the length of password first, before submitting it to the
+            // RIL for CB disable.
+            if (!validatePassword(password)) {
+                mButtonDisableAll.setText("");
+                displayMessage(R.string.call_barring_right_pwd_number);
+                return;
+            }
+        }
+
+        // Submit the disable all request
+        mButtonDisableAll.setText("");
+        Message onComplete = mHandler.obtainMessage(EVENT_DISABLE_ALL_COMPLETE);
+        mPhone.setCallBarring(CommandsInterface.CB_FACILITY_BA_ALL, false, password, onComplete, 0);
+        this.onStarted(mButtonDisableAll, false);
+    }
+
+    /**
+     * Attempt to change the password for call barring settings.
+     */
+    private void updatePWChangeState(boolean positiveResult) {
+        if (!positiveResult) {
+            // Reset the state on cancel
+            resetPwChangeState();
+            return;
+        }
+
+        // Progress through the dialog states, generally in this order:
+        // 1. Enter old password
+        // 2. Enter new password
+        // 3. Re-Enter new password
+        // In general, if any invalid entries are made, the dialog re-
+        // appears with text to indicate what the issue is.
+        switch (mPwChangeState) {
+            case PW_CHANGE_OLD:
+                mOldPassword = mButtonChangePW.getText();
+                mButtonChangePW.setText("");
+                if (validatePassword(mOldPassword)) {
+                    mPwChangeState = PW_CHANGE_NEW;
+                    displayPwChangeDialog();
+                } else {
+                    displayPwChangeDialog(R.string.call_barring_right_pwd_number, true);
+                }
+                break;
+            case PW_CHANGE_NEW:
+                mNewPassword = mButtonChangePW.getText();
+                mButtonChangePW.setText("");
+                if (validatePassword(mNewPassword)) {
+                    mPwChangeState = PW_CHANGE_REENTER;
+                    displayPwChangeDialog();
+                } else {
+                    displayPwChangeDialog(R.string.call_barring_right_pwd_number, true);
+                }
+                break;
+            case PW_CHANGE_REENTER:
+                // If the re-entered password is not valid, display a message
+                // and reset the state.
+                if (!mNewPassword.equals(mButtonChangePW.getText())) {
+                    mPwChangeState = PW_CHANGE_NEW;
+                    mButtonChangePW.setText("");
+                    displayPwChangeDialog(R.string.call_barring_pwd_not_match, true);
+                } else {
+                    // If the password is valid, then submit the change password request
+                    mButtonChangePW.setText("");
+                    Message onComplete = mHandler.obtainMessage(EVENT_PW_CHANGE_COMPLETE);
+                    ((GsmCdmaPhone) mPhone).changeCallBarringPassword(
+                            CommandsInterface.CB_FACILITY_BA_ALL,
+                            mOldPassword, mNewPassword, onComplete);
+                    this.onStarted(mButtonChangePW, false);
+                }
+                break;
+            default:
+                if (DBG) {
+                    Log.d(LOG_TAG, "updatePWChangeState: Unknown password change state: "
+                            + mPwChangeState);
+                }
+                break;
+        }
+    }
+
+    /**
+     * Handler for asynchronous replies from the framework layer.
+     */
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar = (AsyncResult) msg.obj;
+            switch (msg.what) {
+                // Handle the response message for password change from the framework layer.
+                case EVENT_PW_CHANGE_COMPLETE: {
+                    onFinished(mButtonChangePW, false);
+                    // Unsuccessful change, display a toast to user with failure reason.
+                    if (ar.exception != null) {
+                        if (DBG) {
+                            Log.d(LOG_TAG,
+                                    "change password for call barring failed with exception: "
+                                            + ar.exception);
+                        }
+                        onException(mButtonChangePW, (CommandException) ar.exception);
+                        mButtonChangePW.setEnabled(true);
+                    } else if (ar.userObj instanceof Throwable) {
+                        onError(mButtonChangePW, RESPONSE_ERROR);
+                    } else {
+                        // Successful change.
+                        displayMessage(R.string.call_barring_change_pwd_success);
+                    }
+                    resetPwChangeState();
+                    break;
+                }
+                // When disabling all call barring, either fail and display a toast,
+                // or just update the UI.
+                case EVENT_DISABLE_ALL_COMPLETE: {
+                    onFinished(mButtonDisableAll, false);
+                    if (ar.exception != null) {
+                        if (DBG) {
+                            Log.d(LOG_TAG, "can not disable all call barring with exception: "
+                                    + ar.exception);
+                        }
+                        onException(mButtonDisableAll, (CommandException) ar.exception);
+                        mButtonDisableAll.setEnabled(true);
+                    } else if (ar.userObj instanceof Throwable) {
+                        onError(mButtonDisableAll, RESPONSE_ERROR);
+                    } else {
+                        // Reset to normal behaviour on successful change.
+                        displayMessage(R.string.call_barring_deactivate_success);
+                        resetCallBarringPrefState(false);
+                    }
+                    break;
+                }
+                default: {
+                    if (DBG) {
+                        Log.d(LOG_TAG, "Unknown message id: " + msg.what);
+                    }
+                    break;
+                }
+            }
+        }
+    };
+
+    /**
+     * The next two functions are for updating the message field on the dialog.
+     */
+    private void displayPwChangeDialog() {
+        displayPwChangeDialog(0, true);
+    }
+
+    private void displayPwChangeDialog(int strId, boolean shouldDisplay) {
+        int msgId = 0;
+        switch (mPwChangeState) {
+            case PW_CHANGE_OLD:
+                msgId = R.string.call_barring_old_pwd;
+                break;
+            case PW_CHANGE_NEW:
+                msgId = R.string.call_barring_new_pwd;
+                break;
+            case PW_CHANGE_REENTER:
+                msgId = R.string.call_barring_confirm_pwd;
+                break;
+            default:
+                break;
+        }
+
+        // Append the note/additional message, if needed.
+        if (strId != 0) {
+            mButtonChangePW.setDialogMessage(getText(msgId) + "\n" + getText(strId));
+        } else {
+            mButtonChangePW.setDialogMessage(msgId);
+        }
+
+        // Only display if requested.
+        if (shouldDisplay) {
+            mButtonChangePW.showPinDialog();
+        }
+        mPwChangeDialogStrId = strId;
+    }
+
+    /**
+     * Reset the state of the password change dialog.
+     */
+    private void resetPwChangeState() {
+        mPwChangeState = PW_CHANGE_OLD;
+        displayPwChangeDialog(0, false);
+        mOldPassword = "";
+        mNewPassword = "";
+    }
+
+    /**
+     * Reset the state of the all call barring setting to disable.
+     */
+    private void resetCallBarringPrefState(boolean enable) {
+        for (CallBarringEditPreference pref : mPreferences) {
+            pref.mIsActivated = enable;
+            pref.updateSummaryText();
+        }
+    }
+
+    /**
+     * Validate the password entry.
+     *
+     * @param password This is the password to validate
+     */
+    private boolean validatePassword(String password) {
+        return password != null && password.length() == PW_LENGTH;
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        if (DBG) {
+            Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file");
+        }
+        addPreferencesFromResource(R.xml.callbarring_options);
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+        mPhone = mSubscriptionInfoHelper.getPhone();
+        if (DBG) {
+            Log.d(LOG_TAG, "onCreate, reading callbarring_options.xml file finished!");
+        }
+
+        // Get UI object references
+        PreferenceScreen prefSet = getPreferenceScreen();
+        mButtonBAOC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOC_KEY);
+        mButtonBAOIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOIC_KEY);
+        mButtonBAOICxH = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAOICxH_KEY);
+        mButtonBAIC = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAIC_KEY);
+        mButtonBAICr = (CallBarringEditPreference) prefSet.findPreference(BUTTON_BAICr_KEY);
+        mButtonDisableAll = (CallBarringDeselectAllPreference)
+                prefSet.findPreference(BUTTON_BA_ALL_KEY);
+        mButtonChangePW = (EditPinPreference) prefSet.findPreference(BUTTON_BA_CHANGE_PW_KEY);
+
+        // Assign click listener and update state
+        mButtonBAOC.setOnPinEnteredListener(this);
+        mButtonBAOIC.setOnPinEnteredListener(this);
+        mButtonBAOICxH.setOnPinEnteredListener(this);
+        mButtonBAIC.setOnPinEnteredListener(this);
+        mButtonBAICr.setOnPinEnteredListener(this);
+        mButtonDisableAll.setOnPinEnteredListener(this);
+        mButtonChangePW.setOnPinEnteredListener(this);
+
+        // Store CallBarringEditPreferencence objects in array list.
+        mPreferences.add(mButtonBAOC);
+        mPreferences.add(mButtonBAOIC);
+        mPreferences.add(mButtonBAOICxH);
+        mPreferences.add(mButtonBAIC);
+        mPreferences.add(mButtonBAICr);
+
+        // Find out if password is currently used.
+        boolean usePassword = true;
+        boolean useDisableaAll = true;
+
+        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+        if (imsPhone != null
+                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                        || imsPhone.isUtEnabled())) {
+            usePassword = false;
+            useDisableaAll = false;
+        }
+
+        // Find out if the sim card is ready.
+        boolean isSimReady = TelephonyManager.from(this).getSimState(
+                SubscriptionManager.getSlotIndex(mPhone.getSubId()))
+                        == TelephonyManager.SIM_STATE_READY;
+
+        // Deactivate all option is unavailable when sim card is not ready or Ut is enabled.
+        if (isSimReady && useDisableaAll) {
+            mButtonDisableAll.setEnabled(true);
+            mButtonDisableAll.init(mPhone);
+        } else {
+            mButtonDisableAll.setEnabled(false);
+        }
+
+        // Change password option is unavailable when sim card is not ready or when the password is
+        // not used.
+        if (isSimReady && usePassword) {
+            mButtonChangePW.setEnabled(true);
+        } else {
+            mButtonChangePW.setEnabled(false);
+            mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled);
+        }
+
+        // Wait to do the initialization until onResume so that the TimeConsumingPreferenceActivity
+        // dialog can display as it relies on onResume / onPause to maintain its foreground state.
+        mFirstResume = true;
+        mIcicle = icicle;
+
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            // android.R.id.home will be triggered in onOptionsItemSelected()
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        if (mIcicle != null && !mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) {
+            if (DBG) {
+                Log.d(LOG_TAG, "restore stored states");
+            }
+            mInitIndex = mPreferences.size();
+
+            for (CallBarringEditPreference pref : mPreferences) {
+                Bundle bundle = mIcicle.getParcelable(pref.getKey());
+                if (bundle != null) {
+                    pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS));
+                    pref.init(this, true, mPhone);
+                    pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()));
+                    pref.setInputMethodNeeded(bundle.getBoolean(PREFERENCE_SHOW_PASSWORD_KEY,
+                            pref.needInputMethod()));
+                }
+            }
+            mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY);
+            mOldPassword = mIcicle.getString(OLD_PW_KEY);
+            mNewPassword = mIcicle.getString(NEW_PW_KEY);
+            displayPwChangeDialog(mIcicle.getInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId), false);
+            mButtonChangePW.setText(mIcicle.getString(DIALOG_PW_ENTRY_KEY));
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        if (mFirstResume) {
+            if (mIcicle == null || mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "onResume: start to init ");
+                }
+                resetPwChangeState();
+                mPreferences.get(mInitIndex).init(this, false, mPhone);
+
+                // Request removing BUSY_SAVING_DIALOG because reading is restarted.
+                // (If it doesn't exist, nothing happen.)
+                removeDialog(BUSY_SAVING_DIALOG);
+            }
+            mFirstResume = false;
+            mIcicle = null;
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        for (CallBarringEditPreference pref : mPreferences) {
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(KEY_STATUS, pref.mIsActivated);
+            bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled());
+            bundle.putBoolean(PREFERENCE_SHOW_PASSWORD_KEY, pref.needInputMethod());
+            outState.putParcelable(pref.getKey(), bundle);
+        }
+        outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState);
+        outState.putString(OLD_PW_KEY, mOldPassword);
+        outState.putString(NEW_PW_KEY, mNewPassword);
+        outState.putInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId);
+        outState.putString(DIALOG_PW_ENTRY_KEY, mButtonChangePW.getText());
+
+        outState.putBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY,
+                mProgressDialog != null && mProgressDialog.isShowing());
+    }
+
+    /**
+     * Finish initialization of this preference and start next.
+     *
+     * @param preference The preference.
+     * @param reading If true to dismiss the busy reading dialog,
+     *                false to dismiss the busy saving dialog.
+     */
+    public void onFinished(Preference preference, boolean reading) {
+        if (mInitIndex < mPreferences.size() - 1 && !isFinishing()) {
+            mInitIndex++;
+            mPreferences.get(mInitIndex).init(this, false, mPhone);
+        }
+        super.onFinished(preference, reading);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int itemId = item.getItemId();
+        if (itemId == android.R.id.home) {
+            CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper);
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+        super.onPrepareDialog(id, dialog, args);
+        if (id == BUSY_READING_DIALOG || id == BUSY_SAVING_DIALOG) {
+            // For onSaveInstanceState, treat the SAVING dialog as the same as the READING. As
+            // the result, if the activity is recreated while waiting for SAVING, it starts reading
+            // all the newest data.
+            mProgressDialog = dialog;
+        }
+    }
+}
diff --git a/src/com/android/phone/GsmUmtsCallOptions.java b/src/com/android/phone/GsmUmtsCallOptions.java
index 419e72c..3b27d28 100644
--- a/src/com/android/phone/GsmUmtsCallOptions.java
+++ b/src/com/android/phone/GsmUmtsCallOptions.java
@@ -17,10 +17,12 @@
 package com.android.phone;
 
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
 import android.view.MenuItem;
 
 import com.android.internal.telephony.Phone;
@@ -31,6 +33,7 @@
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+    private static final String CALL_BARRING_KEY = "call_barring_key";
     private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
 
     @Override
@@ -68,5 +71,18 @@
                 prefScreen.findPreference(ADDITIONAL_GSM_SETTINGS_KEY);
         additionalGsmSettingsPref.setIntent(
                 subInfoHelper.getIntent(GsmUmtsAdditionalCallOptions.class));
+
+        Preference callBarringPref = prefScreen.findPreference(CALL_BARRING_KEY);
+        PersistableBundle b = null;
+        if (subInfoHelper.hasSubId()) {
+            b = PhoneGlobals.getInstance().getCarrierConfigForSubId(subInfoHelper.getSubId());
+        } else {
+            b = PhoneGlobals.getInstance().getCarrierConfig();
+        }
+        if (b != null && b.getBoolean(CarrierConfigManager.KEY_CALL_BARRING_VISIBILITY_BOOL)) {
+            callBarringPref.setIntent(subInfoHelper.getIntent(GsmUmtsCallBarringOptions.class));
+        } else {
+            prefScreen.removePreference(callBarringPref);
+        }
     }
 }
diff --git a/src/com/android/phone/MMIDialogActivity.java b/src/com/android/phone/MMIDialogActivity.java
index 4afa6be..c9be2ac 100644
--- a/src/com/android/phone/MMIDialogActivity.java
+++ b/src/com/android/phone/MMIDialogActivity.java
@@ -25,7 +25,6 @@
 import android.os.Message;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
-import android.widget.Toast;
 
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.MmiCode;
@@ -56,6 +55,11 @@
         int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
         mPhone = PhoneGlobals.getPhone(subId);
+        if (mPhone == null) {
+            Log.w(TAG, "onCreate: invalid subscription id (" + subId + ") lead to null"
+                    + " Phone.");
+            finish();
+        }
         mHandler = new Handler() {
                 @Override
                 public void handleMessage(Message msg) {
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index c061fbc..96309ad 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -1671,7 +1671,7 @@
 
             try {
                 if ((mImsMgr == null
-                        || mImsMgr.getImsServiceStatus() != ImsFeature.STATE_READY
+                        || mImsMgr.getImsServiceState() != ImsFeature.STATE_READY
                         || !mImsMgr.isVolteEnabledByPlatform()
                         || !mImsMgr.isVolteProvisionedOnDevice()
                         || carrierConfig.getBoolean(
diff --git a/src/com/android/phone/OutgoingCallBroadcaster.java b/src/com/android/phone/OutgoingCallBroadcaster.java
deleted file mode 100644
index 6bb1388..0000000
--- a/src/com/android/phone/OutgoingCallBroadcaster.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.AppOpsManager;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.ProgressBar;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyCapabilities;
-
-/**
- * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the
- * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which
- * contains the phone number being dialed. Applications can use this intent to (1) see which numbers
- * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call
- * from being placed.
- *
- * After the other applications have had a chance to see the
- * ACTION_NEW_OUTGOING_CALL intent, it finally reaches the
- * {@link OutgoingCallReceiver}, which passes the (possibly modified)
- * intent on to the {@link SipCallOptionHandler}, which will
- * ultimately start the call using the CallController.placeCall() API.
- *
- * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail
- * number) are exempt from being broadcast.
- * Calls to emergency numbers are still broadcast for informative purposes. The call is placed
- * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented.
- */
-public class OutgoingCallBroadcaster extends Activity
-        implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
-
-    private static final String TAG = "OutgoingCallBroadcaster";
-    private static final boolean DBG =
-            (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
-    // Do not check in with VDBG = true, since that may write PII to the system log.
-    private static final boolean VDBG = false;
-
-    public static final String ACTION_SIP_SELECT_PHONE = "com.android.phone.SIP_SELECT_PHONE";
-    public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED";
-    public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI";
-    public static final String EXTRA_NEW_CALL_INTENT = "android.phone.extra.NEW_CALL_INTENT";
-    public static final String EXTRA_SIP_PHONE_URI = "android.phone.extra.SIP_PHONE_URI";
-    public static final String EXTRA_ACTUAL_NUMBER_TO_DIAL =
-            "android.phone.extra.ACTUAL_NUMBER_TO_DIAL";
-    public static final String EXTRA_THIRD_PARTY_CALL_COMPONENT =
-            "android.phone.extra.THIRD_PARTY_CALL_COMPONENT";
-
-    /**
-     * Identifier for intent extra for sending an empty Flash message for
-     * CDMA networks. This message is used by the network to simulate a
-     * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
-     *
-     * TODO: Receiving an intent extra to tell the phone to send this flash is a
-     * temporary measure. To be replaced with an external ITelephony call in the future.
-     * TODO: Keep in sync with the string defined in TwelveKeyDialer.java in Contacts app
-     * until this is replaced with the ITelephony API.
-     */
-    public static final String EXTRA_SEND_EMPTY_FLASH =
-            "com.android.phone.extra.SEND_EMPTY_FLASH";
-
-    // Dialog IDs
-    private static final int DIALOG_NOT_VOICE_CAPABLE = 1;
-
-    /** Note message codes < 100 are reserved for the PhoneApp. */
-    private static final int EVENT_OUTGOING_CALL_TIMEOUT = 101;
-    private static final int EVENT_DELAYED_FINISH = 102;
-
-    private static final int OUTGOING_CALL_TIMEOUT_THRESHOLD = 2000; // msec
-    private static final int DELAYED_FINISH_TIME = 2000; // msec
-
-    /**
-     * ProgressBar object with "spinner" style, which will be shown if we take more than
-     * {@link #EVENT_OUTGOING_CALL_TIMEOUT} msec to handle the incoming Intent.
-     */
-    private ProgressBar mWaitingSpinner;
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == EVENT_OUTGOING_CALL_TIMEOUT) {
-                Log.i(TAG, "Outgoing call takes too long. Showing the spinner.");
-                mWaitingSpinner.setVisibility(View.VISIBLE);
-            } else if (msg.what == EVENT_DELAYED_FINISH) {
-                finish();
-            } else {
-                Log.wtf(TAG, "Unknown message id: " + msg.what);
-            }
-        }
-    };
-
-    /**
-     * Starts the delayed finish() of OutgoingCallBroadcaster in order to give the UI
-     * some time to start up.
-     */
-    private void startDelayedFinish() {
-        mHandler.sendEmptyMessageDelayed(EVENT_DELAYED_FINISH, DELAYED_FINISH_TIME);
-    }
-
-    /**
-     * OutgoingCallReceiver finishes NEW_OUTGOING_CALL broadcasts, starting
-     * the InCallScreen if the broadcast has not been canceled, possibly with
-     * a modified phone number and optional provider info (uri + package name + remote views.)
-     */
-    public class OutgoingCallReceiver extends BroadcastReceiver {
-        private static final String TAG = "OutgoingCallReceiver";
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT);
-            final boolean isAttemptingCall = doReceive(context, intent);
-            if (DBG) Log.v(TAG, "OutgoingCallReceiver is going to finish the Activity itself.");
-
-            // We cannot finish the activity immediately here because it would cause the temporary
-            // black screen of OutgoingBroadcaster to go away and we need it to stay up until the
-            // UI (in a different process) has time to come up.
-            // However, if we know we are not attemping a call, we need to finish the activity
-            // immediately so that subsequent CALL intents will retrigger a new
-            // OutgoingCallReceiver. see b/10857203
-            if (isAttemptingCall) {
-                startDelayedFinish();
-            } else {
-                finish();
-            }
-        }
-
-
-        /**
-         * Handes receipt of ordered new_outgoing_call intent. Verifies that the return from the
-         * ordered intent is valid.
-         * @return true if the call is being attempted; false if we are canceling the call.
-         */
-        public boolean doReceive(Context context, Intent intent) {
-            if (DBG) Log.v(TAG, "doReceive: " + intent);
-
-            boolean alreadyCalled;
-            String number;
-            String originalUri;
-
-            alreadyCalled = intent.getBooleanExtra(
-                    OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false);
-            if (alreadyCalled) {
-                if (DBG) Log.v(TAG, "CALL already placed -- returning.");
-                return false;
-            }
-
-            // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData
-            // is used as the actual number to call. (If null, no call will be
-            // placed.)
-
-            number = getResultData();
-            if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'");
-
-            final PhoneGlobals app = PhoneGlobals.getInstance();
-            final Phone phone = PhoneGlobals.getPhone();
-
-            if (number == null) {
-                if (DBG) Log.v(TAG, "CALL cancelled (null number), returning...");
-                return false;
-            } else if (TelephonyCapabilities.supportsOtasp(phone)
-                    && (phone.getState() != PhoneConstants.State.IDLE)
-                    && (phone.isOtaSpNumber(number))) {
-                if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning.");
-                return false;
-            } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number)) {
-                // Just like 3rd-party apps aren't allowed to place emergency
-                // calls via the ACTION_CALL intent, we also don't allow 3rd
-                // party apps to use the NEW_OUTGOING_CALL broadcast to rewrite
-                // an outgoing call into an emergency number.
-                Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + ".");
-                return false;
-            }
-
-            originalUri = intent.getStringExtra(
-                    OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
-            if (originalUri == null) {
-                Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning.");
-                return false;
-            }
-
-            Uri uri = Uri.parse(originalUri);
-
-            // We already called convertKeypadLettersToDigits() and
-            // stripSeparators() way back in onCreate(), before we sent out the
-            // NEW_OUTGOING_CALL broadcast.  But we need to do it again here
-            // too, since the number might have been modified/rewritten during
-            // the broadcast (and may now contain letters or separators again.)
-            number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
-            number = PhoneNumberUtils.stripSeparators(number);
-
-            if (DBG) Log.v(TAG, "doReceive: proceeding with call...");
-            if (VDBG) Log.v(TAG, "- uri: " + uri);
-            if (VDBG) Log.v(TAG, "- actual number to dial: '" + number + "'");
-
-            startSipCallOptionHandler(context, intent, uri, number);
-
-            return true;
-        }
-    }
-
-    /**
-     * Launch the SipCallOptionHandler, which is the next step(*) in the
-     * outgoing-call sequence after the outgoing call broadcast is
-     * complete.
-     *
-     * (*) We now know exactly what phone number we need to dial, so the next
-     *     step is for the SipCallOptionHandler to decide which Phone type (SIP
-     *     or PSTN) should be used.  (Depending on the user's preferences, this
-     *     decision may also involve popping up a dialog to ask the user to
-     *     choose what type of call this should be.)
-     *
-     * @param context used for the startActivity() call
-     *
-     * @param intent the intent from the previous step of the outgoing-call
-     *   sequence.  Normally this will be the NEW_OUTGOING_CALL broadcast intent
-     *   that came in to the OutgoingCallReceiver, although it can also be the
-     *   original ACTION_CALL intent that started the whole sequence (in cases
-     *   where we don't do the NEW_OUTGOING_CALL broadcast at all, like for
-     *   emergency numbers or SIP addresses).
-     *
-     * @param uri the data URI from the original CALL intent, presumably either
-     *   a tel: or sip: URI.  For tel: URIs, note that the scheme-specific part
-     *   does *not* necessarily have separators and keypad letters stripped (so
-     *   we might see URIs like "tel:(650)%20555-1234" or "tel:1-800-GOOG-411"
-     *   here.)
-     *
-     * @param number the actual number (or SIP address) to dial.  This is
-     *   guaranteed to be either a PSTN phone number with separators stripped
-     *   out and keypad letters converted to digits (like "16505551234"), or a
-     *   raw SIP address (like "user@example.com").
-     */
-    private void startSipCallOptionHandler(Context context, Intent intent,
-            Uri uri, String number) {
-        // TODO: Remove this code.
-    }
-
-    /**
-     * This method is the single point of entry for the CALL intent, which is used (by built-in
-     * apps like Contacts / Dialer, as well as 3rd-party apps) to initiate an outgoing voice call.
-     *
-     *
-     */
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        setContentView(R.layout.outgoing_call_broadcaster);
-        mWaitingSpinner = (ProgressBar) findViewById(R.id.spinner);
-
-        Intent intent = getIntent();
-        if (DBG) {
-            final Configuration configuration = getResources().getConfiguration();
-            Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle);
-            Log.v(TAG, " - getIntent() = " + intent);
-            Log.v(TAG, " - configuration = " + configuration);
-        }
-
-        if (icicle != null) {
-            // A non-null icicle means that this activity is being
-            // re-initialized after previously being shut down.
-            //
-            // In practice this happens very rarely (because the lifetime
-            // of this activity is so short!), but it *can* happen if the
-            // framework detects a configuration change at exactly the
-            // right moment; see bug 2202413.
-            //
-            // In this case, do nothing.  Our onCreate() method has already
-            // run once (with icicle==null the first time), which means
-            // that the NEW_OUTGOING_CALL broadcast for this new call has
-            // already been sent.
-            Log.i(TAG, "onCreate: non-null icicle!  "
-                  + "Bailing out, not sending NEW_OUTGOING_CALL broadcast...");
-
-            // No need to finish() here, since the OutgoingCallReceiver from
-            // our original instance will do that.  (It'll actually call
-            // finish() on our original instance, which apparently works fine
-            // even though the ActivityManager has already shut that instance
-            // down.  And note that if we *do* call finish() here, that just
-            // results in an "ActivityManager: Duplicate finish request"
-            // warning when the OutgoingCallReceiver runs.)
-
-            return;
-        }
-
-        processIntent(intent);
-
-        // isFinishing() return false when 1. broadcast is still ongoing, or 2. dialog is being
-        // shown. Otherwise finish() is called inside processIntent(), is isFinishing() here will
-        // return true.
-        if (DBG) Log.v(TAG, "At the end of onCreate(). isFinishing(): " + isFinishing());
-    }
-
-    /**
-     * Interprets a given Intent and starts something relevant to the Intent.
-     *
-     * This method will handle three kinds of actions:
-     *
-     * - CALL (action for usual outgoing voice calls)
-     * - CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth)
-     * - CALL_EMERGENCY (from the EmergencyDialer that's reachable from the lockscreen.)
-     *
-     * The exact behavior depends on the intent's data:
-     *
-     * - The most typical is a tel: URI, which we handle by starting the
-     *   NEW_OUTGOING_CALL broadcast.  That broadcast eventually triggers
-     *   the sequence OutgoingCallReceiver -> SipCallOptionHandler ->
-     *   InCallScreen.
-     *
-     * - Or, with a sip: URI we skip the NEW_OUTGOING_CALL broadcast and
-     *   go directly to SipCallOptionHandler, which then leads to the
-     *   InCallScreen.
-     *
-     * - voicemail: URIs take the same path as regular tel: URIs.
-     *
-     * Other special cases:
-     *
-     * - Outgoing calls are totally disallowed on non-voice-capable
-     *   devices (see handleNonVoiceCapable()).
-     *
-     * - A CALL intent with the EXTRA_SEND_EMPTY_FLASH extra (and
-     *   presumably no data at all) means "send an empty flash" (which
-     *   is only meaningful on CDMA devices while a call is already
-     *   active.)
-     *
-     */
-    private void processIntent(Intent intent) {
-        if (DBG) {
-            Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread());
-        }
-        final Configuration configuration = getResources().getConfiguration();
-
-        // Outgoing phone calls are only allowed on "voice-capable" devices.
-        if (!PhoneGlobals.sVoiceCapable) {
-            Log.i(TAG, "This device is detected as non-voice-capable device.");
-            handleNonVoiceCapable(intent);
-            return;
-        }
-
-        String action = intent.getAction();
-        String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
-        // Check the number, don't convert for sip uri
-        // TODO put uriNumber under PhoneNumberUtils
-        if (number != null) {
-            if (!PhoneNumberUtils.isUriNumber(number)) {
-                number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
-                number = PhoneNumberUtils.stripSeparators(number);
-            }
-        } else {
-            Log.w(TAG, "The number obtained from Intent is null.");
-        }
-
-        AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);
-        int launchedFromUid;
-        String launchedFromPackage;
-        try {
-            launchedFromUid = ActivityManager.getService().getLaunchedFromUid(
-                    getActivityToken());
-            launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage(
-                    getActivityToken());
-        } catch (RemoteException e) {
-            launchedFromUid = -1;
-            launchedFromPackage = null;
-        }
-        if (appOps.noteOpNoThrow(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage)
-                != AppOpsManager.MODE_ALLOWED) {
-            Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package "
-                    + launchedFromPackage);
-            finish();
-            return;
-        }
-
-        // If true, this flag will indicate that the current call is a special kind
-        // of call (most likely an emergency number) that 3rd parties aren't allowed
-        // to intercept or affect in any way.  (In that case, we start the call
-        // immediately rather than going through the NEW_OUTGOING_CALL sequence.)
-        boolean callNow;
-
-        if (getClass().getName().equals(intent.getComponent().getClassName())) {
-            // If we were launched directly from the OutgoingCallBroadcaster,
-            // not one of its more privileged aliases, then make sure that
-            // only the non-privileged actions are allowed.
-            if (!Intent.ACTION_CALL.equals(intent.getAction())) {
-                Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL");
-                intent.setAction(Intent.ACTION_CALL);
-            }
-        }
-
-        // Check whether or not this is an emergency number, in order to
-        // enforce the restriction that only the CALL_PRIVILEGED and
-        // CALL_EMERGENCY intents are allowed to make emergency calls.
-        //
-        // (Note that the ACTION_CALL check below depends on the result of
-        // isPotentialLocalEmergencyNumber() rather than just plain
-        // isLocalEmergencyNumber(), to be 100% certain that we *don't*
-        // allow 3rd party apps to make emergency calls by passing in an
-        // "invalid" number like "9111234" that isn't technically an
-        // emergency number but might still result in an emergency call
-        // with some networks.)
-        final boolean isExactEmergencyNumber =
-                (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(this, number);
-        final boolean isPotentialEmergencyNumber =
-                (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(this, number);
-        if (VDBG) {
-            Log.v(TAG, " - Checking restrictions for number '" + number + "':");
-            Log.v(TAG, "     isExactEmergencyNumber     = " + isExactEmergencyNumber);
-            Log.v(TAG, "     isPotentialEmergencyNumber = " + isPotentialEmergencyNumber);
-        }
-
-        /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */
-        // TODO: This code is redundant with some code in InCallScreen: refactor.
-        if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
-            // We're handling a CALL_PRIVILEGED intent, so we know this request came
-            // from a trusted source (like the built-in dialer.)  So even a number
-            // that's *potentially* an emergency number can safely be promoted to
-            // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from
-            // the dialer if you really want to.)
-            if (isPotentialEmergencyNumber) {
-                Log.i(TAG, "ACTION_CALL_PRIVILEGED is used while the number is a potential"
-                        + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead.");
-                action = Intent.ACTION_CALL_EMERGENCY;
-            } else {
-                action = Intent.ACTION_CALL;
-            }
-            if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action);
-            intent.setAction(action);
-        }
-
-        if (Intent.ACTION_CALL.equals(action)) {
-            if (isPotentialEmergencyNumber) {
-                Log.w(TAG, "Cannot call potential emergency number '" + number
-                        + "' with CALL Intent " + intent + ".");
-                Log.i(TAG, "Launching default dialer instead...");
-
-                Intent invokeFrameworkDialer = new Intent();
-
-                // TwelveKeyDialer is in a tab so we really want
-                // DialtactsActivity.  Build the intent 'manually' to
-                // use the java resolver to find the dialer class (as
-                // opposed to a Context which look up known android
-                // packages only)
-                final Resources resources = getResources();
-                invokeFrameworkDialer.setClassName(
-                        resources.getString(R.string.ui_default_package),
-                        resources.getString(R.string.dialer_default_class));
-                invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
-                invokeFrameworkDialer.setData(intent.getData());
-                if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: "
-                               + invokeFrameworkDialer);
-                startActivity(invokeFrameworkDialer);
-                finish();
-                return;
-            }
-            callNow = false;
-        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
-            // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED
-            // intent that we just turned into a CALL_EMERGENCY intent (see
-            // above), or else it really is an CALL_EMERGENCY intent that
-            // came directly from some other app (e.g. the EmergencyDialer
-            // activity built in to the Phone app.)
-            // Make sure it's at least *possible* that this is really an
-            // emergency number.
-            if (!isPotentialEmergencyNumber) {
-                Log.w(TAG, "Cannot call non-potential-emergency number " + number
-                        + " with EMERGENCY_CALL Intent " + intent + "."
-                        + " Finish the Activity immediately.");
-                finish();
-                return;
-            }
-            callNow = true;
-        } else {
-            Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately.");
-            finish();
-            return;
-        }
-
-        // Make sure the screen is turned on.  This is probably the right
-        // thing to do, and more importantly it works around an issue in the
-        // activity manager where we will not launch activities consistently
-        // when the screen is off (since it is trying to keep them paused
-        // and has...  issues).
-        //
-        // Also, this ensures the device stays awake while doing the following
-        // broadcast; technically we should be holding a wake lock here
-        // as well.
-        PhoneGlobals.getInstance().wakeUpScreen();
-
-        // If number is null, we're probably trying to call a non-existent voicemail number,
-        // send an empty flash or something else is fishy.  Whatever the problem, there's no
-        // number, so there's no point in allowing apps to modify the number.
-        if (TextUtils.isEmpty(number)) {
-            if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) {
-                Log.i(TAG, "onCreate: SEND_EMPTY_FLASH...");
-                PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone());
-                finish();
-                return;
-            } else {
-                Log.i(TAG, "onCreate: null or empty number, setting callNow=true...");
-                callNow = true;
-            }
-        }
-
-        if (callNow) {
-            // This is a special kind of call (most likely an emergency number)
-            // that 3rd parties aren't allowed to intercept or affect in any way.
-            // So initiate the outgoing call immediately.
-
-            Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent);
-
-            // Initiate the outgoing call, and simultaneously launch the
-            // InCallScreen to display the in-call UI:
-            PhoneGlobals.getInstance().callController.placeCall(intent);
-
-            // Note we do *not* "return" here, but instead continue and
-            // send the ACTION_NEW_OUTGOING_CALL broadcast like for any
-            // other outgoing call.  (But when the broadcast finally
-            // reaches the OutgoingCallReceiver, we'll know not to
-            // initiate the call again because of the presence of the
-            // EXTRA_ALREADY_CALLED extra.)
-        }
-
-        // For now, SIP calls will be processed directly without a
-        // NEW_OUTGOING_CALL broadcast.
-        //
-        // TODO: In the future, though, 3rd party apps *should* be allowed to
-        // intercept outgoing calls to SIP addresses as well.  To do this, we should
-        // (1) update the NEW_OUTGOING_CALL intent documentation to explain this
-        // case, and (2) pass the outgoing SIP address by *not* overloading the
-        // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold
-        // the outgoing SIP address.  (Be sure to document whether it's a URI or just
-        // a plain address, whether it could be a tel: URI, etc.)
-        Uri uri = intent.getData();
-        String scheme = uri.getScheme();
-        if (PhoneAccount.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) {
-            Log.i(TAG, "The requested number was detected as SIP call.");
-            startSipCallOptionHandler(this, intent, uri, number);
-            finish();
-            return;
-
-            // TODO: if there's ever a way for SIP calls to trigger a
-            // "callNow=true" case (see above), we'll need to handle that
-            // case here too (most likely by just doing nothing at all.)
-        }
-
-        Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
-        if (number != null) {
-            broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
-        }
-        CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);
-        broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
-        broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString());
-        // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app
-        // to intercept the outgoing call.
-        broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + ".");
-
-        // Set a timer so that we can prepare for unexpected delay introduced by the broadcast.
-        // If it takes too much time, the timer will show "waiting" spinner.
-        // This message will be removed when OutgoingCallReceiver#onReceive() is called before the
-        // timeout.
-        mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
-                OUTGOING_CALL_TIMEOUT_THRESHOLD);
-        sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.SYSTEM,
-                android.Manifest.permission.PROCESS_OUTGOING_CALLS,
-                AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
-                new OutgoingCallReceiver(),
-                null,  // scheduler
-                Activity.RESULT_OK,  // initialCode
-                number,  // initialData: initial value for the result data
-                null);  // initialExtras
-    }
-
-    @Override
-    protected void onStop() {
-        // Clean up (and dismiss if necessary) any managed dialogs.
-        //
-        // We don't do this in onPause() since we can be paused/resumed
-        // due to orientation changes (in which case we don't want to
-        // disturb the dialog), but we *do* need it here in onStop() to be
-        // sure we clean up if the user hits HOME while the dialog is up.
-        //
-        // Note it's safe to call removeDialog() even if there's no dialog
-        // associated with that ID.
-        removeDialog(DIALOG_NOT_VOICE_CAPABLE);
-
-        super.onStop();
-    }
-
-    /**
-     * Handle the specified CALL or CALL_* intent on a non-voice-capable
-     * device.
-     *
-     * This method may launch a different intent (if there's some useful
-     * alternative action to take), or otherwise display an error dialog,
-     * and in either case will finish() the current activity when done.
-     */
-    private void handleNonVoiceCapable(Intent intent) {
-        if (DBG) Log.v(TAG, "handleNonVoiceCapable: handling " + intent
-                       + " on non-voice-capable device...");
-
-        // Just show a generic "voice calling not supported" dialog.
-        showDialog(DIALOG_NOT_VOICE_CAPABLE);
-        // ...and we'll eventually finish() when the user dismisses
-        // or cancels the dialog.
-    }
-
-    @Override
-    protected Dialog onCreateDialog(int id) {
-        Dialog dialog;
-        switch(id) {
-            case DIALOG_NOT_VOICE_CAPABLE:
-                dialog = new AlertDialog.Builder(this)
-                        .setTitle(R.string.not_voice_capable)
-                        .setIconAttribute(android.R.attr.alertDialogIcon)
-                        .setPositiveButton(android.R.string.ok, this)
-                        .setOnCancelListener(this)
-                        .create();
-                break;
-            default:
-                Log.w(TAG, "onCreateDialog: unexpected ID " + id);
-                dialog = null;
-                break;
-        }
-        return dialog;
-    }
-
-    /** DialogInterface.OnClickListener implementation */
-    @Override
-    public void onClick(DialogInterface dialog, int id) {
-        // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far
-        // at least), and its only button is "OK".
-        finish();
-    }
-
-    /** DialogInterface.OnCancelListener implementation */
-    @Override
-    public void onCancel(DialogInterface dialog) {
-        // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far
-        // at least), and canceling it is just like hitting "OK".
-        finish();
-    }
-
-    /**
-     * Implement onConfigurationChanged() purely for debugging purposes,
-     * to make sure that the android:configChanges element in our manifest
-     * is working properly.
-     */
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (DBG) Log.v(TAG, "onConfigurationChanged: newConfig = " + newConfig);
-    }
-}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 98b94cc..2d0b99d 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -134,9 +134,6 @@
 
     private static PhoneGlobals sMe;
 
-    // A few important fields we expose to the rest of the package
-    // directly (rather than thru set/get methods) for efficiency.
-    CallController callController;
     CallManager mCM;
     CallNotifier notifier;
     CallerInfoCache callerInfoCache;
@@ -326,11 +323,6 @@
 
             callGatewayManager = CallGatewayManager.getInstance();
 
-            // Create the CallController singleton, which is the interface
-            // to the telephony layer for user-initiated telephony functionality
-            // (like making outgoing calls.)
-            callController = CallController.init(this, callLogger, callGatewayManager);
-
             // Create the CallerInfoCache singleton, which remembers custom ring tone and
             // send-to-voicemail settings.
             //
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 8127eae..f10a609 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -70,6 +70,10 @@
 import android.telephony.UiccSlotInfo;
 import android.telephony.UssdResponse;
 import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsMmTelFeature;
+import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IImsRegistration;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -77,9 +81,6 @@
 import android.util.Slog;
 
 import com.android.ims.ImsManager;
-import com.android.ims.internal.IImsMMTelFeature;
-import com.android.ims.internal.IImsRcsFeature;
-import com.android.ims.internal.IImsRegistration;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.CallStateException;
@@ -183,6 +184,12 @@
     private static final int CMD_SWITCH_SLOTS = 50;
     private static final int EVENT_SWITCH_SLOTS_DONE = 51;
 
+    // Parameters of select command.
+    private static final int SELECT_COMMAND = 0xA4;
+    private static final int SELECT_P1 = 0x04;
+    private static final int SELECT_P2 = 0;
+    private static final int SELECT_P3 = 0x10;
+
     /** The singleton instance. */
     private static PhoneInterfaceManager sInstance;
 
@@ -2419,10 +2426,23 @@
     }
 
     @Override
-    public String iccTransmitApduBasicChannel(int subId, int cla, int command, int p1, int p2,
-                int p3, String data) {
+    public String iccTransmitApduBasicChannel(int subId, String callingPackage, int cla,
+            int command, int p1, int p2, int p3, String data) {
         enforceModifyPermissionOrCarrierPrivilege(subId);
 
+        if (command == SELECT_COMMAND && p1 == SELECT_P1 && p2 == SELECT_P2 && p3 == SELECT_P3
+                && TextUtils.equals(ISDR_AID, data)) {
+            // Only allows LPA to select ISD-R.
+            mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+            ComponentInfo bestComponent =
+                    EuiccConnector.findBestComponent(mPhone.getContext().getPackageManager());
+            if (bestComponent == null
+                    || !TextUtils.equals(callingPackage, bestComponent.packageName)) {
+                loge("The calling package is not allowed to select ISD-R.");
+                throw new SecurityException("The calling package is not allowed to select ISD-R.");
+            }
+        }
+
         if (DBG) {
             log("iccTransmitApduBasicChannel: subId=" + subId + " cla=" + cla + " cmd=" + command
                     + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
@@ -2596,26 +2616,32 @@
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id for the MMTel
-     * feature or {@link null} if the service is not available. If the feature is available, the
-     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
+     * status updates, if not already enabled.
      */
-    public IImsMMTelFeature getMMTelFeatureAndListen(int slotId,
-            IImsServiceFeatureCallback callback) {
+    public void enableIms(int slotId) {
         enforceModifyPermission();
-        return PhoneFactory.getImsResolver().getMMTelFeatureAndListen(slotId, callback);
+        PhoneFactory.getImsResolver().enableIms(slotId);
     }
 
     /**
-     * Returns the {@link IImsMMTelFeature} that corresponds to the given slot Id for the MMTel
-     * feature during emergency calling or {@link null} if the service is not available. If the
-     * feature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
-     * listener for feature updates.
+     * Disables IMS for the framework. This will trigger IMS de-registration and trigger ImsFeature
+     * status updates to disabled.
      */
-    public IImsMMTelFeature getEmergencyMMTelFeatureAndListen(int slotId,
+    public void disableIms(int slotId) {
+        enforceModifyPermission();
+        PhoneFactory.getImsResolver().disableIms(slotId);
+    }
+
+    /**
+     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id for the MMTel
+     * feature or {@link null} if the service is not available. If the feature is available, the
+     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
+     */
+    public IImsMmTelFeature getMmTelFeatureAndListen(int slotId,
             IImsServiceFeatureCallback callback) {
         enforceModifyPermission();
-        return PhoneFactory.getImsResolver().getEmergencyMMTelFeatureAndListen(slotId, callback);
+        return PhoneFactory.getImsResolver().getMmTelFeatureAndListen(slotId, callback);
     }
 
     /**
@@ -2638,6 +2664,15 @@
         return PhoneFactory.getImsResolver().getImsRegistration(slotId, feature);
     }
 
+    /**
+     * Returns the {@link IImsConfig} structure associated with the slotId and feature
+     * specified.
+     */
+    public IImsConfig getImsConfig(int slotId, int feature) throws RemoteException {
+        enforceModifyPermission();
+        return PhoneFactory.getImsResolver().getImsConfig(slotId, feature);
+    }
+
     public void setImsRegistrationState(boolean registered) {
         enforceModifyPermission();
         mPhone.setImsRegistrationState(registered);
@@ -4000,7 +4035,8 @@
 
             }
 
-            infos[i] = new UiccSlotInfo(slot.isActive(), slot.isEuicc(), cardId, cardState);
+            infos[i] = new UiccSlotInfo(
+                    slot.isActive(), slot.isEuicc(), cardId, cardState, slot.getPhoneId());
         }
         return infos;
     }
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 50e7f0a..a153cf7 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -32,7 +32,6 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
@@ -61,7 +60,6 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyCapabilities;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.sip.SipPhone;
 import com.android.phone.CallGatewayManager.RawGatewayInfo;
 
@@ -1117,54 +1115,6 @@
     }
 
     /**
-     * Given an Intent (which is presumably the ACTION_CALL intent that
-     * initiated this outgoing call), figure out the actual phone number we
-     * should dial.
-     *
-     * Note that the returned "number" may actually be a SIP address,
-     * if the specified intent contains a sip: URI.
-     *
-     * This method is basically a wrapper around PhoneUtils.getNumberFromIntent(),
-     * except it's also aware of the EXTRA_ACTUAL_NUMBER_TO_DIAL extra.
-     * (That extra, if present, tells us the exact string to pass down to the
-     * telephony layer.  It's guaranteed to be safe to dial: it's either a PSTN
-     * phone number with separators and keypad letters stripped out, or a raw
-     * unencoded SIP address.)
-     *
-     * @return the phone number corresponding to the specified Intent, or null
-     *   if the Intent has no action or if the intent's data is malformed or
-     *   missing.
-     *
-     * @throws VoiceMailNumberMissingException if the intent
-     *   contains a "voicemail" URI, but there's no voicemail
-     *   number configured on the device.
-     */
-    public static String getInitialNumber(Intent intent)
-            throws PhoneUtils.VoiceMailNumberMissingException {
-        if (DBG) log("getInitialNumber(): " + intent);
-
-        String action = intent.getAction();
-        if (TextUtils.isEmpty(action)) {
-            return null;
-        }
-
-        // If the EXTRA_ACTUAL_NUMBER_TO_DIAL extra is present, get the phone
-        // number from there.  (That extra takes precedence over the actual data
-        // included in the intent.)
-        if (intent.hasExtra(OutgoingCallBroadcaster.EXTRA_ACTUAL_NUMBER_TO_DIAL)) {
-            String actualNumberToDial =
-                    intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_ACTUAL_NUMBER_TO_DIAL);
-            if (DBG) {
-                log("==> got EXTRA_ACTUAL_NUMBER_TO_DIAL; returning '"
-                        + toLogSafePhoneNumber(actualNumberToDial) + "'");
-            }
-            return actualNumberToDial;
-        }
-
-        return getNumberFromIntent(PhoneGlobals.getInstance(), intent);
-    }
-
-    /**
      * Gets the phone number to be called from an intent.  Requires a Context
      * to access the contacts database, and a Phone to access the voicemail
      * number.
diff --git a/src/com/android/phone/ProcessOutgoingCallTest.java b/src/com/android/phone/ProcessOutgoingCallTest.java
deleted file mode 100644
index c76fb43..0000000
--- a/src/com/android/phone/ProcessOutgoingCallTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.app.SearchManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.util.Log;
-
-/**
- * ProcessOutgoingCallTest tests {@link OutgoingCallBroadcaster} by performing
- * a couple of simple modifications to outgoing calls, and by printing log
- * messages for each call.
- */
-public class ProcessOutgoingCallTest extends BroadcastReceiver {
-    private static final String TAG = "ProcessOutgoingCallTest";
-    private static final String AREACODE = "617";
-
-    private static final boolean LOGV = false;
-
-    private static final boolean REDIRECT_411_TO_GOOG411 = true;
-    private static final boolean SEVEN_DIGIT_DIALING = true;
-    private static final boolean POUND_POUND_SEARCH = true;
-    private static final boolean BLOCK_555 = true;
-
-    public void onReceive(Context context, Intent intent) {
-        if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
-            String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
-            if (LOGV) Log.v(TAG, "Received intent " + intent + " (number = " + number + ".");
-            /* Example of how to redirect calls from one number to another. */
-            if (REDIRECT_411_TO_GOOG411 && number.equals("411")) {
-                setResultData("18004664411");
-            }
-
-            /* Example of how to modify the phone number in flight. */
-            if (SEVEN_DIGIT_DIALING && number.length() == 7) {
-                setResultData(AREACODE + number);
-            }
-
-            /* Example of how to route a call to another Application. */
-            if (POUND_POUND_SEARCH && number.startsWith("##")) {
-                Intent newIntent = new Intent(Intent.ACTION_SEARCH);
-                newIntent.putExtra(SearchManager.QUERY, number.substring(2));
-                newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                context.startActivity(newIntent);
-                setResultData(null);
-            }
-
-            /* Example of how to deny calls to a particular number.
-             * Note that no UI is displayed to the user -- the call simply 
-             * does not happen.  It is the application's responaibility to
-             * explain this to the user. */
-            int length = number.length();
-            if (BLOCK_555 && length >= 7) {
-                String exchange = number.substring(length - 7, length - 4);
-                Log.v(TAG, "exchange = " + exchange);
-                if (exchange.equals("555")) {
-                    setResultData(null);
-                }
-            }
-        }
-    }
-}
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index e626a53..354c4bb 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -23,7 +23,7 @@
 public class TimeConsumingPreferenceActivity extends PreferenceActivity
                         implements TimeConsumingPreferenceListener,
                         DialogInterface.OnCancelListener {
-    private static final String LOG_TAG = "TimeConsumingPreferenceActivity";
+    private static final String LOG_TAG = "TimeConsumingPrefActivity";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private class DismissOnClickListener implements DialogInterface.OnClickListener {
@@ -155,8 +155,7 @@
     @Override
     public void onStarted(Preference preference, boolean reading) {
         if (DBG) dumpState();
-        if (DBG) Log.d(LOG_TAG, "onStarted, preference=" + preference.getKey()
-                + ", reading=" + reading);
+        Log.i(LOG_TAG, "onStarted, preference=" + preference.getKey() + ", reading=" + reading);
         mBusyList.add(preference.getKey());
 
         if (mIsForeground) {
@@ -172,8 +171,7 @@
     @Override
     public void onFinished(Preference preference, boolean reading) {
         if (DBG) dumpState();
-        if (DBG) Log.d(LOG_TAG, "onFinished, preference=" + preference.getKey()
-                + ", reading=" + reading);
+        Log.i(LOG_TAG, "onFinished, preference=" + preference.getKey() + ", reading=" + reading);
         mBusyList.remove(preference.getKey());
 
         if (mBusyList.isEmpty()) {
@@ -189,7 +187,7 @@
     @Override
     public void onError(Preference preference, int error) {
         if (DBG) dumpState();
-        if (DBG) Log.d(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
+        Log.i(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
 
         if (mIsForeground) {
             showDialog(error);
diff --git a/src/com/android/phone/settings/fdn/EditPinPreference.java b/src/com/android/phone/settings/fdn/EditPinPreference.java
index eaa3507..42fc418 100644
--- a/src/com/android/phone/settings/fdn/EditPinPreference.java
+++ b/src/com/android/phone/settings/fdn/EditPinPreference.java
@@ -39,7 +39,17 @@
 
     private boolean shouldHideButtons;
 
-    interface OnPinEnteredListener {
+    /**
+     * Interface definition for a callback to be invoked when the PIN is entered.
+     */
+    public interface OnPinEnteredListener {
+        /**
+         * Called when the dialog of {@link #EditPinPreference} is dismissed.
+         *
+         * @param preference the specified {@link #EditPinPreference}
+         * @param positiveResult Whether the positive button was clicked (true), or
+         *                       the negative button was clicked or the dialog was canceled (false).
+         */
         void onPinEntered(EditPinPreference preference, boolean positiveResult);
     }
 
diff --git a/src/com/android/services/telephony/EmergencyTonePlayer.java b/src/com/android/services/telephony/EmergencyTonePlayer.java
index aaec24f..a21489d 100644
--- a/src/com/android/services/telephony/EmergencyTonePlayer.java
+++ b/src/com/android/services/telephony/EmergencyTonePlayer.java
@@ -67,11 +67,7 @@
                 startVibrate();
                 break;
             case EMERGENCY_TONE_ALERT:
-                // Only start if we are not in silent mode.
-                int ringerMode = mAudioManager.getRingerMode();
-                if (ringerMode == AudioManager.RINGER_MODE_NORMAL) {
-                    startAlert();
-                }
+                startAlert();
                 break;
             case EMERGENCY_TONE_OFF:
                 // nothing;
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index ca547fa..23485d1 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -81,12 +81,6 @@
             }
         }
 
-        // For GSM connections, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN should be applied whenever
-        // PROPERTY_IS_DOWNGRADED_CONFERENCE is true.
-        if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
-            capabilities |= CAPABILITY_CONFERENCE_HAS_NO_CHILDREN;
-        }
-
         return capabilities;
     }
 
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 8fe31af..3cf420a 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -891,7 +891,6 @@
                         mConferenceHost.isOutgoingCall());
                 // This is a newly created conference connection as a result of SRVCC
                 c.setConferenceSupported(true);
-                c.addCapability(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
                 c.setConnectionProperties(
                         c.getConnectionProperties() | Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
                 c.updateState();
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 3f21d17..17c6049 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -227,6 +227,13 @@
                         isHandoverFromSupported);
             }
 
+            final boolean isTelephonyAudioDeviceSupported = mContext.getResources().getBoolean(
+                    R.bool.config_support_telephony_audio_device);
+            if (isTelephonyAudioDeviceSupported && !isEmergency
+                    && isCarrierUseCallRecordingTone()) {
+                extras.putBoolean(PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, true);
+            }
+
             boolean isDeviceRttSupported = mContext.getResources().getBoolean(
                     R.bool.config_support_rtt);
             if (isDeviceRttSupported && isCarrierRttSupported()) {
@@ -444,6 +451,18 @@
         }
 
         /**
+         * Determines from carrier config whether the carrier requires the use of a call recording
+         * tone.
+         *
+         * @return {@code true} if a call recording tone should be used, {@code false} otherwise.
+         */
+        private boolean isCarrierUseCallRecordingTone() {
+            PersistableBundle b =
+                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            return b.getBoolean(CarrierConfigManager.KEY_PLAY_CALL_RECORDING_TONE_BOOL);
+        }
+
+        /**
          * Where a device supports instant lettering and call subjects, retrieves the necessary
          * PhoneAccount extras for those features.
          * @return The {@link PhoneAccount} extras associated with the current subscription.
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 2358160..67e9739 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -22,6 +22,7 @@
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.telecom.CallAudioState;
@@ -33,12 +34,14 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsCallProfile;
+import android.text.TextUtils;
 import android.util.Pair;
 
 import com.android.ims.ImsCall;
-import com.android.ims.ImsCallProfile;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallFailCause;
 import com.android.internal.telephony.CallStateException;
@@ -94,7 +97,7 @@
     private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
     private static final int MSG_HANGUP = 17;
 
-    private final Handler mHandler = new Handler() {
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -168,10 +171,7 @@
                                         new ArrayList(Arrays.asList(mSsNotification.history)));
                                 putExtras(lastForwardedNumber);
                             }
-                            if (mSsNotification.code
-                                    == SuppServiceNotification.MO_CODE_CALL_FORWARDED) {
-                                sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
-                            }
+                            handleSuppServiceNotification(mSsNotification);
                         }
                     }
                     break;
@@ -254,6 +254,115 @@
     };
 
     /**
+     * Handles {@link SuppServiceNotification}s pertinent to Telephony.
+     * @param ssn the notification.
+     */
+    private void handleSuppServiceNotification(SuppServiceNotification ssn) {
+        Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType,
+                ssn.code);
+        if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
+                && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
+            sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
+        }
+        sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
+    }
+
+    /**
+     * Sends a supplementary service notification connection event.
+     * This connection event includes the type and code, as well as a human readable message which
+     * is suitable for display to the user if the UI chooses to do so.
+     * @param type the {@link SuppServiceNotification#type}.
+     * @param code the {@link SuppServiceNotification#code}.
+     */
+    private void sendSuppServiceNotificationEvent(int type, int code) {
+        Bundle extras = new Bundle();
+        extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
+        extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
+        extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
+                getSuppServiceMessage(type, code));
+        sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
+    }
+
+    /**
+     * Retrieves a human-readable message for a supplementary service notification.
+     * This message is suitable for display to the user.
+     * @param type the code group.
+     * @param code the code.
+     * @return A {@link CharSequence} containing the message, or {@code null} if none defined.
+     */
+    private CharSequence getSuppServiceMessage(int type, int code) {
+        int messageId = -1;
+        if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) {
+            switch (code) {
+                case SuppServiceNotification.CODE_1_CALL_DEFLECTED:
+                    messageId = R.string.supp_service_notification_call_deflected;
+                    break;
+                case SuppServiceNotification.CODE_1_CALL_FORWARDED:
+                    messageId = R.string.supp_service_notification_call_forwarded;
+                    break;
+                case SuppServiceNotification.CODE_1_CALL_IS_WAITING:
+                    messageId = R.string.supp_service_notification_call_waiting;
+                    break;
+                case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED:
+                    messageId = R.string.supp_service_clir_suppression_rejected;
+                    break;
+                case SuppServiceNotification.CODE_1_CUG_CALL:
+                    messageId = R.string.supp_service_closed_user_group_call;
+                    break;
+                case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED:
+                    messageId = R.string.supp_service_incoming_calls_barred;
+                    break;
+                case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED:
+                    messageId = R.string.supp_service_outgoing_calls_barred;
+                    break;
+                case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE:
+                    // Intentional fall through.
+                case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE:
+                    messageId = R.string.supp_service_call_forwarding_active;
+                    break;
+            }
+        } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) {
+            switch (code) {
+                case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED:
+                    messageId = R.string.supp_service_additional_call_forwarded;
+                    break;
+                case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT:
+                    messageId = R.string.supp_service_additional_ect_connected;
+                    break;
+                case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT:
+                    messageId = R.string.supp_service_additional_ect_connecting;
+                    break;
+                case SuppServiceNotification.CODE_2_CALL_ON_HOLD:
+                    messageId = R.string.supp_service_call_on_hold;
+                    break;
+                case SuppServiceNotification.CODE_2_CALL_RETRIEVED:
+                    messageId = R.string.supp_service_call_resumed;
+                    break;
+                case SuppServiceNotification.CODE_2_CUG_CALL:
+                    messageId = R.string.supp_service_closed_user_group_call;
+                    break;
+                case SuppServiceNotification.CODE_2_DEFLECTED_CALL:
+                    messageId = R.string.supp_service_deflected_call;
+                    break;
+                case SuppServiceNotification.CODE_2_FORWARDED_CALL:
+                    messageId = R.string.supp_service_forwarded_call;
+                    break;
+                case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL:
+                    messageId = R.string.supp_service_conference_call;
+                    break;
+                case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED:
+                    messageId = R.string.supp_service_held_call_released;
+                    break;
+            }
+        }
+        if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
+            return getPhone().getContext().getText(messageId);
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
      */
     public boolean isCarrierVideoConferencingSupported() {
@@ -443,6 +552,13 @@
                 sendRttInitiationFailure(status);
             }
         }
+
+        @Override
+        public void onDisconnect(int cause) {
+            Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(),
+                    DisconnectCause.toString(cause));
+            mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget();
+        }
     };
 
     protected com.android.internal.telephony.Connection mOriginalConnection;
@@ -630,6 +746,45 @@
     }
 
     @Override
+    public void onDeflect(Uri address) {
+        Log.v(this, "onDeflect");
+        if (mOriginalConnection != null && isValidRingingCall()) {
+            if (address == null) {
+                Log.w(this, "call deflect address uri is null");
+                return;
+            }
+            String scheme = address.getScheme();
+            String deflectNumber = "";
+            String uriString = address.getSchemeSpecificPart();
+            if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
+                if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
+                    Log.w(this, "onDeflect, address scheme is not of type tel instead: " +
+                            scheme);
+                    return;
+                }
+                if (PhoneNumberUtils.isUriNumber(uriString)) {
+                    Log.w(this, "Invalid deflect address. Not a legal PSTN number.");
+                    return;
+                }
+                deflectNumber = PhoneNumberUtils.convertAndStrip(uriString);
+                if (TextUtils.isEmpty(deflectNumber)) {
+                    Log.w(this, "Empty deflect number obtained from address uri");
+                    return;
+                }
+            } else {
+                Log.w(this, "Cannot deflect to voicemail uri");
+                return;
+            }
+
+            try {
+                mOriginalConnection.deflect(deflectNumber);
+            } catch (CallStateException e) {
+                Log.e(this, e, "Failed to deflect call.");
+            }
+        }
+    }
+
+    @Override
     public void onReject() {
         Log.v(this, "onReject");
         if (isValidRingingCall()) {
@@ -801,6 +956,8 @@
         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
                 isExternalConnection() && isPullable());
         newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
+        newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
+                isImsConnection() && canDeflectImsCalls());
 
         if (getConnectionCapabilities() != newCapabilities) {
             setConnectionCapabilities(newCapabilities);
@@ -889,7 +1046,6 @@
         getPhone().registerForHandoverStateChanged(
                 mHandler, MSG_HANDOVER_STATE_CHANGED, null);
         getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
-        getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);
         getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
         getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
         getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
@@ -1040,6 +1196,8 @@
                 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO);
         boolean canVideoCallsBeHdAudio =
                 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO);
+        boolean canGsmCdmaCallsBeHdAudio =
+                b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO);
         boolean shouldDisplayHdAudio =
                 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL);
 
@@ -1047,6 +1205,10 @@
             return false;
         }
 
+        if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) {
+            return false;
+        }
+
         if (isVideoCall && !canVideoCallsBeHdAudio) {
             return false;
         }
@@ -1073,6 +1235,17 @@
         return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
     }
 
+    private boolean canDeflectImsCalls() {
+        PersistableBundle b = getCarrierConfig();
+        // Return false if the CarrierConfig is unavailable
+        if (b != null) {
+            return b.getBoolean(
+                    CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) &&
+                    isValidRingingCall();
+        }
+        return false;
+    }
+
     /**
      * Determines if the device will respect the value of the
      * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
@@ -1329,7 +1502,8 @@
             newState = mOriginalConnection.getState();
         }
         int cause = mOriginalConnection.getDisconnectCause();
-        Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState, this);
+        Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState,
+                getTelecomCallId());
 
         if (mConnectionState != newState) {
             mConnectionState = newState;
@@ -1712,6 +1886,25 @@
     }
 
     /**
+     * Whether the original connection is an GSM/CDMA connection.
+     * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false}
+     *     otherwise.
+     */
+    protected boolean isGsmCdmaConnection() {
+        Phone phone = getPhone();
+        if (phone != null) {
+            switch (phone.getPhoneType()) {
+                case PhoneConstants.PHONE_TYPE_GSM:
+                case PhoneConstants.PHONE_TYPE_CDMA:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Whether the original connection was ever an IMS connection, either before or now.
      * @return {@code True} if the original connection was ever an IMS connection, {@code false}
      *     otherwise.
diff --git a/testapps/ImsTestService/Android.mk b/testapps/ImsTestService/Android.mk
new file mode 100644
index 0000000..6afb3d5
--- /dev/null
+++ b/testapps/ImsTestService/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    android-support-v4 \
+    android-support-v7-appcompat \
+    android-support-v7-recyclerview \
+    android-support-v7-cardview
+
+LOCAL_USE_AAPT2 := true
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := ImsTestApp
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/ImsTestService/AndroidManifest.xml b/testapps/ImsTestService/AndroidManifest.xml
new file mode 100644
index 0000000..4d81ffd
--- /dev/null
+++ b/testapps/ImsTestService/AndroidManifest.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          coreApp="true"
+          package="com.android.phone.testapps.imstestapp">
+    <application
+        android:label="ImsTestService"
+        android:directBootAware="true">
+        <activity
+            android:name=".ImsTestServiceApp"
+            android:label="ImsTestService">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".ImsRegistrationActivity" android:label="IMS Registration" />
+        <activity android:name=".ImsCallingActivity" android:label="IMS Calling" />
+        <activity android:name=".ImsConfigActivity" android:label="IMS Config" />
+
+        <service android:name=".TestImsService"
+                 android:exported="true"
+                 android:enabled="true"
+                 android:persistent="true"
+                 android:permission="android.permission.BIND_IMS_SERVICE">
+            <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true"/>
+            <!--meta-data android:name="android.telephony.ims.EMERGENCY_MMTEL_FEATURE"
+                       android:value="true" /-->
+            <intent-filter>
+                <action android:name="android.telephony.ims.ImsService" />
+            </intent-filter>
+        </service>
+    </application>
+</manifest>
+
diff --git a/testapps/ImsTestService/res/layout/activity_calling.xml b/testapps/ImsTestService/res/layout/activity_calling.xml
new file mode 100644
index 0000000..c1d6993
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/activity_calling.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <GridLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:columnCount="2" android:rowCount="2">
+
+        <CheckBox
+            android:id="@+id/call_cap_voice"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_column="0"
+            android:layout_gravity="center" android:layout_row="0"
+            android:text="@string/calling_cap_voice"/>
+        <CheckBox
+            android:id="@+id/call_cap_video"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_column="1"
+            android:layout_gravity="center" android:layout_row="0"
+            android:text="@string/calling_cap_video"/>
+        <CheckBox
+            android:id="@+id/call_cap_ut"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_column="0"
+            android:layout_gravity="center" android:layout_row="1"
+            android:text="@string/calling_cap_ut"/>
+        <CheckBox
+            android:id="@+id/call_cap_sms"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_column="1"
+            android:layout_gravity="center" android:layout_row="1"
+            android:text="@string/calling_cap_sms"/>
+    </GridLayout>
+    <Button
+        android:id="@+id/call_cap_change"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/calling_cap_change_button" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider"
+        android:paddingRight="4dp"/>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" android:text="@string/calling_cap_enabled_text"
+        android:textStyle="bold"/>
+    <TextView
+        android:id="@+id/call_cap_enabled_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" android:text=""/>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/activity_config.xml b/testapps/ImsTestService/res/layout/activity_config.xml
new file mode 100644
index 0000000..636bc2d
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/activity_config.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <TextView
+        android:id="@+id/textView2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textStyle="bold"
+        android:text="Set Configuration Value"/>
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="4dp">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:text="Item"/>
+
+        <EditText
+            android:id="@+id/set_config_item"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:inputType="number" android:text="0"/>
+    </LinearLayout>
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="4dp">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:text="Value"/>
+
+        <EditText
+            android:id="@+id/set_config_value"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:inputType="number" android:text="0"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/config_button"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingRight="4dp"
+        android:text="Set"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textStyle="bold"
+        android:text="Cached Config Values"/>
+
+    <ListView
+        android:id="@+id/config_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/activity_main.xml b/testapps/ImsTestService/res/layout/activity_main.xml
new file mode 100644
index 0000000..a45bd40
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/activity_main.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@+id/features_main_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/features_main_text"/>
+        <CheckBox
+            android:id="@+id/features_check_mmtel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/features_check_mmtel"/>
+        <CheckBox
+            android:id="@+id/features_check_rcs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/features_check_rcs"/>
+        <Button
+            android:id="@+id/features_change_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/features_change_button"/>
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="ImsService Controls"/>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <Button
+                android:id="@+id/control_launch_reg"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingRight="4dp"
+                android:text="Registration"/>
+            <Button
+                android:id="@+id/control_launch_calling"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingRight="4dp"
+                android:text="Calling"/>
+            <Button
+                android:id="@+id/control_launch_config"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:paddingRight="4dp"
+                android:text="Config"/>
+        </LinearLayout>
+    </LinearLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/connections_list"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/activity_registration.xml b/testapps/ImsTestService/res/layout/activity_registration.xml
new file mode 100644
index 0000000..2e381eb
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/activity_registration.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="4dp">
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:paddingLeft="4dp">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/reg_text_reg_tech"/>
+        <Spinner
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/reg_tech_selector"/>
+    </LinearLayout>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/reg_registered_button"
+        android:id="@+id/reg_registered_button"/>
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/reg_registering_button"
+        android:id="@+id/reg_registering_button"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <include layout="@layout/construct_imsreasoninfo"
+             android:id="@+id/deregistered_imsreasoninfo" />
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/reg_deregistered_button"
+        android:id="@+id/reg_deregistered_button"/>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height= "1dp"
+        android:paddingRight="4dp"
+        android:background="?android:attr/listDivider" />
+
+    <include layout="@layout/construct_imsreasoninfo"
+             android:id="@+id/regchangefail_imsreasoninfo" />
+    <Button
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/reg_changefailed_button"
+        android:id="@+id/reg_changefailed_button"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/config_item.xml b/testapps/ImsTestService/res/layout/config_item.xml
new file mode 100644
index 0000000..796a741
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/config_item.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/configItem"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" android:text="Item" android:textAlignment="viewStart"/>
+
+    <TextView
+        android:id="@+id/configValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1" android:text="Value"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/construct_imsreasoninfo.xml b/testapps/ImsTestService/res/layout/construct_imsreasoninfo.xml
new file mode 100644
index 0000000..dd32ca3
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/construct_imsreasoninfo.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/imsreasoninfo_text"/>
+        
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:maxLines="1" android:minEms="2" android:paddingRight="4dp"
+            android:text="@string/imsreasoninfo_errorcode"/>
+        <EditText
+            android:id="@+id/imsreasoninfo_error"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:ems="3"
+            android:inputType="number" android:text="0"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:autoText="true" android:maxLines="1" android:paddingRight="4dp"
+            android:text="@string/imsreasoninfo_extracode"/>
+        <EditText
+            android:id="@+id/imsreasoninfo_extra"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:ems="3"
+            android:inputType="number" android:text="0"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1" android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="0"
+            android:paddingRight="4dp" android:text="@string/imsreasoninfo_message"/>
+        <EditText
+            android:id="@+id/imsreasoninfo_message"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:ems="10"
+            android:inputType="text"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/layout/ims_connection.xml b/testapps/ImsTestService/res/layout/ims_connection.xml
new file mode 100644
index 0000000..4c08082
--- /dev/null
+++ b/testapps/ImsTestService/res/layout/ims_connection.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<android.support.v7.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    android:layout_gravity="center"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:id="@+id/connection_info"
+    card_view:cardCornerRadius="4dp"
+    card_view:cardElevation="4dp"
+    card_view:cardUseCompatPadding="true">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical" >
+        <TextView
+            android:id="@+id/reg_main_text"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" android:text="@string/features_main_text"/>
+        <CheckBox
+            android:id="@+id/reg_check_mmtel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" android:text="@string/features_check_mmtel"/>
+        <CheckBox
+            android:id="@+id/reg_check_rcs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" android:text="@string/features_check_rcs"/>
+        </LinearLayout>
+</android.support.v7.widget.CardView>
\ No newline at end of file
diff --git a/testapps/ImsTestService/res/values/donottranslate_strings.xml b/testapps/ImsTestService/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..68da69e
--- /dev/null
+++ b/testapps/ImsTestService/res/values/donottranslate_strings.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <string name="features_main_text">Registration Configuration</string>
+    <string name="features_check_mmtel">MmTel Feature</string>
+    <string name="features_check_rcs">RCS Feature</string>
+    <string name="features_change_button">Change Features</string>
+
+    <string name="reg_text_reg_tech">Registration Technology</string>
+    <string name="reg_registered_button">Send Registered</string>
+    <string name="reg_registering_button">Send Registering</string>
+    <string name="reg_deregistered_button">Send Deregistered Using Reason Above</string>
+    <string name="reg_changefailed_button">Reg Change Failed Using Reason Above</string>
+
+    <string name="imsreasoninfo_text">Construct ImsReasonInfo</string>
+    <string name="imsreasoninfo_errorcode">Error Code</string>
+    <string name="imsreasoninfo_extracode">Extra Code</string>
+    <string name="imsreasoninfo_message">Message</string>
+
+    <string name="calling_cap_voice">Voice</string>
+    <string name="calling_cap_video">Video</string>
+    <string name="calling_cap_ut">UT</string>
+    <string name="calling_cap_sms">SMS</string>
+    <string name="calling_cap_change_button">Change Capability Status</string>
+    <string name="calling_cap_enabled_text">Enabled Capabilities</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
new file mode 100644
index 0000000..48944e4
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsCallingActivity.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.SparseArray;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class ImsCallingActivity extends Activity {
+
+    //Capabilities available by service
+    private CheckBox mCapVoiceAvailBox;
+    private CheckBox mCapVideoAvailBox;
+    private CheckBox mCapUtAvailBox;
+    private CheckBox mCapSmsAvailBox;
+
+    private TextView mCapEnabledText;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_calling);
+
+        TestMmTelFeatureImpl.getInstance().initialize(this, 0);
+
+        mCapVoiceAvailBox = findViewById(R.id.call_cap_voice);
+        mCapVideoAvailBox = findViewById(R.id.call_cap_video);
+        mCapUtAvailBox = findViewById(R.id.call_cap_ut);
+        mCapSmsAvailBox = findViewById(R.id.call_cap_sms);
+        mCapEnabledText = findViewById(R.id.call_cap_enabled_text);
+        Button capChangedButton = findViewById(R.id.call_cap_change);
+        capChangedButton.setOnClickListener((v) -> onCapabilitiesChangedClicked());
+
+        TestMmTelFeatureImpl.getInstance().addUpdateCallback(
+                new TestMmTelFeatureImpl.MmTelUpdateCallback() {
+                    @Override
+                    void onEnabledCapabilityChanged() {
+                        mmTelCapabilityChanged();
+                    }
+                });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mmTelCapabilityChanged();
+    }
+
+    private void mmTelCapabilityChanged() {
+        SparseArray<MmTelFeature.MmTelCapabilities> caps =
+                TestMmTelFeatureImpl.getInstance().getEnabledCapabilities();
+        StringBuilder sb = new StringBuilder("LTE: ");
+        sb.append("{");
+        sb.append(caps.get(ImsRegistrationImplBase.REGISTRATION_TECH_LTE));
+        sb.append("}, \nIWLAN: ");
+        sb.append("{");
+        sb.append(caps.get(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN));
+        sb.append("}");
+        mCapEnabledText.setText(sb.toString());
+    }
+
+    private void onCapabilitiesChangedClicked() {
+        if (!isFrameworkConnected()) {
+            return;
+        }
+        boolean isVoiceAvail = mCapVoiceAvailBox.isChecked();
+        boolean isVideoAvail = mCapVideoAvailBox.isChecked();
+        boolean isUtAvail = mCapUtAvailBox.isChecked();
+        // Not used yet
+        boolean isSmsAvail = mCapSmsAvailBox.isChecked();
+
+        MmTelFeature.MmTelCapabilities capabilities = new MmTelFeature.MmTelCapabilities();
+        if (isVoiceAvail) {
+            capabilities.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
+        }
+        if (isVideoAvail) {
+            capabilities.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+        }
+        if (isUtAvail) {
+            capabilities.addCapabilities(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+        }
+        TestMmTelFeatureImpl.getInstance().sendCapabilitiesUpdate(capabilities);
+    }
+
+    private boolean isFrameworkConnected() {
+        if (!TestMmTelFeatureImpl.getInstance().isReady()) {
+            Toast.makeText(this, "Connection to Framework Unavailable",
+                    Toast.LENGTH_SHORT).show();
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsConfigActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsConfigActivity.java
new file mode 100644
index 0000000..1f8e1c3
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsConfigActivity.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class ImsConfigActivity extends Activity {
+
+    private static final String PREFIX_ITEM = "Item: ";
+    private static final String PREFIX_VALUE = "Value: ";
+
+    public static class ConfigItemAdapter extends ArrayAdapter<TestImsConfigImpl.ConfigItem> {
+        public ConfigItemAdapter(Context context, ArrayList<TestImsConfigImpl.ConfigItem> configs) {
+            super(context, 0, configs);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TestImsConfigImpl.ConfigItem configItem = getItem(position);
+
+            if (convertView == null) {
+                convertView = LayoutInflater.from(getContext()).inflate(R.layout.config_item,
+                        parent, false);
+            }
+
+            TextView textItem = (TextView) convertView.findViewById(R.id.configItem);
+            TextView textValue = (TextView) convertView.findViewById(R.id.configValue);
+
+            textItem.setText(PREFIX_ITEM + configItem.item);
+            if (configItem.valueString != null) {
+                textValue.setText(PREFIX_VALUE + configItem.valueString);
+            } else {
+                textValue.setText(PREFIX_VALUE + configItem.value);
+            }
+
+            return convertView;
+        }
+    }
+
+    private final TestImsConfigImpl.ImsConfigListener mConfigListener =
+            new TestImsConfigImpl.ImsConfigListener() {
+                @Override
+                public void notifyConfigChanged() {
+                    Log.i("ImsConfigActivity", "notifyConfigChanged");
+                    mConfigItemAdapter.notifyDataSetChanged();
+                }
+            };
+
+    ConfigItemAdapter mConfigItemAdapter;
+    ListView mListView;
+
+    EditText mConfigItemText;
+    EditText mConfigValueText;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_config);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        mConfigItemAdapter = new ConfigItemAdapter(this,
+                TestImsConfigImpl.getInstance().getConfigList());
+
+        mListView = (ListView) findViewById(R.id.config_list);
+        mListView.setAdapter(mConfigItemAdapter);
+
+        TestImsConfigImpl.getInstance().setConfigListener(mConfigListener);
+
+        Button setConfigButton = findViewById(R.id.config_button);
+        setConfigButton.setOnClickListener((v) -> onSetConfigClicked());
+
+        mConfigItemText = findViewById(R.id.set_config_item);
+        mConfigValueText = findViewById(R.id.set_config_value);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        TestImsConfigImpl.getInstance().setConfigListener(null);
+    }
+
+    private void onSetConfigClicked() {
+        String configItem = mConfigItemText.getText().toString();
+        String configValue = mConfigValueText.getText().toString();
+        TestImsConfigImpl.getInstance().setConfigValue(Integer.parseInt(configItem),
+                Integer.parseInt(configValue));
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
new file mode 100644
index 0000000..7e27371
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArrayMap;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Toast;
+import android.telephony.ims.ImsReasonInfo;
+
+import java.util.Map;
+
+public class ImsRegistrationActivity extends Activity {
+
+    private int mSelectedRegTech = ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+
+    private static final Map<String, Integer> REG_TECH = new ArrayMap<>(2);
+    static {
+        REG_TECH.put("LTE", ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        REG_TECH.put("IWLAN", ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
+    }
+
+    private View mDeregisteredReason;
+    private View mRegChangeFailedReason;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_registration);
+
+        //Set up registration tech spinner
+        Spinner regTechDropDown = findViewById(R.id.reg_tech_selector);
+        regTechDropDown.setAdapter(new ArrayAdapter<>(this,
+                android.R.layout.simple_spinner_dropdown_item,
+                REG_TECH.keySet().toArray(new String[REG_TECH.size()])));
+        regTechDropDown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                onTechDropDownChanged((String) parent.getItemAtPosition(position));
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // Don't change selection
+            }
+        });
+
+        // Map buttons to onClick listeners
+        Button registeredButton = findViewById(R.id.reg_registered_button);
+        registeredButton.setOnClickListener((v)->onRegisteredClicked());
+        Button registeringButton = findViewById(R.id.reg_registering_button);
+        registeringButton.setOnClickListener((v)->onRegisteringClicked());
+        Button deregisteredButton = findViewById(R.id.reg_deregistered_button);
+        deregisteredButton.setOnClickListener((v)->onDeregisteredClicked());
+        Button regChangeFailedButton = findViewById(R.id.reg_changefailed_button);
+        regChangeFailedButton.setOnClickListener((v)->onRegChangeFailedClicked());
+
+        mDeregisteredReason = findViewById(R.id.deregistered_imsreasoninfo);
+        mRegChangeFailedReason = findViewById(R.id.regchangefail_imsreasoninfo);
+    }
+
+    private void onRegisteredClicked() {
+        if (!isFrameworkConnected()) {
+            return;
+        }
+        TestImsRegistrationImpl.getInstance().onRegistered(mSelectedRegTech);
+    }
+
+    private void onRegisteringClicked() {
+        if (!isFrameworkConnected()) {
+            return;
+        }
+        TestImsRegistrationImpl.getInstance().onRegistering(mSelectedRegTech);
+    }
+
+    private void onDeregisteredClicked() {
+        if (!isFrameworkConnected()) {
+            return;
+        }
+        TestImsRegistrationImpl.getInstance().onDeregistered(getReasonInfo(mDeregisteredReason));
+    }
+
+    private void onRegChangeFailedClicked() {
+        if (!isFrameworkConnected()) {
+            return;
+        }
+        TestImsRegistrationImpl.getInstance().onTechnologyChangeFailed(mSelectedRegTech,
+                getReasonInfo(mRegChangeFailedReason));
+    }
+
+    private void onTechDropDownChanged(String item) {
+        mSelectedRegTech = REG_TECH.get(item);
+    }
+
+    private ImsReasonInfo getReasonInfo(View reasonView) {
+        EditText errorCodeText = reasonView.findViewById(R.id.imsreasoninfo_error);
+        EditText extraCodeText = reasonView.findViewById(R.id.imsreasoninfo_extra);
+        EditText messageText = reasonView.findViewById(R.id.imsreasoninfo_message);
+
+        int errorCode = ImsReasonInfo.CODE_UNSPECIFIED;
+        try {
+            errorCode = Integer.parseInt(errorCodeText.getText().toString());
+        } catch (NumberFormatException e) {
+            Toast.makeText(this, "Couldn't parse reason, defaulting to Unspecified.",
+                    Toast.LENGTH_SHORT).show();
+        }
+
+        int extraCode = ImsReasonInfo.CODE_UNSPECIFIED;
+        try {
+            extraCode = Integer.parseInt(extraCodeText.getText().toString());
+        } catch (NumberFormatException e) {
+            Toast.makeText(this, "Couldn't parse reason, defaulting to Unspecified.",
+                    Toast.LENGTH_SHORT).show();
+        }
+
+        String message = messageText.getText().toString();
+
+        ImsReasonInfo result = new ImsReasonInfo(errorCode, extraCode, message);
+        Toast.makeText(this, "getReasonInfo: " + result, Toast.LENGTH_SHORT).show();
+        return result;
+    }
+
+    private boolean isFrameworkConnected() {
+        if (TestImsRegistrationImpl.getInstance() == null) {
+            Toast.makeText(this, "Connection to Framework Unavailable!",
+                    Toast.LENGTH_LONG).show();
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsTestServiceApp.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsTestServiceApp.java
new file mode 100644
index 0000000..6b78a30
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsTestServiceApp.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+/**
+ * Main activity for Test ImsService Application.
+ */
+
+public class ImsTestServiceApp extends Activity {
+
+    private LinearLayout mConnections;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_main);
+
+        ((Button) findViewById(R.id.control_launch_reg)).setOnClickListener(
+                (view) -> launchRegistrationActivity());
+
+        ((Button) findViewById(R.id.control_launch_calling)).setOnClickListener(
+                (view) -> launchCallingActivity());
+
+        ((Button) findViewById(R.id.control_launch_config)).setOnClickListener(
+                (view) -> launchConfigActivity());
+
+        // Adds Card view for testing
+        mConnections = findViewById(R.id.connections_list);
+        mConnections.addView(getLayoutInflater().inflate(R.layout.ims_connection, null, false));
+    }
+
+    private void launchRegistrationActivity() {
+        Intent intent = new Intent(this, ImsRegistrationActivity.class);
+        startActivity(intent);
+    }
+
+    private void launchCallingActivity() {
+        Intent intent = new Intent(this, ImsCallingActivity.class);
+        startActivity(intent);
+    }
+
+    private void launchConfigActivity() {
+        Intent intent = new Intent(this, ImsConfigActivity.class);
+        startActivity(intent);
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsConfigImpl.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsConfigImpl.java
new file mode 100644
index 0000000..4b8842a
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsConfigImpl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.telephony.ims.stub.ImsConfigImplBase;
+
+import com.android.ims.ImsConfig;
+
+import java.util.ArrayList;
+
+public class TestImsConfigImpl extends ImsConfigImplBase {
+
+    public static class ConfigItem {
+        public int item;
+        public int value;
+        public String valueString;
+
+        public ConfigItem(int item, int value) {
+            this.item = item;
+            this.value = value;
+        }
+
+        public ConfigItem(int item, String value) {
+            this.item = item;
+            valueString = value;
+        }
+    }
+
+    public interface ImsConfigListener {
+        void notifyConfigChanged();
+    }
+
+    private static TestImsConfigImpl sTestImsConfigImpl;
+    private ImsConfigListener mListener;
+    private ArrayList<ConfigItem> mArrayOfConfigs = new ArrayList<>();
+
+    public static TestImsConfigImpl getInstance() {
+        if (sTestImsConfigImpl == null) {
+            sTestImsConfigImpl = new TestImsConfigImpl();
+        }
+        return sTestImsConfigImpl;
+    }
+
+    public void setConfigListener(ImsConfigListener listener) {
+        mListener = listener;
+    }
+
+    public ArrayList<ConfigItem> getConfigList() {
+        return mArrayOfConfigs;
+    }
+
+    @Override
+    public int setConfig(int item, int value) {
+        replaceConfig(new ConfigItem(item, value));
+        return ImsConfig.OperationStatusConstants.SUCCESS;
+    }
+
+    @Override
+    public int setConfig(int item, String value) {
+        replaceConfig(new ConfigItem(item, value));
+        return ImsConfig.OperationStatusConstants.SUCCESS;
+    }
+
+    @Override
+    public int getConfigInt(int item) {
+        replaceConfig(new ConfigItem(item, ImsConfig.FeatureValueConstants.ON));
+        return ImsConfig.FeatureValueConstants.ON;
+    }
+
+    @Override
+    public String getConfigString(int item) {
+        return null;
+    }
+
+    public void setConfigValue(int item, int value) {
+        replaceConfig(new ConfigItem(item, value));
+        notifyProvisionedValueChanged(item, value);
+    }
+
+    public void replaceConfig(ConfigItem configItem) {
+        ConfigItem config = mArrayOfConfigs.stream()
+                .filter(configElem -> configElem.item == configItem.item)
+                .findFirst().orElse(null);
+        if (config != null) {
+            mArrayOfConfigs.remove(config);
+        }
+        mArrayOfConfigs.add(configItem);
+        if (mListener != null) {
+            mListener.notifyConfigChanged();
+        }
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsRegistrationImpl.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsRegistrationImpl.java
new file mode 100644
index 0000000..50bf5d4
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsRegistrationImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+public class TestImsRegistrationImpl extends ImsRegistrationImplBase {
+
+    private static TestImsRegistrationImpl sInstance;
+
+    public static TestImsRegistrationImpl getInstance() {
+        if (sInstance == null) {
+            sInstance = new TestImsRegistrationImpl();
+        }
+        return sInstance;
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java
new file mode 100644
index 0000000..434cdb5
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestImsService.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.telephony.ims.ImsService;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+/**
+ * Creates a test ImsService, which is used for testing framework IMS.
+ */
+
+public class TestImsService extends ImsService {
+
+    public static final String LOG_TAG = "ImsTestApp";
+
+    public static TestImsService mInstance;
+
+    public TestImsRegistrationImpl mImsRegistration;
+    public TestMmTelFeatureImpl mTestMmTelFeature;
+    public TestRcsFeatureImpl mTestRcsFeature;
+    public TestImsConfigImpl mTestImsConfig;
+
+    public static TestImsService getInstance() {
+        return mInstance;
+    }
+
+    @Override
+    public void onCreate() {
+        Log.i(LOG_TAG, "TestImsService: onCreate");
+        mImsRegistration = TestImsRegistrationImpl.getInstance();
+        mTestMmTelFeature = TestMmTelFeatureImpl.getInstance();
+        mTestRcsFeature = new TestRcsFeatureImpl();
+        mTestImsConfig = TestImsConfigImpl.getInstance();
+
+        mInstance = this;
+    }
+
+    @Override
+    public MmTelFeature createMmTelFeature(int slotId) {
+        Log.i(LOG_TAG, "TestImsService: onCreateEmergencyMMTelImsFeature");
+        return mTestMmTelFeature;
+    }
+
+    @Override
+    public RcsFeature createRcsFeature(int slotId) {
+        return mTestRcsFeature;
+    }
+
+    @Override
+    public ImsRegistrationImplBase getRegistration(int slotId) {
+        Log.i(LOG_TAG, "TestImsService: getRegistration");
+        return mImsRegistration;
+    }
+
+    @Override
+    public ImsConfigImplBase getConfig(int slotId) {
+        return mTestImsConfig;
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestMmTelFeatureImpl.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestMmTelFeatureImpl.java
new file mode 100644
index 0000000..2a0463d
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestMmTelFeatureImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.telephony.ims.feature.CapabilityChangeRequest;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.widget.Toast;
+
+import java.util.Set;
+
+public class TestMmTelFeatureImpl extends MmTelFeature {
+
+    public static TestMmTelFeatureImpl sTestMmTelFeatureImpl;
+    private boolean mIsReady = false;
+    // Enabled Capabilities - not status
+    private SparseArray<MmTelCapabilities> mEnabledCapabilities = new SparseArray<>();
+    private final Set<MmTelUpdateCallback> mCallbacks = new ArraySet<>();
+
+    static class MmTelUpdateCallback {
+        void onEnabledCapabilityChanged() {
+        }
+    }
+
+    public TestMmTelFeatureImpl() {
+        mEnabledCapabilities.append(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                new MmTelCapabilities());
+        mEnabledCapabilities.append(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
+                new MmTelCapabilities());
+        setFeatureState(STATE_READY);
+    }
+
+    public static TestMmTelFeatureImpl getInstance() {
+        if (sTestMmTelFeatureImpl == null) {
+            sTestMmTelFeatureImpl = new TestMmTelFeatureImpl();
+        }
+        return sTestMmTelFeatureImpl;
+    }
+
+    public void addUpdateCallback(MmTelUpdateCallback callback) {
+        mCallbacks.add(callback);
+    }
+
+    public boolean isReady() {
+        return mIsReady;
+    }
+
+    @Override
+    public boolean queryCapabilityConfiguration(int capability, int radioTech) {
+        return mEnabledCapabilities.get(radioTech).isCapable(capability);
+    }
+
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        for (CapabilityChangeRequest.CapabilityPair pair : request.getCapabilitiesToEnable()) {
+            mEnabledCapabilities.get(pair.getRadioTech()).addCapabilities(pair.getCapability());
+        }
+        for (CapabilityChangeRequest.CapabilityPair pair : request.getCapabilitiesToDisable()) {
+            mEnabledCapabilities.get(pair.getRadioTech()).removeCapabilities(pair.getCapability());
+        }
+        mCallbacks.forEach(callback->callback.onEnabledCapabilityChanged());
+    }
+
+    @Override
+    public void onFeatureRemoved() {
+        super.onFeatureRemoved();
+    }
+
+    public void sendCapabilitiesUpdate(MmTelFeature.MmTelCapabilities c) {
+        Toast.makeText(mContext, "Sending Capabilities:{" + c + "}",
+                Toast.LENGTH_LONG).show();
+
+        notifyCapabilitiesStatusChanged(c);
+    }
+
+    public SparseArray<MmTelCapabilities> getEnabledCapabilities() {
+        return mEnabledCapabilities;
+    }
+
+    @Override
+    public void onFeatureReady() {
+        mIsReady = true;
+    }
+}
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestRcsFeatureImpl.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestRcsFeatureImpl.java
new file mode 100644
index 0000000..996a569
--- /dev/null
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/TestRcsFeatureImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.imstestapp;
+
+import android.telephony.ims.feature.RcsFeature;
+
+public class TestRcsFeatureImpl extends RcsFeature {
+
+    private boolean mIsReady = false;
+
+
+    public boolean isReady() {
+        return mIsReady;
+    }
+
+    @Override
+    public void onFeatureReady() {
+        mIsReady = true;
+    }
+}
diff --git a/testapps/TelephonyRegistryTestApp/Android.mk b/testapps/TelephonyRegistryTestApp/Android.mk
new file mode 100644
index 0000000..ed1f2a3
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+src_dirs := src
+res_dirs := res
+
+LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
+
+LOCAL_PACKAGE_NAME := TelephonyRegistryTestApp
+
+LOCAL_CERTIFICATE := platform
+LOCAL_MODULE_TAGS := tests
+#LOCAL_MODULE_TAGS := debug
+
+include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..5f19509
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.phone.testapps.telephonyregistry">
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <application android:label="TelephonyRegistryTestApp">
+        <activity
+            android:name=".TelephonyRegistryTestApp"
+            android:label="TelephonyRegistryTestApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
diff --git a/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml b/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..405efe2
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/res/layout/activity_main.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:id="@+id/events">
+        </LinearLayout>
+    </ScrollView>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/queryCellLocationButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_row="0"
+            android:layout_column="0"
+            android:text="@string/query_cell_location_button" />
+        <Button
+            android:id="@+id/registerButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_row="0"
+            android:layout_column="0"
+            android:text="@string/register_button" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml b/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..897b83e
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<resources>
+    <string name="register_button">Register</string>
+    <string name="query_cell_location_button">Query</string>
+</resources>
\ No newline at end of file
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
new file mode 100644
index 0000000..e423619
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.phone.testapps.telephonyregistry;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.telephony.CellInfo;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.util.SparseArray;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TelephonyRegistryTestApp extends Activity {
+    private TelephonyManager telephonyManager;
+    private NotificationManager notificationManager;
+    private int mSelectedEvents = 0;
+    private static final String NOTIFICATION_CHANNEL = "registryUpdate";
+
+    private static final SparseArray<String> EVENTS = new SparseArray<String>() {{
+        put(PhoneStateListener.LISTEN_SERVICE_STATE, "SERVICE_STATE");
+        put(PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR, "MESSAGE_WAITING_INDICATOR");
+        put(PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR, "CALL_FORWARDING_INDICATOR");
+        put(PhoneStateListener.LISTEN_CELL_LOCATION, "CELL_LOCATION");
+        put(PhoneStateListener.LISTEN_CALL_STATE, "CALL_STATE");
+        put(PhoneStateListener.LISTEN_DATA_CONNECTION_STATE, "DATA_CONNECTION_STATE");
+        put(PhoneStateListener.LISTEN_DATA_ACTIVITY, "DATA_ACTIVITY");
+        put(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS, "SIGNAL_STRENGTHS");
+        put(PhoneStateListener.LISTEN_OTASP_CHANGED, "OTASP_CHANGED");
+        put(PhoneStateListener.LISTEN_CELL_INFO, "CELL_INFO");
+        put(PhoneStateListener.LISTEN_PRECISE_CALL_STATE, "PRECISE_CALL_STATE");
+        put(PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
+                "PRECISE_DATA_CONNECTION_STATE");
+        put(PhoneStateListener.LISTEN_VOLTE_STATE, "VOLTE_STATE");
+        put(PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE, "CARRIER_NETWORK_CHANGE");
+        put(PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE, "VOICE_ACTIVATION_STATE");
+        put(PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE, "DATA_ACTIVATION_STATE");
+    }};
+
+    private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onCellLocationChanged(CellLocation location) {
+            notify("onCellLocationChanged", location);
+        }
+
+        @Override
+        public void onCellInfoChanged(List<CellInfo> cellInfo) {
+            notify("onCellInfoChanged", cellInfo);
+        }
+
+        private void notify(String method, Object data) {
+            Notification.Builder builder = new Notification.Builder(TelephonyRegistryTestApp.this,
+                    NOTIFICATION_CHANNEL);
+            Notification notification = builder.setSmallIcon(android.R.drawable.sym_def_app_icon)
+                    .setContentTitle("Registry update: " + method)
+                    .setContentText(data == null ? "null" : data.toString())
+                    .build();
+            notificationManager.notify(0, notification);
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        telephonyManager = getSystemService(TelephonyManager.class);
+
+        LinearLayout eventContainer = (LinearLayout) findViewById(R.id.events);
+        for (int i = 0; i < EVENTS.size(); i++) {
+            CheckBox box = new CheckBox(this);
+            box.setText(EVENTS.valueAt(i));
+            final int eventCode = EVENTS.keyAt(i);
+            box.setOnCheckedChangeListener((buttonView, isChecked) -> {
+                if (buttonView.isChecked()) {
+                    mSelectedEvents |= eventCode;
+                } else {
+                    mSelectedEvents &= ~eventCode;
+                }
+            });
+            eventContainer.addView(box);
+        }
+
+        Button registerButton = (Button) findViewById(R.id.registerButton);
+        registerButton.setOnClickListener(v ->
+                telephonyManager.listen(phoneStateListener, mSelectedEvents));
+
+        Button queryCellLocationButton = findViewById(R.id.queryCellLocationButton);
+        queryCellLocationButton.setOnClickListener(v -> {
+            List<CellInfo> cellInfos = telephonyManager.getAllCellInfo();
+            String cellInfoText;
+            if (cellInfos == null || cellInfos.size() == 0) {
+                cellInfoText = "null";
+            } else {
+                cellInfoText = cellInfos.stream().map(CellInfo::toString)
+                        .collect(Collectors.joining(","));
+            }
+            Toast.makeText(TelephonyRegistryTestApp.this, "queryCellInfo: " + cellInfoText,
+                    Toast.LENGTH_SHORT).show();
+        });
+
+        notificationManager = getSystemService(NotificationManager.class);
+        NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL,
+                "Telephony Registry updates", NotificationManager.IMPORTANCE_HIGH);
+        notificationManager.createNotificationChannel(channel);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index eb8c48a..b4c93ee 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,13 +16,29 @@
 
 package com.android.services.telephony;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
 import android.telecom.DisconnectCause;
 import android.telecom.TelecomManager;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -30,29 +46,18 @@
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 /**
  * Unit tests for TelephonyConnectionService.
  */
@@ -60,6 +65,7 @@
 @RunWith(AndroidJUnit4.class)
 public class TelephonyConnectionServiceTest extends TelephonyTestBase {
 
+    private static final long TIMEOUT_MS = 100;
     private static final int SLOT_0_PHONE_ID = 0;
     private static final int SLOT_1_PHONE_ID = 1;
 
@@ -752,6 +758,58 @@
         }
     }
 
+    @Test
+    @SmallTest
+    public void testSuppServiceNotification() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+
+        // We need to set the original connection to cause the supp service notification
+        // registration to occur.
+        Phone phone = c.getPhone();
+        c.setOriginalConnection(c.getOriginalConnection());
+
+        // When the registration occurs, we'll capture the handler and message so we can post our
+        // own messages to it.
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Integer> messageCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(phone).registerForSuppServiceNotification(handlerCaptor.capture(),
+                messageCaptor.capture(), any());
+        Handler handler = handlerCaptor.getValue();
+        int message = messageCaptor.getValue();
+
+        // With the handler and message now known, we'll post a supp service notification.
+        AsyncResult result = getSuppServiceNotification(
+                SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+                SuppServiceNotification.CODE_1_CALL_FORWARDED);
+        handler.obtainMessage(message, result).sendToTarget();
+        waitForHandlerAction(handler, TIMEOUT_MS);
+
+        assertTrue(c.getLastConnectionEvents().contains(TelephonyManager.EVENT_CALL_FORWARDED));
+
+        // With the handler and message now known, we'll post a supp service notification.
+        result = getSuppServiceNotification(
+                SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+                SuppServiceNotification.CODE_1_CALL_IS_WAITING);
+        handler.obtainMessage(message, result).sendToTarget();
+        waitForHandlerAction(handler, TIMEOUT_MS);
+
+        // We we want the 3rd event since the forwarding one above sends 2.
+        assertEquals(c.getLastConnectionEvents().get(2),
+                TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION);
+        Bundle extras = c.getLastConnectionEventExtras().get(2);
+        assertEquals(SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+                extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE));
+        assertEquals(SuppServiceNotification.CODE_1_CALL_IS_WAITING,
+                extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
+    }
+
+    private AsyncResult getSuppServiceNotification(int notificationType, int code) {
+        SuppServiceNotification notification = new SuppServiceNotification();
+        notification.notificationType = notificationType;
+        notification.code = code;
+        return new AsyncResult(null, notification, null);
+    }
+
     private Phone makeTestPhone(int phoneId, int serviceState, boolean isEmergencyOnly) {
         Phone phone = mock(Phone.class);
         ServiceState testServiceState = new ServiceState();
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index ea0f965..9040257 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -16,17 +16,25 @@
 
 package com.android.services.telephony;
 
+import android.content.Context;
+import android.os.Bundle;
 import android.telecom.PhoneAccountHandle;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Mock Telephony Connection used in TelephonyConferenceController.java for testing purpose
  */
@@ -39,8 +47,13 @@
     @Mock
     Call mMockCall;
 
+    @Mock
+    Context mMockContext;
+
     private Phone mMockPhone;
     private int mNotifyPhoneAccountChangedCount = 0;
+    private List<String> mLastConnectionEvents = new ArrayList<>();
+    private List<Bundle> mLastConnectionEventExtras = new ArrayList<>();
 
     @Override
     public com.android.internal.telephony.Connection getOriginalConnection() {
@@ -52,11 +65,17 @@
         MockitoAnnotations.initMocks(this);
 
         mMockPhone = mock(Phone.class);
+        mMockContext = mock(Context.class);
         // Set up mMockRadioConnection and mMockPhone to contain an active call
         when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
         when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
+        doNothing().when(mMockRadioConnection).addListener(any(Connection.Listener.class));
+        doNothing().when(mMockRadioConnection).addPostDialListener(
+                any(Connection.PostDialListener.class));
         when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
+        when(mMockPhone.getContext()).thenReturn(null);
         when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
+        when(mMockCall.getPhone()).thenReturn(mMockPhone);
     }
 
     @Override
@@ -82,7 +101,21 @@
         mNotifyPhoneAccountChangedCount++;
     }
 
+    @Override
+    public void sendConnectionEvent(String event, Bundle extras) {
+        mLastConnectionEvents.add(event);
+        mLastConnectionEventExtras.add(extras);
+    }
+
     public int getNotifyPhoneAccountChangedCount() {
         return mNotifyPhoneAccountChangedCount;
     }
+
+    public List<String> getLastConnectionEvents() {
+        return mLastConnectionEvents;
+    }
+
+    public List<Bundle> getLastConnectionEventExtras() {
+        return mLastConnectionEventExtras;
+    }
 }