Fixing Google Voice to work with new incallui

We were not previously storing and sending the gateway information to
the UI.  This change consolidates Call gateway functionality from
InCallUiState and PhoneUtils into a new class CallGatewayManager.  It is
responsible for storing a mapping from the call's connection to the
gateway used to make that connection.  It also adds gateway packagename
and number to the Call object sent to the UI so that it can show UI
appropriately.

bug: 10393622
Change-Id: Ic5a0b068475bcab60e8cc96470273e36005ccc2e
diff --git a/src/com/android/phone/CallCard.java b/src/com/android/phone/CallCard.java
index fcef7f3..22215c0 100644
--- a/src/com/android/phone/CallCard.java
+++ b/src/com/android/phone/CallCard.java
@@ -101,14 +101,6 @@
     /** Secondary "call info" block (the background "on hold" call) */
     private ViewStub mSecondaryCallInfo;
 
-    /**
-     * Container for both provider info and call state. This will take care of showing/hiding
-     * animation for those views.
-     */
-    private ViewGroup mSecondaryInfoContainer;
-    private ViewGroup mProviderInfo;
-    private TextView mProviderLabel;
-    private TextView mProviderAddress;
 
     // "Call state" widgets
     private TextView mCallStateLabel;
@@ -217,10 +209,6 @@
         mPrimaryCallInfo = (ViewGroup) findViewById(R.id.primary_call_info);
         mPrimaryCallBanner = (ViewGroup) findViewById(R.id.primary_call_banner);
 
-        mSecondaryInfoContainer = (ViewGroup) findViewById(R.id.secondary_info_container);
-        mProviderInfo = (ViewGroup) findViewById(R.id.providerInfo);
-        mProviderLabel = (TextView) findViewById(R.id.providerLabel);
-        mProviderAddress = (TextView) findViewById(R.id.providerAddress);
         mCallStateLabel = (TextView) findViewById(R.id.callStateLabel);
         mElapsedTime = (TextView) findViewById(R.id.elapsedTime);
 
@@ -418,8 +406,6 @@
     private void updateAlreadyDisconnected(CallManager cm) {
         // For the foreground call, we manually set up every component based on previous state.
         mPrimaryCallInfo.setVisibility(View.VISIBLE);
-        mSecondaryInfoContainer.setLayoutTransition(null);
-        mProviderInfo.setVisibility(View.GONE);
         mCallStateLabel.setVisibility(View.VISIBLE);
         mCallStateLabel.setText(mContext.getString(R.string.card_title_call_ended));
         mElapsedTime.setVisibility(View.VISIBLE);
@@ -829,8 +815,7 @@
         final InCallUiState inCallUiState = mApplication.inCallUiState;
         if (DBG) {
             log("==> callStateLabel: '" + callStateLabel
-                    + "', bluetoothIconId = " + bluetoothIconId
-                    + ", providerInfoVisible = " + inCallUiState.providerInfoVisible);
+                    + "', bluetoothIconId = " + bluetoothIconId);
         }
 
         // Animation will be done by mCallerDetail's LayoutTransition, but in some cases, we don't
@@ -841,22 +826,6 @@
                 || state == Call.State.DISCONNECTING
                 || state == Call.State.DISCONNECTED);
         LayoutTransition layoutTransition = null;
-        if (skipAnimation) {
-            // Evict LayoutTransition object to skip animation.
-            layoutTransition = mSecondaryInfoContainer.getLayoutTransition();
-            mSecondaryInfoContainer.setLayoutTransition(null);
-        }
-
-        if (inCallUiState.providerInfoVisible) {
-            mProviderInfo.setVisibility(View.VISIBLE);
-            mProviderLabel.setText(context.getString(R.string.calling_via_template,
-                    inCallUiState.providerLabel));
-            mProviderAddress.setText(inCallUiState.providerAddress);
-
-            mInCallScreen.requestRemoveProviderInfoWithDelay();
-        } else {
-            mProviderInfo.setVisibility(View.GONE);
-        }
 
         if (!TextUtils.isEmpty(callStateLabel)) {
             mCallStateLabel.setVisibility(View.VISIBLE);
@@ -881,10 +850,6 @@
                 mCallStateLabel.setGravity(Gravity.END);
             }
         }
-        if (skipAnimation) {
-            // Restore LayoutTransition object to recover animation.
-            mSecondaryInfoContainer.setLayoutTransition(layoutTransition);
-        }
 
         // ...and update the elapsed time widget too.
         switch (state) {
diff --git a/src/com/android/phone/CallController.java b/src/com/android/phone/CallController.java
index 7b889de..1b19d9a 100644
--- a/src/com/android/phone/CallController.java
+++ b/src/com/android/phone/CallController.java
@@ -20,6 +20,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyCapabilities;
+import com.android.phone.CallGatewayManager.RawGatewayInfo;
 import com.android.phone.Constants.CallStatusCode;
 import com.android.phone.InCallUiState.InCallScreenMode;
 import com.android.phone.OtaUtils.CdmaOtaScreenState;
@@ -69,9 +70,10 @@
     /** The singleton CallController instance. */
     private static CallController sInstance;
 
-    private PhoneGlobals mApp;
-    private CallManager mCM;
-    private CallLogger mCallLogger;
+    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;
@@ -102,10 +104,11 @@
      * PhoneApp's public "callController" field, which is why there's no
      * getInstance() method here.
      */
-    /* package */ static CallController init(PhoneGlobals app, CallLogger callLogger) {
+    /* package */ static CallController init(PhoneGlobals app, CallLogger callLogger,
+            CallGatewayManager callGatewayManager) {
         synchronized (CallController.class) {
             if (sInstance == null) {
-                sInstance = new CallController(app, callLogger);
+                sInstance = new CallController(app, callLogger, callGatewayManager);
             } else {
                 Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -117,11 +120,13 @@
      * Private constructor (this is a singleton).
      * @see init()
      */
-    private CallController(PhoneGlobals app, CallLogger callLogger) {
+    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
@@ -243,15 +248,6 @@
         // by the PhoneUtils phone state change handler.)
         mApp.setRestoreMuteOnInCallResume(false);
 
-        // If a provider is used, extract the info to build the
-        // overlay and route the call.  The overlay will be
-        // displayed when the InCallScreen becomes visible.
-        if (PhoneUtils.hasPhoneProviderExtras(intent)) {
-            inCallUiState.setProviderInfo(intent);
-        } else {
-            inCallUiState.clearProviderInfo();
-        }
-
         CallStatusCode status = placeCallInternal(intent);
 
         switch (status) {
@@ -477,6 +473,9 @@
         // 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,
@@ -484,7 +483,8 @@
                                               number,
                                               contactUri,
                                               (isEmergencyNumber || isEmergencyIntent),
-                                              inCallUiState.providerGatewayUri);
+                                              rawGatewayInfo,
+                                              mCallGatewayManager);
 
         switch (callStatus) {
             case PhoneUtils.CALL_STATUS_DIALED:
diff --git a/src/com/android/phone/CallGatewayManager.java b/src/com/android/phone/CallGatewayManager.java
new file mode 100644
index 0000000..6fe2444
--- /dev/null
+++ b/src/com/android/phone/CallGatewayManager.java
@@ -0,0 +1,209 @@
+/*
+ * 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.content.Intent;
+import android.net.Uri;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.Connection;
+import com.google.android.collect.Maps;
+
+import java.util.HashMap;
+
+/**
+ * This class manages gateway information for outgoing calls. When calls are made, they may contain
+ * gateway information for services which route phone calls through their own service/numbers.
+ * The data consists of a number to call and the package name of the service. This data is used in
+ * two ways:<br/>
+ * 1. Call the appropriate routing number<br/>
+ * 2. Display information about the routing to the user<br/>
+ *
+ * <p>When an outgoing call is finally placed in PhoneUtils.placeCall, it uses this class to get the
+ * proper number to dial. It also saves an association between the connection object and the gateway
+ * data into this class.  This association is later used in CallModeler when building Call objects
+ * to send to the UI which require the gateway data to show an alert to users.
+ */
+public class CallGatewayManager {
+    private static final String LOG_TAG = CallGatewayManager.class.getSimpleName();
+
+    /**
+     * Intent extra to specify the package name of the gateway
+     * provider.  Used to get the name displayed in the in-call screen
+     * during the call setup. The value is a string.
+     */
+    // TODO: This extra is currently set by the gateway application as
+    // a temporary measure. Ultimately, the framework will securely
+    // set it.
+    /* package */ static final String EXTRA_GATEWAY_PROVIDER_PACKAGE =
+            "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE";
+
+    /**
+     * Intent extra to specify the URI of the provider to place the
+     * call. The value is a string. It holds the gateway address
+     * (phone gateway URL should start with the 'tel:' scheme) that
+     * will actually be contacted to call the number passed in the
+     * intent URL or in the EXTRA_PHONE_NUMBER extra.
+     */
+    // TODO: Should the value be a Uri (Parcelable)? Need to make sure
+    // MMI code '#' don't get confused as URI fragments.
+    /* package */ static final String EXTRA_GATEWAY_URI =
+            "com.android.phone.extra.GATEWAY_URI";
+
+    public static final RawGatewayInfo EMPTY_INFO = new RawGatewayInfo(null, null, null);
+
+    private final HashMap<Connection, RawGatewayInfo> mMap = Maps.newHashMap();
+
+    public CallGatewayManager() {
+    }
+
+    /**
+     * Static method returns an object containing the gateway data stored in the extras of the
+     * Intent parameter.  If no such data exists, returns a Null-Object RawGatewayInfo.
+     * @param intent The intent from which to read gateway data.
+     * @return A populated or empty RawGatewayInfo object.
+     */
+    public static RawGatewayInfo getRawGatewayInfo(Intent intent, String number) {
+        if (hasPhoneProviderExtras(intent)) {
+            return new RawGatewayInfo(intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE),
+                    getProviderGatewayUri(intent), number);
+        }
+        return EMPTY_INFO;
+    }
+
+    /**
+     * This function sets the current mapping from connection to gatewayInfo so that CallModeler
+     * can request this data when creating Call objects.
+     * @param connection The connection object for the placed outgoing call.
+     * @param gatewayInfo Gateway info gathered using getRawGatewayInfo.
+     */
+    public void setGatewayInfoForConnection(Connection connection, RawGatewayInfo gatewayInfo) {
+        if (!gatewayInfo.isEmpty()) {
+            mMap.put(connection, gatewayInfo);
+        } else {
+            mMap.remove(connection);
+        }
+    }
+
+    /**
+     * Clears the gateway information previously stored via setGatewayInfoForConnection.
+     */
+    public void clearGatewayData(Connection connection) {
+        setGatewayInfoForConnection(connection, EMPTY_INFO);
+    }
+
+    /**
+     * If the parameter matches the connection object we previously saved through
+     * setGatewayInfoForConnection, return the associated raw gateway info data. If not, then
+     * return an empty raw gateway info.
+     */
+    public RawGatewayInfo getGatewayInfo(Connection connection) {
+        final RawGatewayInfo info = mMap.get(connection);
+        if (info != null) {
+            return info;
+        }
+
+        return EMPTY_INFO;
+    }
+
+    /**
+     * Check if all the provider's info is present in the intent.
+     * @param intent Expected to have the provider's extra.
+     * @return true if the intent has all the extras to build the
+     * in-call screen's provider info overlay.
+     */
+    public static boolean hasPhoneProviderExtras(Intent intent) {
+        if (null == intent) {
+            return false;
+        }
+        final String name = intent.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE);
+        final String gatewayUri = intent.getStringExtra(EXTRA_GATEWAY_URI);
+
+        return !TextUtils.isEmpty(name) && !TextUtils.isEmpty(gatewayUri);
+    }
+
+    /**
+     * Copy all the expected extras set when a 3rd party provider is
+     * used from the source intent to the destination one.  Checks all
+     * the required extras are present, if any is missing, none will
+     * be copied.
+     * @param src Intent which may contain the provider's extras.
+     * @param dst Intent where a copy of the extras will be added if applicable.
+     */
+    public static void checkAndCopyPhoneProviderExtras(Intent src, Intent dst) {
+        if (!hasPhoneProviderExtras(src)) {
+            Log.d(LOG_TAG, "checkAndCopyPhoneProviderExtras: some or all extras are missing.");
+            return;
+        }
+
+        dst.putExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE,
+                     src.getStringExtra(EXTRA_GATEWAY_PROVIDER_PACKAGE));
+        dst.putExtra(EXTRA_GATEWAY_URI,
+                     src.getStringExtra(EXTRA_GATEWAY_URI));
+    }
+
+    /**
+     * Return the gateway uri from the intent.
+     * @param intent With the gateway uri extra.
+     * @return The gateway URI or null if not found.
+     */
+    public static Uri getProviderGatewayUri(Intent intent) {
+        final String uri = intent.getStringExtra(EXTRA_GATEWAY_URI);
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    /**
+     * Return a formatted version of the uri's scheme specific
+     * part. E.g for 'tel:12345678', return '1-234-5678'.
+     * @param uri A 'tel:' URI with the gateway phone number.
+     * @return the provider's address (from the gateway uri) formatted
+     * for user display. null if uri was null or its scheme was not 'tel:'.
+     */
+    public static String formatProviderUri(Uri uri) {
+        if (uri != null) {
+            if (Constants.SCHEME_TEL.equals(uri.getScheme())) {
+                return PhoneNumberUtils.formatNumber(uri.getSchemeSpecificPart());
+            } else {
+                return uri.toString();
+            }
+        }
+        return null;
+    }
+
+    public static class RawGatewayInfo {
+        public String packageName;
+        public Uri gatewayUri;
+        public String trueNumber;
+
+        public RawGatewayInfo(String packageName, Uri gatewayUri,
+                String trueNumber) {
+            this.packageName = packageName;
+            this.gatewayUri = gatewayUri;
+            this.trueNumber = trueNumber;
+        }
+
+        public String getFormattedGatewayNumber() {
+            return formatProviderUri(gatewayUri);
+        }
+
+        public boolean isEmpty() {
+            return TextUtils.isEmpty(packageName) || gatewayUri == null;
+        }
+    }
+}
diff --git a/src/com/android/phone/CallModeler.java b/src/com/android/phone/CallModeler.java
index 89dcb21..a5e2044 100644
--- a/src/com/android/phone/CallModeler.java
+++ b/src/com/android/phone/CallModeler.java
@@ -35,6 +35,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyCapabilities;
+import com.android.phone.CallGatewayManager.RawGatewayInfo;
 import com.android.services.telephony.common.Call;
 import com.android.services.telephony.common.Call.Capabilities;
 import com.android.services.telephony.common.Call.State;
@@ -84,6 +85,7 @@
 
     private final CallStateMonitor mCallStateMonitor;
     private final CallManager mCallManager;
+    private final CallGatewayManager mCallGatewayManager;
     private final HashMap<Connection, Call> mCallMap = Maps.newHashMap();
     private final HashMap<Connection, Call> mConfCallMap = Maps.newHashMap();
     private final AtomicInteger mNextCallId = new AtomicInteger(CALL_ID_START_VALUE);
@@ -91,10 +93,12 @@
     private RejectWithTextMessageManager mRejectWithTextMessageManager;
 
     public CallModeler(CallStateMonitor callStateMonitor, CallManager callManager,
-            RejectWithTextMessageManager rejectWithTextMessageManager) {
+            RejectWithTextMessageManager rejectWithTextMessageManager,
+            CallGatewayManager callGatewayManager) {
         mCallStateMonitor = callStateMonitor;
         mCallManager = callManager;
         mRejectWithTextMessageManager = rejectWithTextMessageManager;
+        mCallGatewayManager = callGatewayManager;
 
         mCallStateMonitor.addListener(this);
     }
@@ -330,6 +334,29 @@
     }
 
     /**
+     * Sets the new call state onto the call and performs some additional logic
+     * associated with setting the state.
+     */
+    private void setNewState(Call call, int newState, Connection connection) {
+        Preconditions.checkState(call.getState() != newState);
+
+        // When starting an outgoing call, we need to grab gateway information
+        // for the call, if available, and set it.
+        final RawGatewayInfo info = mCallGatewayManager.getGatewayInfo(connection);
+
+        if (newState == Call.State.DIALING) {
+            if (!info.isEmpty()) {
+                call.setGatewayNumber(info.getFormattedGatewayNumber());
+                call.setGatewayPackage(info.packageName);
+            }
+        } else if (!Call.State.isConnected(newState)) {
+            mCallGatewayManager.clearGatewayData(connection);
+        }
+
+        call.setState(newState);
+    }
+
+    /**
      * Updates the Call properties to match the state of the connection object
      * that it represents.
      * @param call The call object to update.
@@ -344,7 +371,7 @@
         final int newState = translateStateFromTelephony(connection, isForConference);
 
         if (call.getState() != newState) {
-            call.setState(newState);
+            setNewState(call, newState, connection);
             changed = true;
         }
 
@@ -362,29 +389,38 @@
         }
 
         if (!isForConference) {
+            // Number
             final String oldNumber = call.getNumber();
-            if (TextUtils.isEmpty(oldNumber) || !oldNumber.equals(connection.getAddress())) {
-                call.setNumber(connection.getAddress());
+            String newNumber = connection.getAddress();
+            RawGatewayInfo info = mCallGatewayManager.getGatewayInfo(connection);
+            if (!info.isEmpty()) {
+                newNumber = info.trueNumber;
+            }
+            if (TextUtils.isEmpty(oldNumber) || !oldNumber.equals(newNumber)) {
+                call.setNumber(newNumber);
                 changed = true;
             }
 
+            // Number presentation
             final int newNumberPresentation = connection.getNumberPresentation();
             if (call.getNumberPresentation() != newNumberPresentation) {
                 call.setNumberPresentation(newNumberPresentation);
                 changed = true;
             }
 
-            final int newCnapNamePresentation = connection.getCnapNamePresentation();
-            if (call.getCnapNamePresentation() != newCnapNamePresentation) {
-                call.setCnapNamePresentation(newCnapNamePresentation);
-                changed = true;
-            }
-
+            // Name
             final String oldCnapName = call.getCnapName();
             if (TextUtils.isEmpty(oldCnapName) || !oldCnapName.equals(connection.getCnapName())) {
                 call.setCnapName(connection.getCnapName());
                 changed = true;
             }
+
+            // Name Presentation
+            final int newCnapNamePresentation = connection.getCnapNamePresentation();
+            if (call.getCnapNamePresentation() != newCnapNamePresentation) {
+                call.setCnapNamePresentation(newCnapNamePresentation);
+                changed = true;
+            }
         } else {
 
             // update the list of children by:
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 31b0c66..de5052f 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -1165,7 +1165,7 @@
                     if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) {
                         // TODO: (Moto): The contact reference data may need to be stored and use
                         // here when redialing a call. For now, pass in NULL as the URI parameter.
-                        PhoneUtils.placeCall(mApplication, phone, number, null, false, null);
+                        PhoneUtils.placeCall(mApplication, phone, number, null, false);
                         mIsCdmaRedialCall = true;
                     } else {
                         mIsCdmaRedialCall = false;
diff --git a/src/com/android/phone/EmergencyCallHelper.java b/src/com/android/phone/EmergencyCallHelper.java
index 7f5b0d2..a23e3e0 100644
--- a/src/com/android/phone/EmergencyCallHelper.java
+++ b/src/com/android/phone/EmergencyCallHelper.java
@@ -392,8 +392,7 @@
                                               mPhone,
                                               mNumber,
                                               null,  // contactUri
-                                              true,  // isEmergencyCall
-                                              null);  // gatewayUri
+                                              true); // isEmergencyCall
         if (DBG) log("- PhoneUtils.placeCall() returned status = " + callStatus);
 
         boolean success;
diff --git a/src/com/android/phone/InCallScreen.java b/src/com/android/phone/InCallScreen.java
index 31f680b..57006bb 100644
--- a/src/com/android/phone/InCallScreen.java
+++ b/src/com/android/phone/InCallScreen.java
@@ -97,29 +97,6 @@
     // TODO: Should be EXTRA_SHOW_DIALPAD for consistency.
     static final String SHOW_DIALPAD_EXTRA = "com.android.phone.ShowDialpad";
 
-    /**
-     * Intent extra to specify the package name of the gateway
-     * provider.  Used to get the name displayed in the in-call screen
-     * during the call setup. The value is a string.
-     */
-    // TODO: This extra is currently set by the gateway application as
-    // a temporary measure. Ultimately, the framework will securely
-    // set it.
-    /* package */ static final String EXTRA_GATEWAY_PROVIDER_PACKAGE =
-            "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE";
-
-    /**
-     * Intent extra to specify the URI of the provider to place the
-     * call. The value is a string. It holds the gateway address
-     * (phone gateway URL should start with the 'tel:' scheme) that
-     * will actually be contacted to call the number passed in the
-     * intent URL or in the EXTRA_PHONE_NUMBER extra.
-     */
-    // TODO: Should the value be a Uri (Parcelable)? Need to make sure
-    // MMI code '#' don't get confused as URI fragments.
-    /* package */ static final String EXTRA_GATEWAY_URI =
-            "com.android.phone.extra.GATEWAY_URI";
-
     // Amount of time (in msec) that we display the "Call ended" state.
     // The "short" value is for calls ended by the local user, and the
     // "long" value is for calls ended by the remote caller.
@@ -371,7 +348,6 @@
                     break;
 
                 case EVENT_HIDE_PROVIDER_INFO:
-                    mApp.inCallUiState.providerInfoVisible = false;
                     if (mCallCard != null) {
                         mCallCard.updateState(mCM);
                     }
@@ -731,11 +707,6 @@
 
         mIsForegroundActivity = false;
 
-        // Force a clear of the provider info frame. Since the
-        // frame is removed using a timed message, it is
-        // possible we missed it if the prev call was interrupted.
-        mApp.inCallUiState.providerInfoVisible = false;
-
         // "show-already-disconnected-state" should be effective just during the first wake-up.
         // We should never allow it to stay true after that.
         mApp.inCallUiState.showAlreadyDisconnectedState = false;
diff --git a/src/com/android/phone/InCallUiState.java b/src/com/android/phone/InCallUiState.java
index 3b700d7..126ef63 100644
--- a/src/com/android/phone/InCallUiState.java
+++ b/src/com/android/phone/InCallUiState.java
@@ -340,52 +340,6 @@
         return (progressIndication != ProgressIndicationType.NONE);
     }
 
-
-    //
-    // (4) Optional info when a 3rd party "provider" is used.
-    //     @see InCallScreen#requestRemoveProviderInfoWithDelay()
-    //     @see CallCard#updateCallStateWidgets()
-    //
-
-    // TODO: maybe isolate all the provider-related stuff out to a
-    //       separate inner class?
-    boolean providerInfoVisible;
-    CharSequence providerLabel;
-    Drawable providerIcon;
-    Uri providerGatewayUri;
-    // The formatted address extracted from mProviderGatewayUri. User visible.
-    String providerAddress;
-
-    /**
-     * Set the fields related to the provider support
-     * based on the specified intent.
-     */
-    public void setProviderInfo(Intent intent) {
-        providerLabel = PhoneUtils.getProviderLabel(mContext, intent);
-        providerIcon = PhoneUtils.getProviderIcon(mContext, intent);
-        providerGatewayUri = PhoneUtils.getProviderGatewayUri(intent);
-        providerAddress = PhoneUtils.formatProviderUri(providerGatewayUri);
-        providerInfoVisible = true;
-
-        // ...but if any of the "required" fields are missing, completely
-        // disable the overlay.
-        if (TextUtils.isEmpty(providerLabel) || providerIcon == null ||
-            providerGatewayUri == null || TextUtils.isEmpty(providerAddress)) {
-            clearProviderInfo();
-        }
-    }
-
-    /**
-     * Clear all the fields related to the provider support.
-     */
-    public void clearProviderInfo() {
-        providerInfoVisible = false;
-        providerLabel = null;
-        providerIcon = null;
-        providerGatewayUri = null;
-        providerAddress = null;
-    }
-
     /**
      * "Call origin" of the most recent phone call.
      *
@@ -435,15 +389,6 @@
             log("  - pending call status code: none");
         }
         log("  - progressIndication: " + progressIndication);
-        if (providerInfoVisible) {
-            log("  - provider info VISIBLE: "
-                  + providerLabel + " / "
-                  + providerIcon  + " / "
-                  + providerGatewayUri + " / "
-                  + providerAddress);
-        } else {
-            log("  - provider info: none");
-        }
         log("  - latestActiveCallOrigin: " + latestActiveCallOrigin);
     }
 
diff --git a/src/com/android/phone/OtaUtils.java b/src/com/android/phone/OtaUtils.java
index a59c2e6..e713df9 100644
--- a/src/com/android/phone/OtaUtils.java
+++ b/src/com/android/phone/OtaUtils.java
@@ -459,9 +459,8 @@
         int callStatus = PhoneUtils.placeCall(context,
                                               phone,
                                               number,
-                                              null,  // contactRef
-                                              false,  //isEmergencyCall
-                                              null);  // gatewayUri
+                                              null,   // contactRef
+                                              false); //isEmergencyCall
 
         if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
             if (DBG) log("  ==> successful return from placeCall(): callStatus = " + callStatus);
diff --git a/src/com/android/phone/OutgoingCallBroadcaster.java b/src/com/android/phone/OutgoingCallBroadcaster.java
index b9aea52..c5e8953 100644
--- a/src/com/android/phone/OutgoingCallBroadcaster.java
+++ b/src/com/android/phone/OutgoingCallBroadcaster.java
@@ -300,7 +300,7 @@
 
         Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
         newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL, number);
-        PhoneUtils.checkAndCopyPhoneProviderExtras(intent, newIntent);
+        CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, newIntent);
 
         // Finally, launch the SipCallOptionHandler, with the copy of the
         // original CALL intent stashed away in the EXTRA_NEW_CALL_INTENT
@@ -638,7 +638,7 @@
         if (number != null) {
             broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
         }
-        PhoneUtils.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);
+        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
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 9ed0470..165ae67 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -171,6 +171,7 @@
     private AudioRouter audioRouter;
     private BluetoothManager bluetoothManager;
     private CallCommandService callCommandService;
+    private CallGatewayManager callGatewayManager;
     private CallHandlerServiceProxy callHandlerServiceProxy;
     private CallModeler callModeler;
     private CallStateMonitor callStateMonitor;
@@ -474,10 +475,13 @@
 
             CallLogger callLogger = new CallLogger(this, new CallLogAsync());
 
+            callGatewayManager = new CallGatewayManager();
+
             // 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);
+            callController = CallController.init(this, callLogger, callGatewayManager);
+
             // ...and also the InCallUiState instance, used by the CallController to
             // keep track of some "persistent state" of the in-call UI.
             inCallUiState = InCallUiState.init(this);
@@ -495,7 +499,8 @@
             rejectWithTextMessageManager = new RejectWithTextMessageManager();
 
             // Creates call models for use with CallHandlerService.
-            callModeler = new CallModeler(callStateMonitor, mCM, rejectWithTextMessageManager);
+            callModeler = new CallModeler(callStateMonitor, mCM, rejectWithTextMessageManager,
+                    callGatewayManager);
 
             // Plays DTMF Tones
             dtmfTonePlayer = new DTMFTonePlayer(mCM, callModeler);
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 9671c17..6b96737 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -59,6 +59,7 @@
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.cdma.CdmaConnection;
 import com.android.internal.telephony.sip.SipPhone;
+import com.android.phone.CallGatewayManager.RawGatewayInfo;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -561,6 +562,15 @@
     }
 
     /**
+     * @see placeCall below
+     */
+    public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
+            boolean isEmergencyCall) {
+        return placeCall(context, phone, number, contactRef, isEmergencyCall,
+                CallGatewayManager.EMPTY_INFO, null);
+    }
+
+    /**
      * Dial the number using the phone passed in.
      *
      * If the connection is establised, this method issues a sync call
@@ -578,12 +588,14 @@
      * emergency call
      * @param gatewayUri Is the address used to setup the connection, null
      * if not using a gateway
+     * @param callGateway Class for setting gateway data on a successful call.
      *
      * @return either CALL_STATUS_DIALED or CALL_STATUS_FAILED
      */
-    public static int placeCall(Context context, Phone phone,
-            String number, Uri contactRef, boolean isEmergencyCall,
-            Uri gatewayUri) {
+    public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
+            boolean isEmergencyCall, RawGatewayInfo gatewayInfo, CallGatewayManager callGateway) {
+        final Uri gatewayUri = gatewayInfo.gatewayUri;
+
         if (VDBG) {
             log("placeCall()... number: '" + number + "'"
                     + ", GW:'" + gatewayUri + "'"
@@ -642,6 +654,11 @@
             // we dialed an MMI (see below).
         }
 
+        // Now that the call is successful, we can save the gateway info for the call
+        if (callGateway != null) {
+            callGateway.setGatewayInfoForConnection(connection, gatewayInfo);
+        }
+
         int phoneType = phone.getPhoneType();
 
         // On GSM phones, null is returned for MMI codes
@@ -2321,107 +2338,6 @@
     //
 
     /**
-     * Check if all the provider's info is present in the intent.
-     * @param intent Expected to have the provider's extra.
-     * @return true if the intent has all the extras to build the
-     * in-call screen's provider info overlay.
-     */
-    /* package */ static boolean hasPhoneProviderExtras(Intent intent) {
-        if (null == intent) {
-            return false;
-        }
-        final String name = intent.getStringExtra(InCallScreen.EXTRA_GATEWAY_PROVIDER_PACKAGE);
-        final String gatewayUri = intent.getStringExtra(InCallScreen.EXTRA_GATEWAY_URI);
-
-        return !TextUtils.isEmpty(name) && !TextUtils.isEmpty(gatewayUri);
-    }
-
-    /**
-     * Copy all the expected extras set when a 3rd party provider is
-     * used from the source intent to the destination one.  Checks all
-     * the required extras are present, if any is missing, none will
-     * be copied.
-     * @param src Intent which may contain the provider's extras.
-     * @param dst Intent where a copy of the extras will be added if applicable.
-     */
-    /* package */ static void checkAndCopyPhoneProviderExtras(Intent src, Intent dst) {
-        if (!hasPhoneProviderExtras(src)) {
-            Log.d(LOG_TAG, "checkAndCopyPhoneProviderExtras: some or all extras are missing.");
-            return;
-        }
-
-        dst.putExtra(InCallScreen.EXTRA_GATEWAY_PROVIDER_PACKAGE,
-                     src.getStringExtra(InCallScreen.EXTRA_GATEWAY_PROVIDER_PACKAGE));
-        dst.putExtra(InCallScreen.EXTRA_GATEWAY_URI,
-                     src.getStringExtra(InCallScreen.EXTRA_GATEWAY_URI));
-    }
-
-    /**
-     * Get the provider's label from the intent.
-     * @param context to lookup the provider's package name.
-     * @param intent with an extra set to the provider's package name.
-     * @return The provider's application label. null if an error
-     * occurred during the lookup of the package name or the label.
-     */
-    /* package */ static CharSequence getProviderLabel(Context context, Intent intent) {
-        String packageName = intent.getStringExtra(InCallScreen.EXTRA_GATEWAY_PROVIDER_PACKAGE);
-        PackageManager pm = context.getPackageManager();
-
-        try {
-            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
-
-            return pm.getApplicationLabel(info);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Get the provider's icon.
-     * @param context to lookup the provider's icon.
-     * @param intent with an extra set to the provider's package name.
-     * @return The provider's application icon. null if an error occured during the icon lookup.
-     */
-    /* package */ static Drawable getProviderIcon(Context context, Intent intent) {
-        String packageName = intent.getStringExtra(InCallScreen.EXTRA_GATEWAY_PROVIDER_PACKAGE);
-        PackageManager pm = context.getPackageManager();
-
-        try {
-            return pm.getApplicationIcon(packageName);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Return the gateway uri from the intent.
-     * @param intent With the gateway uri extra.
-     * @return The gateway URI or null if not found.
-     */
-    /* package */ static Uri getProviderGatewayUri(Intent intent) {
-        String uri = intent.getStringExtra(InCallScreen.EXTRA_GATEWAY_URI);
-        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
-    }
-
-    /**
-     * Return a formatted version of the uri's scheme specific
-     * part. E.g for 'tel:12345678', return '1-234-5678'.
-     * @param uri A 'tel:' URI with the gateway phone number.
-     * @return the provider's address (from the gateway uri) formatted
-     * for user display. null if uri was null or its scheme was not 'tel:'.
-     */
-    /* package */ static String formatProviderUri(Uri uri) {
-        if (null != uri) {
-            if (Constants.SCHEME_TEL.equals(uri.getScheme())) {
-                return PhoneNumberUtils.formatNumber(uri.getSchemeSpecificPart());
-            } else {
-                return uri.toString();
-            }
-        }
-        return null;
-    }
-
-    /**
      * Check if a phone number can be route through a 3rd party
      * gateway. The number must be a global phone number in numerical
      * form (1-800-666-SEXY won't work).
@@ -2432,7 +2348,7 @@
      * @param number To be dialed via a 3rd party gateway.
      * @return true If the number can be routed through the 3rd party network.
      */
-    /* package */ static boolean isRoutableViaGateway(String number) {
+    private static boolean isRoutableViaGateway(String number) {
         if (TextUtils.isEmpty(number)) {
             return false;
         }
diff --git a/src/com/android/phone/SipCallOptionHandler.java b/src/com/android/phone/SipCallOptionHandler.java
index 500f322..295e886 100644
--- a/src/com/android/phone/SipCallOptionHandler.java
+++ b/src/com/android/phone/SipCallOptionHandler.java
@@ -176,7 +176,7 @@
         // call via the default pstn network. However, if one just alters
         // the destination directly, then we still let it go through the
         // Internet call option process.
-        if (!PhoneUtils.hasPhoneProviderExtras(mIntent)) {
+        if (!CallGatewayManager.hasPhoneProviderExtras(mIntent)) {
             if (!isNetworkConnected()) {
                 if (!isRegularCall) {
                     showDialog(DIALOG_NO_INTERNET_ERROR);