Merge "Add API to cancel outgoing calls"
diff --git a/api/current.txt b/api/current.txt
index d9d17df..fbbae6c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27604,6 +27604,7 @@
   public final class CallServiceAdapter implements android.os.IBinder.DeathRecipient {
     method public void addConferenceCall(java.lang.String);
     method public void binderDied();
+    method public void cancelOutgoingCall(java.lang.String);
     method public void handleFailedOutgoingCall(android.telecomm.ConnectionRequest, int, java.lang.String);
     method public void handleSuccessfulOutgoingCall(java.lang.String);
     method public void handoffCall(java.lang.String);
@@ -27750,7 +27751,7 @@
     method public final void abort(java.lang.String);
     method public final void answer(java.lang.String);
     method public final void call(android.telecomm.CallInfo);
-    method public void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.SimpleResponse<android.telecomm.ConnectionRequest, android.telecomm.RemoteConnection>);
+    method public void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>);
     method public final void disconnect(java.lang.String);
     method public java.util.Collection<android.telecomm.Connection> getAllConnections();
     method public final void hold(java.lang.String);
@@ -27760,7 +27761,7 @@
     method public void onConnectionAdded(android.telecomm.Connection);
     method public void onConnectionRemoved(android.telecomm.Connection);
     method public void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
-    method public void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
+    method public void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.Connection>);
     method public void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
     method public final void onPostDialContinue(java.lang.String, boolean);
     method public final void onPostDialWait(android.telecomm.Connection, java.lang.String);
@@ -27771,6 +27772,12 @@
     method public final void unhold(java.lang.String);
   }
 
+  public static abstract interface ConnectionService.OutgoingCallResponse {
+    method public abstract void onCancel(android.telecomm.ConnectionRequest);
+    method public abstract void onFailure(android.telecomm.ConnectionRequest, int, java.lang.String);
+    method public abstract void onSuccess(android.telecomm.ConnectionRequest, CONNECTION);
+  }
+
   public class GatewayInfo implements android.os.Parcelable {
     method public int describeContents();
     method public android.net.Uri getGatewayHandle();
@@ -27851,7 +27858,7 @@
 
   public class RemoteConnectionService implements android.os.IBinder.DeathRecipient {
     method public void binderDied();
-    method public void createOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.SimpleResponse<android.telecomm.ConnectionRequest, android.telecomm.RemoteConnection>);
+    method public void createOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>);
     method public java.util.List<android.telecomm.Subscription> lookupSubscriptions(android.net.Uri);
   }
 
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index 30084d0..31e37c4 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -135,6 +135,20 @@
     }
 
     /**
+     * Tells Telecomm to cancel the call.
+     *
+     * @param callId The ID of the outgoing call.
+     */
+    public void cancelOutgoingCall(String callId) {
+        for (ICallServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.cancelOutgoingCall(callId);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    /**
      * Sets a call's state to active (e.g., an ongoing call where two parties can actively
      * communicate).
      *
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 4b69a3c..23b99fa 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -52,6 +52,35 @@
     private Uri mSubscriptionLookupHandle;
     private boolean mAreSubscriptionsInitialized = false;
 
+    /**
+     * A callback for providing the resuilt of creating a connection.
+     */
+    public interface OutgoingCallResponse<CONNECTION> {
+        /**
+         * Tells Telecomm that an attempt to place the specified outgoing call succeeded.
+         *
+         * @param request The original request.
+         * @param connection The connection.
+         */
+        void onSuccess(ConnectionRequest request, CONNECTION connection);
+
+        /**
+         * Tells Telecomm that an attempt to place the specified outgoing call failed.
+         *
+         * @param request The original request.
+         * @param code An integer code indicating the reason for failure.
+         * @param msg A message explaining the reason for failure.
+         */
+        void onFailure(ConnectionRequest request, int code, String msg);
+
+        /**
+         * Tells Telecomm to cancel the call.
+         *
+         * @param request The original request.
+         */
+        void onCancel(ConnectionRequest request);
+    }
+
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
         @Override
         public void onStateChanged(Connection c, int state) {
@@ -136,30 +165,24 @@
                         callInfo.getId(),
                         callInfo.getHandle(),
                         callInfo.getExtras()),
-                new Response<ConnectionRequest, Connection>() {
+                new OutgoingCallResponse<Connection>() {
                     @Override
-                    public void onResult(ConnectionRequest request, Connection... result) {
-                        if (result != null && result.length != 1) {
-                            Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo);
-                            getAdapter().handleFailedOutgoingCall(
-                                    request,
-                                    DisconnectCause.ERROR_UNSPECIFIED,
-                                    "Created " + result.length + " Connections, expected 1");
-                            for (Connection c : result) {
-                                c.abort();
-                            }
-                        } else {
-                            Log.d(this, "adapter handleSuccessfulOutgoingCall %s",
-                                    callInfo.getId());
-                            getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
-                            addConnection(callInfo.getId(), result[0]);
-                        }
+                    public void onSuccess(ConnectionRequest request, Connection connection) {
+                        Log.d(this, "adapter handleSuccessfulOutgoingCall %s",
+                                callInfo.getId());
+                        getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
+                        addConnection(callInfo.getId(), connection);
                     }
 
                     @Override
-                    public void onError(ConnectionRequest request, int code, String msg) {
+                    public void onFailure(ConnectionRequest request, int code, String msg) {
                         getAdapter().handleFailedOutgoingCall(request, code, msg);
                     }
+
+                    @Override
+                    public void onCancel(ConnectionRequest request) {
+                        getAdapter().cancelOutgoingCall(callInfo.getId());
+                    }
                 }
         );
     }
@@ -389,7 +412,7 @@
 
     public void createRemoteOutgoingConnection(
             ConnectionRequest request,
-            SimpleResponse<ConnectionRequest, RemoteConnection> response) {
+            OutgoingCallResponse<RemoteConnection> response) {
         mRemoteConnectionManager.createOutgoingConnection(request, response);
     }
 
@@ -408,7 +431,7 @@
      */
     public void onCreateConnections(
             ConnectionRequest request,
-            Response<ConnectionRequest, Connection> callback) {}
+            OutgoingCallResponse<Connection> callback) {}
 
     /**
      * Returns a new or existing conference connection when the the user elects to convert the
diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java
index 4201f23..465fae0 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java
@@ -57,7 +57,7 @@
 
     public void createOutgoingConnection(
             ConnectionRequest request,
-            final SimpleResponse<ConnectionRequest, RemoteConnection> response) {
+            final ConnectionService.OutgoingCallResponse response) {
         Subscription subscription = request.getSubscription();
         if (subscription == null) {
             throw new IllegalArgumentException("subscription must be specified.");
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 86a43cb..7658a76 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -43,7 +43,7 @@
 
     private String mConnectionId;
     private ConnectionRequest mPendingRequest;
-    private SimpleResponse<ConnectionRequest, RemoteConnection> mPendingResponse;
+    private ConnectionService.OutgoingCallResponse<RemoteConnection> mPendingOutgoingCallResponse;
     // Remote connection services only support a single connection.
     private RemoteConnection mConnection;
 
@@ -60,7 +60,7 @@
         public void handleSuccessfulOutgoingCall(String connectionId) {
             if (isPendingConnection(connectionId)) {
                 mConnection = new RemoteConnection(mCallService, connectionId);
-                mPendingResponse.onResult(mPendingRequest, mConnection);
+                mPendingOutgoingCallResponse.onSuccess(mPendingRequest, mConnection);
                 clearPendingInformation();
             }
         }
@@ -72,7 +72,17 @@
             if (isPendingConnection(request.getCallId())) {
                 // Use mPendingRequest instead of request so that we use the same object that was
                 // passed in to us.
-                mPendingResponse.onError(request);
+                mPendingOutgoingCallResponse.onFailure(mPendingRequest, errorCode, errorMessage);
+                mConnectionId = null;
+                clearPendingInformation();
+            }
+        }
+
+        /** ${inheritDoc} */
+            @Override
+        public void cancelOutgoingCall(String connectionId) {
+            if (isPendingConnection(connectionId)) {
+                mPendingOutgoingCallResponse.onCancel(mPendingRequest);
                 mConnectionId = null;
                 clearPendingInformation();
             }
@@ -208,7 +218,7 @@
      */
     public void createOutgoingConnection(
             ConnectionRequest request,
-            SimpleResponse<ConnectionRequest, RemoteConnection> response) {
+            ConnectionService.OutgoingCallResponse<RemoteConnection> response) {
 
         if (mConnectionId == null) {
             String id = UUID.randomUUID().toString();
@@ -216,13 +226,13 @@
             try {
                 mCallService.call(callInfo);
                 mConnectionId = id;
-                mPendingResponse = response;
+                mPendingOutgoingCallResponse = response;
                 mPendingRequest = request;
             } catch (RemoteException e) {
-                response.onError(request);
+                response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
             }
         } else {
-            response.onError(request);
+            response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, null);
         }
     }
 
@@ -254,7 +264,7 @@
     }
 
     private boolean isPendingConnection(String id) {
-        return TextUtils.equals(mConnectionId, id) && mPendingResponse != null;
+        return TextUtils.equals(mConnectionId, id) && mPendingOutgoingCallResponse != null;
     }
 
     private boolean isCurrentConnection(String id) {
@@ -263,7 +273,7 @@
 
     private void clearPendingInformation() {
         mPendingRequest = null;
-        mPendingResponse = null;
+        mPendingOutgoingCallResponse = null;
     }
 
     private void destroyConnection() {
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index 87c8859..373fb16 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -35,6 +35,8 @@
 
     void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
 
+    void cancelOutgoingCall(String callId);
+
     void setActive(String callId);
 
     void setRinging(String callId);