Revert "Make Connection threadsafe"

This reverts commit 53488bf3d152eb0ecacca5716c763e8cd7ab28ae.

Change-Id: Ia13c510279e600e8bd1445fd63e2cd6503f4077e
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index b55f62a..8845821 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -19,9 +19,6 @@
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import com.android.internal.os.SomeArgs;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -33,32 +30,6 @@
  */
 public abstract class Connection {
 
-    private static final int MSG_ADD_CONNECTION_LISTENER = 1;
-    private static final int MSG_REMOVE_CONNECTION_LISTENER = 2;
-    private static final int MSG_SET_AUDIO_STATE = 3;
-    private static final int MSG_SET_PARENT_CONNECTION = 4;
-    private static final int MSG_SET_HANDLE = 5;
-    private static final int MSG_SET_CALLER_DISPLAY_NAME = 6;
-    private static final int MSG_SET_CANCELED = 7;
-    private static final int MSG_SET_FAILED = 8;
-    private static final int MSG_SET_VIDEO_STATE = 9;
-    private static final int MSG_SET_ACTIVE = 10;
-    private static final int MSG_SET_RINGING = 11;
-    private static final int MSG_SET_INITIALIZING = 12;
-    private static final int MSG_SET_INITIALIZED = 13;
-    private static final int MSG_SET_DIALING = 14;
-    private static final int MSG_SET_ON_HOLD = 15;
-    private static final int MSG_SET_VIDEO_CALL_PROVIDER = 16;
-    private static final int MSG_SET_DISCONNECTED = 17;
-    private static final int MSG_SET_POST_DIAL_WAIT = 18;
-    private static final int MSG_SET_REQUESTING_RINGBACK = 19;
-    private static final int MSG_SET_CALL_CAPABILITIES = 20;
-    private static final int MSG_DESTROY = 21;
-    private static final int MSG_SET_SIGNAL = 22;
-    private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 23;
-    private static final int MSG_SET_STATUS_HINTS = 24;
-    private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 25;
-
     /** @hide */
     public abstract static class Listener {
         public void onStateChanged(Connection c, int state) {}
@@ -78,6 +49,7 @@
         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
         public void onStartActivityFromInCall(Connection c, PendingIntent intent) {}
+        public void onFailed(Connection c, int code, String msg) {}
     }
 
     public final class State {
@@ -115,220 +87,6 @@
     private String mFailureMessage;
     private boolean mIsCanceled;
 
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ADD_CONNECTION_LISTENER: {
-                    Listener listener = (Listener) msg.obj;
-                    mListeners.add(listener);
-                }
-                break;
-                case MSG_REMOVE_CONNECTION_LISTENER: {
-                    Listener listener = (Listener) msg.obj;
-                    mListeners.remove(listener);
-                }
-                break;
-                case MSG_SET_AUDIO_STATE: {
-                    CallAudioState state = (CallAudioState) msg.obj;
-                    mCallAudioState = state;
-                    onSetAudioState(state);
-                }
-                break;
-                case MSG_SET_PARENT_CONNECTION: {
-                    Connection parentConnection = (Connection) msg.obj;
-                    if (mParentConnection != parentConnection) {
-                        if (mParentConnection != null) {
-                            mParentConnection.removeChild(Connection.this);
-                        }
-                        mParentConnection = parentConnection;
-                        if (mParentConnection != null) {
-                            mParentConnection.addChild(Connection.this);
-                            // do something if the child connections goes down to ZERO.
-                        }
-                        for (Listener l : mListeners) {
-                            l.onParentConnectionChanged(Connection.this, mParentConnection);
-                        }
-                    }
-                }
-                break;
-                case MSG_SET_HANDLE: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        Uri handle = (Uri) args.arg1;
-                        int presentation = args.argi1;
-                        mHandle = handle;
-                        mHandlePresentation = presentation;
-                        for (Listener l : mListeners) {
-                            l.onHandleChanged(Connection.this, handle, presentation);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_CALLER_DISPLAY_NAME: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        String callerDisplayName = (String) args.arg1;
-                        int presentation = args.argi1;
-                        mCallerDisplayName = callerDisplayName;
-                        mCallerDisplayNamePresentation = presentation;
-                        for (Listener l : mListeners) {
-                            l.onCallerDisplayNameChanged(Connection.this, callerDisplayName,
-                                    presentation);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_CANCELED: {
-                    setState(State.CANCELED);
-                }
-                break;
-                case MSG_SET_FAILED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        int code = args.argi1;
-                        String message = (String) args.arg1;
-                        mFailureCode = code;
-                        mFailureMessage = message;
-                        setState(State.FAILED);
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_VIDEO_STATE: {
-                    int videoState = ((Integer) msg.obj).intValue();
-                    mVideoState = videoState;
-                    for (Listener l : mListeners) {
-                        l.onVideoStateChanged(Connection.this, mVideoState);
-                    }
-                }
-                break;
-                case MSG_SET_ACTIVE: {
-                    setRequestingRingback(false);
-                    setState(State.ACTIVE);
-                }
-                break;
-                case MSG_SET_RINGING: {
-                    setState(State.RINGING);
-                }
-                break;
-                case MSG_SET_INITIALIZING: {
-                    setState(State.INITIALIZING);
-                }
-                break;
-                case MSG_SET_INITIALIZED: {
-                    setState(State.NEW);
-                }
-                break;
-                case MSG_SET_DIALING: {
-                    setState(State.DIALING);
-                }
-                break;
-                case MSG_SET_ON_HOLD: {
-                    setState(State.HOLDING);
-                }
-                break;
-                case MSG_SET_VIDEO_CALL_PROVIDER: {
-                    ConnectionService.VideoCallProvider videoCallProvider =
-                            (ConnectionService.VideoCallProvider) msg.obj;
-                    mVideoCallProvider = videoCallProvider;
-                    for (Listener l : mListeners) {
-                        l.onVideoCallProviderChanged(Connection.this, videoCallProvider);
-                    }
-                }
-                break;
-                case MSG_SET_DISCONNECTED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        int cause = args.argi1;
-                        String message = (String) args.arg1;
-                        setState(State.DISCONNECTED);
-                        Log.d(this, "Disconnected with cause %d message %s", cause, message);
-                        for (Listener l : mListeners) {
-                            l.onDisconnected(Connection.this, cause, message);
-                        }
-                    } finally {
-                        args.recycle();
-                    }
-                }
-                break;
-                case MSG_SET_POST_DIAL_WAIT: {
-                    String remaining = (String) msg.obj;
-                    for (Listener l : mListeners) {
-                        l.onPostDialWait(Connection.this, remaining);
-                    }
-                }
-                break;
-                case MSG_SET_REQUESTING_RINGBACK: {
-                    boolean ringback = ((Boolean) msg.obj).booleanValue();
-                    if (mRequestingRingback != ringback) {
-                        mRequestingRingback = ringback;
-                        for (Listener l : mListeners) {
-                            l.onRequestingRingback(Connection.this, ringback);
-                        }
-                    }
-                } break;
-                case MSG_SET_CALL_CAPABILITIES: {
-                    int callCapabilities = ((Integer) msg.obj).intValue();
-                    if (mCallCapabilities != callCapabilities) {
-                        mCallCapabilities = callCapabilities;
-                        for (Listener l : mListeners) {
-                            l.onCallCapabilitiesChanged(Connection.this, mCallCapabilities);
-                        }
-                    }
-                }
-                break;
-                case MSG_DESTROY: {
-                    // TODO: Is this still relevant because everything is on the main thread now.
-                    // It is possible that onDestroy() will trigger the listener to remove itself
-                    // which will result in a concurrent modification exception. To counteract
-                    // this we make a copy of the listeners and iterate on that.
-                    for (Listener l : new ArrayList<>(mListeners)) {
-                        if (mListeners.contains(l)) {
-                            l.onDestroyed(Connection.this);
-                        }
-                    }
-                }
-                break;
-                case MSG_SET_SIGNAL: {
-                    Bundle details = (Bundle) msg.obj;
-                    for (Listener l : mListeners) {
-                        l.onSignalChanged(Connection.this, details);
-                    }
-                }
-                break;
-                case MSG_SET_AUDIO_MODE_IS_VOIP: {
-                    boolean isVoip = ((Boolean) msg.obj).booleanValue();
-                    mAudioModeIsVoip = isVoip;
-                    for (Listener l : mListeners) {
-                        l.onAudioModeIsVoipChanged(Connection.this, isVoip);
-                    }
-                }
-                break;
-                case MSG_SET_STATUS_HINTS: {
-                    StatusHints statusHints = (StatusHints) msg.obj;
-                    mStatusHints = statusHints;
-                    for (Listener l : mListeners) {
-                        l.onStatusHintsChanged(Connection.this, statusHints);
-                    }
-                }
-                break;
-                case MSG_START_ACTIVITY_FROM_IN_CALL: {
-                    PendingIntent intent = (PendingIntent) msg.obj;
-                    for (Listener l : mListeners) {
-                        l.onStartActivityFromInCall(Connection.this, intent);
-                    }
-                }
-                break;
-            }
-        }
-    };
-
     /**
      * Create a new Connection.
      */
@@ -430,7 +188,7 @@
      * @hide
      */
     public final Connection addConnectionListener(Listener l) {
-        mHandler.obtainMessage(MSG_ADD_CONNECTION_LISTENER, l).sendToTarget();
+        mListeners.add(l);
         return this;
     }
 
@@ -443,7 +201,7 @@
      * @hide
      */
     public final Connection removeConnectionListener(Listener l) {
-        mHandler.obtainMessage(MSG_REMOVE_CONNECTION_LISTENER, l).sendToTarget();
+        mListeners.remove(l);
         return this;
     }
 
@@ -469,7 +227,8 @@
      */
     final void setAudioState(CallAudioState state) {
         Log.d(this, "setAudioState %s", state);
-        mHandler.obtainMessage(MSG_SET_AUDIO_STATE, state).sendToTarget();
+        mCallAudioState = state;
+        onSetAudioState(state);
     }
 
     /**
@@ -507,7 +266,19 @@
      */
     public final void setParentConnection(Connection parentConnection) {
         Log.d(this, "parenting %s to %s", this, parentConnection);
-        mHandler.obtainMessage(MSG_SET_PARENT_CONNECTION, parentConnection).sendToTarget();
+        if (mParentConnection != parentConnection) {
+            if (mParentConnection != null) {
+                mParentConnection.removeChild(this);
+            }
+            mParentConnection = parentConnection;
+            if (mParentConnection != null) {
+                mParentConnection.addChild(this);
+                // do something if the child connections goes down to ZERO.
+            }
+            for (Listener l : mListeners) {
+                l.onParentConnectionChanged(this, mParentConnection);
+            }
+        }
     }
 
     public final Connection getParentConnection() {
@@ -534,10 +305,11 @@
      */
     public final void setHandle(Uri handle, int presentation) {
         Log.d(this, "setHandle %s", handle);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = handle;
-        args.argi1 = presentation;
-        mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget();
+        mHandle = handle;
+        mHandlePresentation = presentation;
+        for (Listener l : mListeners) {
+            l.onHandleChanged(this, handle, presentation);
+        }
     }
 
     /**
@@ -549,10 +321,11 @@
      */
     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = callerDisplayName;
-        args.argi1 = presentation;
-        mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget();
+        mCallerDisplayName = callerDisplayName;
+        mCallerDisplayNamePresentation = presentation;
+        for (Listener l : mListeners) {
+            l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+        }
     }
 
     /**
@@ -561,7 +334,7 @@
      */
     public final void setCanceled() {
         Log.d(this, "setCanceled");
-        mHandler.obtainMessage(MSG_SET_CANCELED).sendToTarget();
+        setState(State.CANCELED);
     }
 
     /**
@@ -577,10 +350,9 @@
      */
     public final void setFailed(int code, String message) {
         Log.d(this, "setFailed (%d: %s)", code, message);
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = code;
-        args.arg1 = message;
-        mHandler.obtainMessage(MSG_SET_FAILED, args).sendToTarget();
+        mFailureCode = code;
+        mFailureMessage = message;
+        setState(State.FAILED);
     }
 
     /**
@@ -594,7 +366,10 @@
      */
     public final void setVideoState(int videoState) {
         Log.d(this, "setVideoState %d", videoState);
-        mHandler.obtainMessage(MSG_SET_VIDEO_STATE, Integer.valueOf(videoState)).sendToTarget();
+        mVideoState = videoState;
+        for (Listener l : mListeners) {
+            l.onVideoStateChanged(this, mVideoState);
+        }
     }
 
     /**
@@ -602,28 +377,28 @@
      * communicate).
      */
     public final void setActive() {
-        mHandler.obtainMessage(MSG_SET_ACTIVE).sendToTarget();
+        setRequestingRingback(false);
+        setState(State.ACTIVE);
     }
 
     /**
      * Sets state to ringing (e.g., an inbound ringing call).
      */
     public final void setRinging() {
-        mHandler.obtainMessage(MSG_SET_RINGING).sendToTarget();
+        setState(State.RINGING);
     }
 
     /**
      * Sets state to initializing (this Connection is not yet ready to be used).
      */
     public final void setInitializing() {
-        mHandler.obtainMessage(MSG_SET_INITIALIZING).sendToTarget();
+        setState(State.INITIALIZING);
     }
 
     /**
      * Sets state to initialized (the Connection has been set up and is now ready to be used).
      */
     public final void setInitialized() {
-        mHandler.obtainMessage(MSG_SET_INITIALIZED).sendToTarget();
         setState(State.NEW);
     }
 
@@ -631,14 +406,14 @@
      * Sets state to dialing (e.g., dialing an outbound call).
      */
     public final void setDialing() {
-        mHandler.obtainMessage(MSG_SET_DIALING).sendToTarget();
+        setState(State.DIALING);
     }
 
     /**
      * Sets state to be on hold.
      */
     public final void setOnHold() {
-        mHandler.obtainMessage(MSG_SET_ON_HOLD).sendToTarget();
+        setState(State.HOLDING);
     }
 
     /**
@@ -646,7 +421,10 @@
      * @param videoCallProvider The video call provider.
      */
     public final void setVideoCallProvider(ConnectionService.VideoCallProvider videoCallProvider) {
-        mHandler.obtainMessage(MSG_SET_VIDEO_CALL_PROVIDER, videoCallProvider).sendToTarget();
+        mVideoCallProvider = videoCallProvider;
+        for (Listener l : mListeners) {
+            l.onVideoCallProviderChanged(this, videoCallProvider);
+        }
     }
 
     public final ConnectionService.VideoCallProvider getVideoCallProvider() {
@@ -661,17 +439,20 @@
      * @param message Optional call-service-provided message about the disconnect.
      */
     public final void setDisconnected(int cause, String message) {
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = cause;
-        args.arg1 = message;
-        mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget();
+        setState(State.DISCONNECTED);
+        Log.d(this, "Disconnected with cause %d message %s", cause, message);
+        for (Listener l : mListeners) {
+            l.onDisconnected(this, cause, message);
+        }
     }
 
     /**
      * TODO(santoscordon): Needs documentation.
      */
     public final void setPostDialWait(String remaining) {
-        mHandler.obtainMessage(MSG_SET_POST_DIAL_WAIT, remaining).sendToTarget();
+        for (Listener l : mListeners) {
+            l.onPostDialWait(this, remaining);
+        }
     }
 
     /**
@@ -681,8 +462,12 @@
      * @param ringback Whether the ringback tone is to be played.
      */
     public final void setRequestingRingback(boolean ringback) {
-        mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, Boolean.valueOf(ringback))
-                .sendToTarget();
+        if (mRequestingRingback != ringback) {
+            mRequestingRingback = ringback;
+            for (Listener l : mListeners) {
+                l.onRequestingRingback(this, ringback);
+            }
+        }
     }
 
     /**
@@ -691,15 +476,26 @@
      * @param callCapabilities The new call capabilities.
      */
     public final void setCallCapabilities(int callCapabilities) {
-        mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, Integer.valueOf(callCapabilities))
-                .sendToTarget();
+        if (mCallCapabilities != callCapabilities) {
+            mCallCapabilities = callCapabilities;
+            for (Listener l : mListeners) {
+                l.onCallCapabilitiesChanged(this, mCallCapabilities);
+            }
+        }
     }
 
     /**
      * TODO(santoscordon): Needs documentation.
      */
     public final void destroy() {
-        mHandler.obtainMessage(MSG_DESTROY).sendToTarget();
+        // It is possible that onDestroy() will trigger the listener to remove itself which will
+        // result in a concurrent modification exception. To counteract this we make a copy of the
+        // listeners and iterate on that.
+        for (Listener l : new ArrayList<>(mListeners)) {
+            if (mListeners.contains(l)) {
+                l.onDestroyed(this);
+            }
+        }
     }
 
     /**
@@ -708,7 +504,9 @@
      * @param details A {@link android.os.Bundle} containing details of the current level.
      */
     public final void setSignal(Bundle details) {
-        mHandler.obtainMessage(MSG_SET_SIGNAL, details).sendToTarget();
+        for (Listener l : mListeners) {
+            l.onSignalChanged(this, details);
+        }
     }
 
     /**
@@ -717,7 +515,10 @@
      * @param isVoip True if the audio mode is VOIP.
      */
     public final void setAudioModeIsVoip(boolean isVoip) {
-        mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, Boolean.valueOf(isVoip)).sendToTarget();
+        mAudioModeIsVoip = isVoip;
+        for (Listener l : mListeners) {
+            l.onAudioModeIsVoipChanged(this, isVoip);
+        }
     }
 
     /**
@@ -726,7 +527,10 @@
      * @param statusHints The status label and icon to set.
      */
     public final void setStatusHints(StatusHints statusHints) {
-        mHandler.obtainMessage(MSG_SET_STATUS_HINTS, statusHints).sendToTarget();
+        mStatusHints = statusHints;
+        for (Listener l : mListeners) {
+            l.onStatusHintsChanged(this, statusHints);
+        }
     }
 
     /**
@@ -738,13 +542,13 @@
         if (!intent.isActivity()) {
             throw new IllegalArgumentException("Activity intent required.");
         }
-        mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, intent).sendToTarget();
+        for (Listener l : mListeners) {
+            l.onStartActivityFromInCall(this, intent);
+        }
     }
 
     /**
      * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
-     * <p>
-     * This callback will happen on the main thread.
      *
      * @param state The new call audio state.
      */
@@ -753,8 +557,6 @@
     /**
      * Notifies this Connection of an internal state change. This method is called after the
      * state is changed.
-     * <p>
-     * This callback will happen on the main thread.
      *
      * @param state The new state, a {@link Connection.State} member.
      */
@@ -762,8 +564,6 @@
 
     /**
      * Notifies this Connection of a request to play a DTMF tone.
-     * <p>
-     * This callback will happen on the main thread.
      *
      * @param c A DTMF character.
      */
@@ -771,81 +571,61 @@
 
     /**
      * Notifies this Connection of a request to stop any currently playing DTMF tones.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onStopDtmfTone() {}
 
     /**
      * Notifies this Connection of a request to disconnect.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onDisconnect() {}
 
     /**
      * Notifies this Connection of a request to disconnect.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onSeparate() {}
 
     /**
      * Notifies this Connection of a request to abort.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onAbort() {}
 
     /**
      * Notifies this Connection of a request to hold.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onHold() {}
 
     /**
      * Notifies this Connection of a request to exit a hold state.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onUnhold() {}
 
     /**
-     * Notifies this Connection, which is in {@link State#RINGING}, of a request to accept.
-     * <p>
-     * This callback will happen on the main thread.
+     * Notifies this Connection, which is in {@link State#RINGING}, of
+     * a request to accept.
      *
      * @param videoState The video state in which to answer the call.
      */
     public void onAnswer(int videoState) {}
 
     /**
-     * Notifies this Connection, which is in {@link State#RINGING}, of a request to reject.
-     * <p>
-     * This callback will happen on the main thread.
+     * Notifies this Connection, which is in {@link State#RINGING}, of
+     * a request to reject.
      */
     public void onReject() {}
 
     /**
      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onPostDialContinue(boolean proceed) {}
 
     /**
      * Swap this call with a background call. This is used for calls that don't support hold,
      * e.g. CDMA.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onSwapWithBackgroundCall() {}
 
     /**
      * TODO(santoscordon): Needs documentation.
-     * <p>
-     * This callback will happen on the main thread.
      */
     public void onChildrenChanged(List<Connection> children) {}
 
@@ -854,14 +634,12 @@
      */
     public void onPhoneAccountClicked() {}
 
-    /** This must be called from the main thread. */
     private void addChild(Connection connection) {
         Log.d(this, "adding child %s", connection);
         mChildConnections.add(connection);
         onChildrenChanged(mChildConnections);
     }
 
-    /** This must be called from the main thread. */
     private void removeChild(Connection connection) {
         Log.d(this, "removing child %s", connection);
         mChildConnections.remove(connection);