am 0ceb5a12: Add permission to CONNECTION_MANAGER PhoneAccount registration.
automerge: 5ae91bd

* commit '5ae91bdbbf00f36eec1d66ed854454611264a787':
  Add permission to CONNECTION_MANAGER PhoneAccount registration.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 427b248..ebcf697 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -185,15 +185,6 @@
             </intent-filter>
         </activity-alias>
 
-        <activity-alias android:name="IncomingCallActivity"
-                android:targetActivity="CallActivity"
-                android:exported="true">
-            <intent-filter>
-                <action android:name="android.telecom.action.INCOMING_CALL" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity-alias>
-
         <receiver android:name="TelecomBroadcastReceiver" android:exported="false">
             <intent-filter>
                 <action android:name="com.android.server.telecom.ACTION_CALL_BACK_FROM_NOTIFICATION" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9ecc6bb..a5d8d9f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -83,6 +83,9 @@
     <!-- Call failure message displayed in an error dialog used to indicate that a phone number was not provided -->
     <string name="outgoing_call_error_no_phone_number_supplied">Call not sent, no valid number entered.</string>
 
+    <!-- Message shown when the user tries to make a video call when already in a video call. -->
+    <string name ="duplicate_video_call_not_allowed">Call cannot be added at this time.</string>
+
     <!-- missing voicemail number -->
     <!-- Title of the "Missing voicemail number" dialog -->
     <string name="no_vm_number">Missing voicemail number</string>
diff --git a/src/com/android/server/telecom/BluetoothManager.java b/src/com/android/server/telecom/BluetoothManager.java
index 9b5fd26..f74349f 100644
--- a/src/com/android/server/telecom/BluetoothManager.java
+++ b/src/com/android/server/telecom/BluetoothManager.java
@@ -26,6 +26,8 @@
 import android.content.IntentFilter;
 import android.os.SystemClock;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.util.List;
 
 /**
@@ -244,30 +246,33 @@
         mBluetoothConnectionPending = false;
     }
 
-    private void dumpBluetoothState() {
-        Log.d(this, "============== dumpBluetoothState() =============");
-        Log.d(this, "= isBluetoothAvailable: " + isBluetoothAvailable());
-        Log.d(this, "= isBluetoothAudioConnected: " + isBluetoothAudioConnected());
-        Log.d(this, "= isBluetoothAudioConnectedOrPending: " +
-                isBluetoothAudioConnectedOrPending());
-        Log.d(this, "=");
+    /**
+     * Dumps the state of the {@link BluetoothManager}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("isBluetoothAvailable: " + isBluetoothAvailable());
+        pw.println("isBluetoothAudioConnected: " + isBluetoothAudioConnected());
+        pw.println("isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
+
         if (mBluetoothAdapter != null) {
             if (mBluetoothHeadset != null) {
                 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
 
                 if (deviceList.size() > 0) {
                     BluetoothDevice device = deviceList.get(0);
-                    Log.d(this, "= BluetoothHeadset.getCurrentDevice: " + device);
-                    Log.d(this, "= BluetoothHeadset.State: "
-                        + mBluetoothHeadset.getConnectionState(device));
-                    Log.d(this, "= BluetoothHeadset audio connected: " +
-                        mBluetoothHeadset.isAudioConnected(device));
+                    pw.println("BluetoothHeadset.getCurrentDevice: " + device);
+                    pw.println("BluetoothHeadset.State: "
+                            + mBluetoothHeadset.getConnectionState(device));
+                    pw.println("BluetoothHeadset audio connected: " +
+                            mBluetoothHeadset.isAudioConnected(device));
                 }
             } else {
-                Log.d(this, "= mBluetoothHeadset is null");
+                pw.println("mBluetoothHeadset is null");
             }
         } else {
-            Log.d(this, "= mBluetoothAdapter is null; device is not BT capable");
+            pw.println("mBluetoothAdapter is null; device is not BT capable");
         }
     }
 }
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index a0a8a62..e2bead7 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -182,7 +182,7 @@
      * The time this call was created. Beyond logging and such, may also be used for bookkeeping
      * and specifically for marking certain call attempts as failed attempts.
      */
-    private final long mCreationTimeMillis = System.currentTimeMillis();
+    private long mCreationTimeMillis = System.currentTimeMillis();
 
     /** The gateway information associated with this call. This stores the original call handle
      * that the user is attempting to connect to via the gateway, the actual handle to dial in
@@ -544,6 +544,10 @@
         return mCreationTimeMillis;
     }
 
+    void setCreationTimeMillis(long time) {
+        mCreationTimeMillis = time;
+    }
+
     long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
diff --git a/src/com/android/server/telecom/CallActivity.java b/src/com/android/server/telecom/CallActivity.java
index 7022327..2e34d8f 100644
--- a/src/com/android/server/telecom/CallActivity.java
+++ b/src/com/android/server/telecom/CallActivity.java
@@ -85,8 +85,6 @@
                 Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                 Intent.ACTION_CALL_EMERGENCY.equals(action)) {
             processOutgoingCallIntent(intent);
-        } else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) {
-            processIncomingCallIntent(intent);
         }
     }
 
@@ -132,14 +130,6 @@
         }
     }
 
-    private void processIncomingCallIntent(Intent intent) {
-        if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
-            CallReceiver.processIncomingCallIntent(intent);
-        } else {
-            sendBroadcastToReceiver(intent, true /* isIncoming */);
-        }
-    }
-
     private boolean isDefaultDialer() {
         final String packageName = getCallingPackage();
         if (TextUtils.isEmpty(packageName)) {
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index a89dcea..af4a3fa 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -21,6 +21,7 @@
 import android.telecom.AudioState;
 import android.telecom.CallState;
 
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
 import java.util.Objects;
@@ -498,4 +499,30 @@
     private boolean hasFocus() {
         return mAudioFocusStreamType != STREAM_NONE;
     }
+
+    /**
+     * Dumps the state of the {@link CallAudioManager}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("mAudioState: " + mAudioState);
+        pw.println("mBluetoothManager:");
+        pw.increaseIndent();
+        mBluetoothManager.dump(pw);
+        pw.decreaseIndent();
+        if (mWiredHeadsetManager != null) {
+            pw.println("mWiredHeadsetManager:");
+            pw.increaseIndent();
+            mWiredHeadsetManager.dump(pw);
+            pw.decreaseIndent();
+        } else {
+            pw.println("mWiredHeadsetManager: null");
+        }
+        pw.println("mAudioFocusStreamType: " + mAudioFocusStreamType);
+        pw.println("mIsRinging: " + mIsRinging);
+        pw.println("mIsTonePlaying: " + mIsTonePlaying);
+        pw.println("mWasSpeakerOn: " + mWasSpeakerOn);
+        pw.println("mMostRecentlyUsedMode: " + mMostRecentlyUsedMode);
+    }
 }
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 658af10..8764a04 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -98,10 +98,10 @@
 
     @Override
     public void onCallStateChanged(Call call, int oldState, int newState) {
+        int disconnectCause = call.getDisconnectCause().getCode();
         boolean isNewlyDisconnected =
                 newState == CallState.DISCONNECTED || newState == CallState.ABORTED;
-        boolean isCallCanceled = isNewlyDisconnected &&
-                call.getDisconnectCause().getCode() == DisconnectCause.CANCELED;
+        boolean isCallCanceled = isNewlyDisconnected && disconnectCause == DisconnectCause.CANCELED;
 
         // Log newly disconnected calls only if:
         // 1) It was not in the "choose account" phase when disconnected
@@ -114,7 +114,7 @@
             int type;
             if (!call.isIncoming()) {
                 type = Calls.OUTGOING_TYPE;
-            } else if (oldState == CallState.RINGING) {
+            } else if (disconnectCause == DisconnectCause.MISSED) {
                 type = Calls.MISSED_TYPE;
             } else {
                 type = Calls.INCOMING_TYPE;
diff --git a/src/com/android/server/telecom/CallReceiver.java b/src/com/android/server/telecom/CallReceiver.java
index 17ca3e5..8654b7f 100644
--- a/src/com/android/server/telecom/CallReceiver.java
+++ b/src/com/android/server/telecom/CallReceiver.java
@@ -9,8 +9,10 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.widget.Toast;
 
 /**
  * Single point of entry for all outgoing and incoming calls. {@link CallActivity} serves as a
@@ -29,14 +31,10 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
-        final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false);
-        Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall,
-                isUnknownCall);
+        Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
 
         if (isUnknownCall) {
             processUnknownCallIntent(intent);
-        } else if (isIncomingCall) {
-            processIncomingCallIntent(intent);
         } else {
             processOutgoingCallIntent(context, intent);
         }
@@ -48,6 +46,10 @@
      * @param intent Call intent containing data about the handle to call.
      */
     static void processOutgoingCallIntent(Context context, Intent intent) {
+        if (shouldPreventDuplicateVideoCall(context, intent)) {
+            return;
+        }
+
         Uri handle = intent.getData();
         String scheme = handle.getScheme();
         String uriString = handle.getSchemeSpecificPart();
@@ -153,4 +155,28 @@
         errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
     }
+
+    /**
+     * Whether an outgoing video call should be prevented from going out. Namely, don't allow an
+     * outgoing video call if there is already an ongoing video call. Notify the user if their call
+     * is not sent.
+     *
+     * @return {@code true} if the outgoing call is a video call and should be prevented from going
+     *     out, {@code false} otherwise.
+     */
+    private static boolean shouldPreventDuplicateVideoCall(Context context, Intent intent) {
+        int intentVideoState = intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+                VideoProfile.VideoState.AUDIO_ONLY);
+        if (intentVideoState == VideoProfile.VideoState.AUDIO_ONLY
+                || !getCallsManager().hasVideoCall()) {
+            return false;
+        } else {
+            // Display an error toast to the user.
+            Toast.makeText(
+                    context,
+                    context.getResources().getString(R.string.duplicate_video_call_not_allowed),
+                    Toast.LENGTH_LONG).show();
+            return true;
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e07b279..86d9f01 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -32,6 +32,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.PhoneCapabilities;
 import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
 import android.telephony.TelephonyManager;
 
 import com.android.internal.util.IndentingPrintWriter;
@@ -312,6 +313,15 @@
         return false;
     }
 
+    boolean hasVideoCall() {
+        for (Call call : mCalls) {
+            if (call.getVideoState() != VideoProfile.VideoState.AUDIO_ONLY) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     AudioState getAudioState() {
         return mCallAudioManager.getAudioState();
     }
@@ -833,26 +843,18 @@
     }
 
     /**
-     * Checks to see if the specified call is the only high-level call and if so, enable the
-     * "Add-call" button. We allow you to add a second call but not a third or beyond.
+     * Checks to see if the specified call is the only top level call. If it is not, we should
+     * remove the ADD_CALL capability. We allow you to add a second call but not a third or beyond.
      *
-     * @param call The call to test for add-call.
-     * @return Whether the add-call feature should be enabled for the call.
+     * @param call The call to check to see if it is the only top level call.
+     * @return Whether the call is the only top level call.
      */
-    protected boolean isAddCallCapable(Call call) {
+    protected boolean isOnlyTopLevelCall(Call call) {
         if (call.getParentCall() != null) {
             // Never true for child calls.
             return false;
         }
 
-        // Use canManageConference as a mechanism to check if the call is CDMA.
-        // Disable "Add Call" for CDMA calls which are conference calls.
-        boolean canManageConference = PhoneCapabilities.MANAGE_CONFERENCE
-                == (call.getCallCapabilities() & PhoneCapabilities.MANAGE_CONFERENCE);
-        if (call.isConference() && !canManageConference) {
-            return false;
-        }
-
         // Loop through all the other calls and there exists a top level (has no parent) call
         // that is not the specified call, return false.
         for (Call otherCall : mCalls) {
@@ -1229,7 +1231,6 @@
      */
     public void dump(IndentingPrintWriter pw) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
-        pw.increaseIndent();
         if (mCalls != null) {
             pw.println("mCalls: ");
             pw.increaseIndent();
@@ -1238,6 +1239,34 @@
             }
             pw.decreaseIndent();
         }
-        pw.decreaseIndent();
+        pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall));
+
+        if (mCallAudioManager != null) {
+            pw.println("mCallAudioManager:");
+            pw.increaseIndent();
+            mCallAudioManager.dump(pw);
+            pw.decreaseIndent();
+        }
+
+        if (mTtyManager != null) {
+            pw.println("mTtyManager:");
+            pw.increaseIndent();
+            mTtyManager.dump(pw);
+            pw.decreaseIndent();
+        }
+
+        if (mInCallController != null) {
+            pw.println("mInCallController:");
+            pw.increaseIndent();
+            mInCallController.dump(pw);
+            pw.decreaseIndent();
+        }
+
+        if (mConnectionServiceRepository != null) {
+            pw.println("mConnectionServiceRepository:");
+            pw.increaseIndent();
+            mConnectionServiceRepository.dump(pw);
+            pw.decreaseIndent();
+        }
     }
 }
diff --git a/src/com/android/server/telecom/ConnectionServiceRepository.java b/src/com/android/server/telecom/ConnectionServiceRepository.java
index e9a447d..9019296 100644
--- a/src/com/android/server/telecom/ConnectionServiceRepository.java
+++ b/src/com/android/server/telecom/ConnectionServiceRepository.java
@@ -19,6 +19,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.util.HashMap;
 
 /**
@@ -59,4 +61,18 @@
     public void onUnbind(ConnectionServiceWrapper service) {
         mServiceCache.remove(service.getComponentName());
     }
+
+    /**
+     * Dumps the state of the {@link ConnectionServiceRepository}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("mServiceCache:");
+        pw.increaseIndent();
+        for (ComponentName componentName : mServiceCache.keySet()) {
+            pw.println(componentName);
+        }
+        pw.decreaseIndent();
+    }
 }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 78553ad..0a15ee6 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -40,6 +40,8 @@
 
 // TODO: Needed for move to system service: import com.android.internal.R;
 import com.android.internal.telecom.IInCallService;
+import com.android.internal.util.IndentingPrintWriter;
+
 import com.google.common.collect.ImmutableCollection;
 
 import java.util.ArrayList;
@@ -406,7 +408,6 @@
                 IInCallService inCallService = entry.getValue();
                 ParcelableCall parcelableCall = toParcelableCall(call,
                         componentName.equals(mInCallComponentName) /* includeVideoProvider */);
-
                 Log.v(this, "updateCall %s ==> %s", call, parcelableCall);
                 try {
                     inCallService.updateCall(parcelableCall);
@@ -427,20 +428,29 @@
     private ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider) {
         String callId = mCallIdMapper.getCallId(call);
 
-        int capabilities = call.getCallCapabilities();
-        if (CallsManager.getInstance().isAddCallCapable(call)) {
-            capabilities |= PhoneCapabilities.ADD_CALL;
-        }
-
-        // Disable mute and add call for emergency calls.
-        if (call.isEmergencyCall()) {
-            capabilities &= ~PhoneCapabilities.MUTE;
-            capabilities &= ~PhoneCapabilities.ADD_CALL;
-        }
-
-        int properties = call.isConference() ? CallProperties.CONFERENCE : 0;
-
         int state = call.getState();
+        int capabilities = call.getCallCapabilities();
+
+        if (!CallsManager.getInstance().isOnlyTopLevelCall(call) || state == CallState.DIALING) {
+            capabilities = PhoneCapabilities.remove(capabilities, PhoneCapabilities.ADD_CALL);
+        }
+
+        if (call.isRespondViaSmsCapable()) {
+            capabilities |= PhoneCapabilities.RESPOND_VIA_TEXT;
+        }
+
+        if (call.isEmergencyCall()) {
+            capabilities = PhoneCapabilities.remove(capabilities, PhoneCapabilities.MUTE);
+            capabilities = PhoneCapabilities.remove(capabilities, PhoneCapabilities.ADD_CALL);
+        }
+
+        if (state == CallState.DIALING) {
+            capabilities =
+                    PhoneCapabilities.remove(capabilities, PhoneCapabilities.SUPPORTS_VT_LOCAL);
+            capabilities =
+                    PhoneCapabilities.remove(capabilities, PhoneCapabilities.SUPPORTS_VT_REMOTE);
+        }
+
         if (state == CallState.ABORTED) {
             state = CallState.DISCONNECTED;
         }
@@ -468,10 +478,6 @@
             }
         }
 
-        if (call.isRespondViaSmsCapable()) {
-            capabilities |= PhoneCapabilities.RESPOND_VIA_TEXT;
-        }
-
         Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
                 call.getHandle() : null;
         String callerDisplayName = call.getCallerDisplayNamePresentation() ==
@@ -486,6 +492,7 @@
             }
         }
 
+        int properties = call.isConference() ? CallProperties.CONFERENCE : 0;
         return new ParcelableCall(
                 callId,
                 state,
@@ -519,4 +526,25 @@
             call.addListener(mCallListener);
         }
     }
+
+    /**
+     * Dumps the state of the {@link InCallController}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("mInCallServices (InCalls registered):");
+        pw.increaseIndent();
+        for (ComponentName componentName : mInCallServices.keySet()) {
+            pw.println(componentName);
+        }
+        pw.decreaseIndent();
+
+        pw.println("mServiceConnections (InCalls bound):");
+        pw.increaseIndent();
+        for (ComponentName componentName : mServiceConnections.keySet()) {
+            pw.println(componentName);
+        }
+        pw.decreaseIndent();
+    }
 }
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 2c1ffb8..ced0fdf 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -57,6 +57,14 @@
         Calls.DURATION,
         Calls.TYPE,
     };
+
+    private static final int CALL_LOG_COLUMN_ID = 0;
+    private static final int CALL_LOG_COLUMN_NUMBER = 1;
+    private static final int CALL_LOG_COLUMN_NUMBER_PRESENTATION = 2;
+    private static final int CALL_LOG_COLUMN_DATE = 3;
+    private static final int CALL_LOG_COLUMN_DURATION = 4;
+    private static final int CALL_LOG_COLUMN_TYPE = 5;
+
     private static final int MISSED_CALL_NOTIFICATION_ID = 1;
 
     private final Context mContext;
@@ -280,10 +288,10 @@
                     try {
                         while (cursor.moveToNext()) {
                             // Get data about the missed call from the cursor
-                            final String handleString = cursor.getString(
-                                    cursor.getColumnIndexOrThrow(Calls.NUMBER));
-                            final int presentation = cursor.getInt(cursor.getColumnIndexOrThrow(
-                                    Calls.NUMBER_PRESENTATION));
+                            final String handleString = cursor.getString(CALL_LOG_COLUMN_NUMBER);
+                            final int presentation =
+                                    cursor.getInt(CALL_LOG_COLUMN_NUMBER_PRESENTATION);
+                            final long date = cursor.getLong(CALL_LOG_COLUMN_DATE);
 
                             final Uri handle;
                             if (presentation != Calls.PRESENTATION_ALLOWED
@@ -300,6 +308,7 @@
                                     false);
                             call.setDisconnectCause(new DisconnectCause(DisconnectCause.MISSED));
                             call.setState(CallState.DISCONNECTED);
+                            call.setCreationTimeMillis(date);
 
                             // Listen for the update to the caller information before posting the
                             // notification so that we have the contact info and photo.
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 4ee2c88..0cecf09 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -39,6 +39,7 @@
 // TODO: Needed for move to system service: import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -555,6 +556,27 @@
         public int versionNumber;
     }
 
+    /**
+     * Dumps the state of the {@link CallsManager}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        if (mState != null) {
+            pw.println("xmlVersion: " + mState.versionNumber);
+            pw.println("defaultOutgoing: " + (mState.defaultOutgoing == null ? "none" :
+                    mState.defaultOutgoing));
+            pw.println("simCallManager: " + (mState.simCallManager == null ? "none" :
+                    mState.simCallManager));
+            pw.println("phoneAccounts:");
+            pw.increaseIndent();
+            for (PhoneAccount phoneAccount : mState.accounts) {
+                pw.println(phoneAccount);
+            }
+            pw.decreaseIndent();
+        }
+    }
+
     ////////////////////////////////////////////////////////////////////////////////////////////////
     //
     // State management
diff --git a/src/com/android/server/telecom/ProximitySensorManager.java b/src/com/android/server/telecom/ProximitySensorManager.java
index 289366f..5b82c43 100644
--- a/src/com/android/server/telecom/ProximitySensorManager.java
+++ b/src/com/android/server/telecom/ProximitySensorManager.java
@@ -42,7 +42,7 @@
     @Override
     public void onCallRemoved(Call call) {
         if (CallsManager.getInstance().getCalls().isEmpty()) {
-            Log.v(this, "all calls removed, resetting proximity sensor to default state");
+            Log.i(this, "All calls removed, resetting proximity sensor to default state");
             turnOff(true);
         }
         super.onCallRemoved(call);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 7113678..d58bf7a 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -71,6 +71,8 @@
     private static final class MainThreadRequest {
         /** The result of the request that is run on the main thread */
         public Object result;
+        /** Object that can be used to store non-integer arguments */
+        public Object arg;
     }
 
     /**
@@ -106,6 +108,13 @@
                     case MSG_GET_CURRENT_TTY_MODE:
                         result = mCallsManager.getCurrentTtyMode();
                         break;
+                    case MSG_NEW_INCOMING_CALL:
+                        if (request.arg == null || !(request.arg instanceof Intent)) {
+                            Log.w(this, "Invalid new incoming call request");
+                            break;
+                        }
+                        CallReceiver.processIncomingCallIntent((Intent) request.arg);
+                        break;
                 }
 
                 if (result != null) {
@@ -130,6 +139,7 @@
     private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 5;
     private static final int MSG_IS_TTY_SUPPORTED = 6;
     private static final int MSG_GET_CURRENT_TTY_MODE = 7;
+    private static final int MSG_NEW_INCOMING_CALL = 8;
 
     /** The singleton instance. */
     private static TelecomServiceImpl sInstance;
@@ -474,21 +484,20 @@
      */
     @Override
     public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
+        Log.i(this, "Adding new incoming call with phoneAccountHandle %s", phoneAccountHandle);
         if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
             mAppOpsManager.checkPackage(
                     Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
 
             Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
-            intent.setPackage(mContext.getPackageName());
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+            intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, true);
             if (extras != null) {
                 intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
             }
-
-            long token = Binder.clearCallingIdentity();
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-            Binder.restoreCallingIdentity(token);
+            sendRequestAsync(MSG_NEW_INCOMING_CALL, 0, intent);
+        } else {
+            Log.w(this, "Null phoneAccountHandle. Ignoring request to add new incoming call");
         }
     }
 
@@ -635,7 +644,12 @@
     }
 
     private MainThreadRequest sendRequestAsync(int command, int arg1) {
+        return sendRequestAsync(command, arg1, null);
+    }
+
+    private MainThreadRequest sendRequestAsync(int command, int arg1, Object arg) {
         MainThreadRequest request = new MainThreadRequest();
+        request.arg = arg;
         mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
         return request;
     }
@@ -666,6 +680,13 @@
         }
     }
 
+    /**
+     * Dumps the current state of the TelecomService.  Used when generating problem reports.
+     *
+     * @param fd The file descriptor.
+     * @param writer The print writer to dump the state to.
+     * @param args Optional dump arguments.
+     */
     @Override
     protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
@@ -675,6 +696,11 @@
             pw.increaseIndent();
             mCallsManager.dump(pw);
             pw.decreaseIndent();
+
+            pw.println("mPhoneAccountRegistrar: ");
+            pw.increaseIndent();
+            mPhoneAccountRegistrar.dump(pw);
+            pw.decreaseIndent();
         }
     }
 }
diff --git a/src/com/android/server/telecom/TtyManager.java b/src/com/android/server/telecom/TtyManager.java
index b21f165..e0a9e9c 100644
--- a/src/com/android/server/telecom/TtyManager.java
+++ b/src/com/android/server/telecom/TtyManager.java
@@ -25,6 +25,8 @@
 import android.provider.Settings;
 import android.telecom.TelecomManager;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 // TODO: Needed for move to system service: import com.android.internal.R;
 
 final class TtyManager implements WiredHeadsetManager.Listener {
@@ -122,4 +124,13 @@
             }
         }
     }
+
+    /**
+     * Dumps the state of the {@link TtyManager}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("mCurrentTtyMode: " + mCurrentTtyMode);
+    }
 }
diff --git a/src/com/android/server/telecom/WiredHeadsetManager.java b/src/com/android/server/telecom/WiredHeadsetManager.java
index 8ce7b61..a61dd6e 100644
--- a/src/com/android/server/telecom/WiredHeadsetManager.java
+++ b/src/com/android/server/telecom/WiredHeadsetManager.java
@@ -22,6 +22,8 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 
+import com.android.internal.util.IndentingPrintWriter;
+
 import java.util.Collections;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -91,4 +93,13 @@
             }
         }
     }
+
+    /**
+     * Dumps the state of the {@link WiredHeadsetManager}.
+     *
+     * @param pw The {@code IndentingPrintWriter} to write the state to.
+     */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("mIsPluggedIn: " + mIsPluggedIn);
+    }
 }
diff --git a/tests/src/com/android/server/telecom/testapps/TestCallActivity.java b/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
index 9c9a40b..6f4ae20 100644
--- a/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
+++ b/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
@@ -50,7 +50,7 @@
         final Uri data = intent != null ? intent.getData() : null;
         if (ACTION_NEW_INCOMING_CALL.equals(action) && data != null) {
             CallNotificationReceiver.sendIncomingCallIntent(this, data, false);
-        } if (ACTION_NEW_UNKNOWN_CALL.equals(action) && data != null) {
+        } else if (ACTION_NEW_UNKNOWN_CALL.equals(action) && data != null) {
             CallNotificationReceiver.addNewUnknownCall(this, data, intent.getExtras());
         } else {
             CallServiceNotifier.getInstance().updateNotification(this);