DO NOT MERGE. Implement connection error dialogs (1/4)

Implement reporting of connection errors from ConnectionServices through
Telecomm to the InCallUI.

Bug: 15195720
Bug: 15117141
Change-Id: I0e1443f75a175a212fb19afde5a7eadef15d239d
(cherry picked from commit 976bfe497da31fa8322969648ffbe77898941d0c)
diff --git a/api/current.txt b/api/current.txt
index 0c18c56..078cdc5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27596,7 +27596,7 @@
   }
 
   public final class CallServiceAdapter {
-    method public void handleFailedOutgoingCall(java.lang.String, java.lang.String);
+    method public void handleFailedOutgoingCall(android.telecomm.ConnectionRequest, int, java.lang.String);
     method public void handleSuccessfulOutgoingCall(java.lang.String);
     method public void notifyIncomingCall(android.telecomm.CallInfo);
     method public void setActive(java.lang.String);
@@ -27723,10 +27723,15 @@
     field public static final int RINGING = 1; // 0x1
   }
 
-  public final class ConnectionRequest {
+  public final class ConnectionRequest implements android.os.Parcelable {
     ctor public ConnectionRequest(android.net.Uri, android.os.Bundle);
+    ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle);
+    method public int describeContents();
+    method public java.lang.String getCallId();
     method public android.os.Bundle getExtras();
     method public android.net.Uri getHandle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
   }
 
   public abstract class ConnectionService extends android.telecomm.CallService {
@@ -27777,7 +27782,8 @@
     method public int getCapabilities();
     method public long getConnectTimeMillis();
     method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
-    method public int getDisconnectCause();
+    method public int getDisconnectCauseCode();
+    method public java.lang.String getDisconnectCauseMsg();
     method public android.telecomm.GatewayInfo getGatewayInfo();
     method public android.net.Uri getHandle();
     method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor();
@@ -27801,7 +27807,7 @@
   }
 
   public abstract interface Response {
-    method public abstract void onError(IN, java.lang.String);
+    method public abstract void onError(IN, int, java.lang.String);
     method public abstract void onResult(IN, OUT...);
   }
 
@@ -27982,6 +27988,7 @@
     field public static final int CALL_BARRED = 20; // 0x14
     field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
     field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+    field public static final int CDMA_CALL_LOST = 41; // 0x29
     field public static final int CDMA_DROP = 27; // 0x1b
     field public static final int CDMA_INTERCEPT = 28; // 0x1c
     field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
@@ -27994,6 +28001,8 @@
     field public static final int CS_RESTRICTED = 22; // 0x16
     field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
     field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+    field public static final int DIALED_MMI = 39; // 0x27
+    field public static final int EMERGENCY_ONLY = 37; // 0x25
     field public static final int ERROR_UNSPECIFIED = 36; // 0x24
     field public static final int FDN_BLOCKED = 21; // 0x15
     field public static final int ICC_ERROR = 19; // 0x13
@@ -28004,12 +28013,13 @@
     field public static final int LIMIT_EXCEEDED = 15; // 0xf
     field public static final int LOCAL = 3; // 0x3
     field public static final int LOST_SIGNAL = 14; // 0xe
-    field public static final int MAXIMUM_VALID_VALUE = 36; // 0x24
+    field public static final int MAXIMUM_VALID_VALUE = 42; // 0x2a
     field public static final int MINIMUM_VALID_VALUE = 0; // 0x0
     field public static final int MMI = 6; // 0x6
     field public static final int NORMAL = 2; // 0x2
     field public static final int NOT_DISCONNECTED = 0; // 0x0
     field public static final int NOT_VALID = -1; // 0xffffffff
+    field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
     field public static final int NUMBER_UNREACHABLE = 8; // 0x8
     field public static final int OUT_OF_NETWORK = 11; // 0xb
     field public static final int OUT_OF_SERVICE = 18; // 0x12
@@ -28018,6 +28028,7 @@
     field public static final int SERVER_UNREACHABLE = 9; // 0x9
     field public static final int TIMED_OUT = 13; // 0xd
     field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+    field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
   }
 
   public class NeighboringCellInfo implements android.os.Parcelable {
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index 7396808..0c57828 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -84,12 +84,16 @@
     /**
      * Tells Telecomm that an attempt to place the specified outgoing call failed.
      *
-     * @param callId The ID of the outgoing call.
-     * @param errorMessage The error associated with the failed call attempt.
+     * @param request The originating request for a connection.
+     * @param errorCode The error code associated with the failed call attempt.
+     * @param errorMsg The error message associated with the failed call attempt.
      */
-    public void handleFailedOutgoingCall(String callId, String errorMessage) {
+    public void handleFailedOutgoingCall(
+            ConnectionRequest request,
+            int errorCode,
+            String errorMsg) {
         try {
-            mAdapter.handleFailedOutgoingCall(callId, errorMessage);
+            mAdapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
         } catch (RemoteException e) {
         }
     }
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.aidl b/telecomm/java/android/telecomm/ConnectionRequest.aidl
new file mode 100644
index 0000000..72e5c8c
--- /dev/null
+++ b/telecomm/java/android/telecomm/ConnectionRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014, 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 android.telecomm;
+
+parcelable ConnectionRequest;
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index c1f1871..bf5727b 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -18,23 +18,37 @@
 
 import android.os.Bundle;
 import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 /**
  * Simple data container encapsulating a request to some entity to
  * create a new {@link Connection}.
  */
-public final class ConnectionRequest {
+public final class ConnectionRequest implements Parcelable {
 
     // TODO: Token to limit recursive invocations
     // TODO: Consider upgrading "mHandle" to ordered list of handles, indicating a set of phone
     //         numbers that would satisfy the client's needs, in order of preference
+    private final String mCallId;
     private final Uri mHandle;
     private final Bundle mExtras;
 
     public ConnectionRequest(Uri handle, Bundle extras) {
-        mHandle = handle; mExtras = extras;
+        this(null, handle, extras);
     }
 
+    public ConnectionRequest(String callId, Uri handle, Bundle extras) {
+        mCallId = callId;
+        mHandle = handle;
+        mExtras = extras;
+    }
+
+    /**
+     * An identifier for this call.
+     */
+    public String getCallId() { return mCallId; }
+
     /**
      * The handle (e.g., phone number) to which the {@link Connection} is to connect.
      */
@@ -54,4 +68,40 @@
                         : ConnectionService.toLogSafePhoneNumber(mHandle.toString()),
                 mExtras == null ? "" : mExtras);
     }
-}
+
+    /**
+     * Responsible for creating CallInfo objects for deserialized Parcels.
+     */
+    public static final Parcelable.Creator<ConnectionRequest> CREATOR =
+            new Parcelable.Creator<ConnectionRequest> () {
+                @Override
+                public ConnectionRequest createFromParcel(Parcel source) {
+                    String callId = source.readString();
+                    Uri handle = (Uri) source.readParcelable(getClass().getClassLoader());
+                    Bundle extras = (Bundle) source.readParcelable(getClass().getClassLoader());
+                    return new ConnectionRequest(callId, handle, extras);
+                }
+
+                @Override
+                public ConnectionRequest[] newArray(int size) {
+                    return new ConnectionRequest[size];
+                }
+            };
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Writes CallInfo object into a serializeable Parcel.
+     */
+    @Override
+    public void writeToParcel(Parcel destination, int flags) {
+        destination.writeString(mCallId);
+        destination.writeParcelable(mHandle, 0);
+        destination.writeParcelable(mExtras, 0);
+    }}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index aeb1c33..31de15c 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -18,6 +18,7 @@
 
 import android.net.Uri;
 import android.os.Bundle;
+import android.telephony.DisconnectCause;
 
 import java.util.HashMap;
 import java.util.LinkedList;
@@ -115,9 +116,8 @@
                     }
 
                     @Override
-                    public void onError(Uri handle, String reason) {
-                        Log.w(this, "Error in onFindSubscriptions " + callInfo.getHandle()
-                                + " error: " + reason);
+                    public void onError(Uri handle, int code, String msg) {
+                        Log.w(this, "Error in onFindSubscriptions %s %d %s", handle, code, msg);
                         getAdapter().setIsCompatibleWith(callInfo.getId(), false);
                     }
                 }
@@ -129,6 +129,7 @@
         Log.d(this, "call %s", callInfo);
         onCreateConnections(
                 new ConnectionRequest(
+                        callInfo.getId(),
                         callInfo.getHandle(),
                         callInfo.getExtras()),
                 new Response<ConnectionRequest, Connection>() {
@@ -137,7 +138,8 @@
                         if (result.length != 1) {
                             Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo);
                             getAdapter().handleFailedOutgoingCall(
-                                    callInfo.getId(),
+                                    request,
+                                    DisconnectCause.ERROR_UNSPECIFIED,
                                     "Created " + result.length + " Connections, expected 1");
                             for (Connection c : result) {
                                 c.abort();
@@ -150,8 +152,8 @@
                     }
 
                     @Override
-                    public void onError(ConnectionRequest request, String reason) {
-                        getAdapter().handleFailedOutgoingCall(callInfo.getId(), reason);
+                    public void onError(ConnectionRequest request, int code, String msg) {
+                        getAdapter().handleFailedOutgoingCall(request, code, msg);
                     }
                 }
         );
@@ -168,6 +170,7 @@
         Log.d(this, "setIncomingCallId %s %s", callId, extras);
         onCreateIncomingConnection(
                 new ConnectionRequest(
+                        callId,
                         null,  // TODO: Can we obtain this from "extras"?
                         extras),
                 new Response<ConnectionRequest, Connection>() {
@@ -176,7 +179,8 @@
                         if (result.length != 1) {
                             Log.d(this, "adapter handleFailedOutgoingCall %s", callId);
                             getAdapter().handleFailedOutgoingCall(
-                                    callId,
+                                    request,
+                                    DisconnectCause.ERROR_UNSPECIFIED,
                                     "Created " + result.length + " Connections, expected 1");
                             for (Connection c : result) {
                                 c.abort();
@@ -195,8 +199,9 @@
                     }
 
                     @Override
-                    public void onError(ConnectionRequest request, String reason) {
-                        Log.d(this, "adapter failed setIncomingCallId %s %s", request, reason);
+                    public void onError(ConnectionRequest request, int code, String msg) {
+                        Log.d(this, "adapter failed setIncomingCallId %s %d %s",
+                                request, code, msg);
                     }
                 }
         );
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index 346d207..b531ccd 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -31,7 +31,8 @@
 public final class InCallCall implements Parcelable {
     private final String mId;
     private final CallState mState;
-    private final int mDisconnectCause;
+    private final int mDisconnectCauseCode;
+    private final String mDisconnectCauseMsg;
     private final int mCapabilities;
     private final long mConnectTimeMillis;
     private final Uri mHandle;
@@ -47,15 +48,16 @@
     public InCallCall(
             String id,
             CallState state,
-            int disconnectCause,
+            int disconnectCauseCode,
+            String disconnectCauseMsg,
             int capabilities,
             long connectTimeMillis,
             Uri handle,
             GatewayInfo gatewayInfo,
             CallServiceDescriptor descriptor,
             CallServiceDescriptor handoffDescriptor) {
-        this(id, state, disconnectCause, capabilities, connectTimeMillis, handle, gatewayInfo,
-                descriptor, handoffDescriptor, Collections.EMPTY_LIST, null,
+        this(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities, connectTimeMillis,
+                handle, gatewayInfo, descriptor, handoffDescriptor, Collections.EMPTY_LIST, null,
                 Collections.EMPTY_LIST);
     }
 
@@ -63,7 +65,8 @@
     public InCallCall(
             String id,
             CallState state,
-            int disconnectCause,
+            int disconnectCauseCode,
+            String disconnectCauseMsg,
             int capabilities,
             long connectTimeMillis,
             Uri handle,
@@ -75,7 +78,8 @@
             List<String> childCallIds) {
         mId = id;
         mState = state;
-        mDisconnectCause = disconnectCause;
+        mDisconnectCauseCode = disconnectCauseCode;
+        mDisconnectCauseMsg = disconnectCauseMsg;
         mCapabilities = capabilities;
         mConnectTimeMillis = connectTimeMillis;
         mHandle = handle;
@@ -101,8 +105,16 @@
      * Reason for disconnection, values are defined in {@link DisconnectCause}. Valid when call
      * state is {@link CallState#DISCONNECTED}.
      */
-    public int getDisconnectCause() {
-        return mDisconnectCause;
+    public int getDisconnectCauseCode() {
+        return mDisconnectCauseCode;
+    }
+
+    /**
+     * Further optional textual information about the reason for disconnection. Valid when call
+     * state is {@link CallState#DISCONNECTED}.
+     */
+    public String getDisconnectCauseMsg() {
+        return mDisconnectCauseMsg;
     }
 
     // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
@@ -170,7 +182,8 @@
         public InCallCall createFromParcel(Parcel source) {
             String id = source.readString();
             CallState state = CallState.valueOf(source.readString());
-            int disconnectCause = source.readInt();
+            int disconnectCauseCode = source.readInt();
+            String disconnectCauseMsg = source.readString();
             int capabilities = source.readInt();
             long connectTimeMillis = source.readLong();
             ClassLoader classLoader = InCallCall.class.getClassLoader();
@@ -183,9 +196,9 @@
             String parentCallId = source.readString();
             List<String> childCallIds = new ArrayList<>();
             source.readList(childCallIds, classLoader);
-            return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis,
-                    handle, gatewayInfo, descriptor, handoffDescriptor, conferenceCapableCallIds,
-                    parentCallId, childCallIds);
+            return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities,
+                    connectTimeMillis, handle, gatewayInfo, descriptor, handoffDescriptor,
+                    conferenceCapableCallIds, parentCallId, childCallIds);
         }
 
         @Override
@@ -205,7 +218,8 @@
     public void writeToParcel(Parcel destination, int flags) {
         destination.writeString(mId);
         destination.writeString(mState.name());
-        destination.writeInt(mDisconnectCause);
+        destination.writeInt(mDisconnectCauseCode);
+        destination.writeString(mDisconnectCauseMsg);
         destination.writeInt(mCapabilities);
         destination.writeLong(mConnectTimeMillis);
         destination.writeParcelable(mHandle, 0);
diff --git a/telecomm/java/android/telecomm/Response.java b/telecomm/java/android/telecomm/Response.java
index 14f8340..13c0702 100644
--- a/telecomm/java/android/telecomm/Response.java
+++ b/telecomm/java/android/telecomm/Response.java
@@ -33,7 +33,8 @@
      * Indicates the inability to provide results.
      *
      * @param request The original request.
-     * @param reason The reason for the failure.
+     * @param code An integer code indicating the reason for failure.
+     * @param msg A message explaining the reason for failure.
      */
-    void onError(IN request, String reason);
+    void onError(IN request, int code, String msg);
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index a92b176..f94eb32 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telecomm;
 
 import android.telecomm.CallInfo;
+import android.telecomm.ConnectionRequest;
 
 /**
  * Internal remote callback interface for call services.
@@ -32,7 +33,7 @@
 
     void handleSuccessfulOutgoingCall(String callId);
 
-    void handleFailedOutgoingCall(String callId, String errorMessage);
+    void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
 
     void setActive(String callId);
 
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 8681344..d2044be 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -97,11 +97,62 @@
     public static final int CDMA_ACCESS_BLOCKED            = 35;
     /** Unknown error or not specified */
     public static final int ERROR_UNSPECIFIED              = 36;
+    /**
+     * Only emergency numbers are allowed, but we tried to dial
+     * a non-emergency number.
+     */
+    // TODO: This should be the same as NOT_EMERGENCY
+    public static final int EMERGENCY_ONLY                 = 37;
+    /**
+     * The supplied CALL Intent didn't contain a valid phone number.
+     */
+    public static final int NO_PHONE_NUMBER_SUPPLIED       = 38;
+    /**
+     * Our initial phone number was actually an MMI sequence.
+     */
+    public static final int DIALED_MMI                     = 39;
+    /**
+     * We tried to call a voicemail: URI but the device has no
+     * voicemail number configured.
+     */
+    public static final int VOICEMAIL_NUMBER_MISSING       = 40;
+    /**
+     * This status indicates that InCallScreen should display the
+     * CDMA-specific "call lost" dialog.  (If an outgoing call fails,
+     * and the CDMA "auto-retry" feature is enabled, *and* the retried
+     * call fails too, we display this specific dialog.)
+     *
+     * TODO: this is currently unused, since the "call lost" dialog
+     * needs to be triggered by a *disconnect* event, rather than when
+     * the InCallScreen first comes to the foreground.  For now we use
+     * the needToShowCallLostDialog field for this (see below.)
+     */
+    public static final int CDMA_CALL_LOST                 = 41;
+    /**
+     * This status indicates that the call was placed successfully,
+     * but additionally, the InCallScreen needs to display the
+     * "Exiting ECM" dialog.
+     *
+     * (Details: "Emergency callback mode" is a CDMA-specific concept
+     * where the phone disallows data connections over the cell
+     * network for some period of time after you make an emergency
+     * call.  If the phone is in ECM and you dial a non-emergency
+     * number, that automatically *cancels* ECM, but we additionally
+     * need to warn the user that ECM has been canceled (see bug
+     * 4207607.))
+     *
+     * TODO: Rethink where the best place to put this is. It is not a notification
+     * of a failure of the connection -- it is an additional message that accompanies
+     * a successful connection giving the user important information about what happened.
+     *
+     * {@hide}
+     */
+    public static final int EXITED_ECM                     = 42;
 
     /** Smallest valid value for call disconnect codes. */
     public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
     /** Largest valid value for call disconnect codes. */
-    public static final int MAXIMUM_VALID_VALUE = ERROR_UNSPECIFIED;
+    public static final int MAXIMUM_VALID_VALUE = EXITED_ECM;
 
     /** Private constructor to avoid class instantiation. */
     private DisconnectCause() {
@@ -181,6 +232,18 @@
             return "CDMA_NOT_EMERGENCY";
         case CDMA_ACCESS_BLOCKED:
             return "CDMA_ACCESS_BLOCKED";
+        case EMERGENCY_ONLY:
+            return "EMERGENCY_ONLY";
+        case NO_PHONE_NUMBER_SUPPLIED:
+            return "NO_PHONE_NUMBER_SUPPLIED";
+        case DIALED_MMI:
+            return "DIALED_MMI";
+        case VOICEMAIL_NUMBER_MISSING:
+            return "VOICEMAIL_NUMBER_MISSING";
+        case CDMA_CALL_LOST:
+            return "CDMA_CALL_LOST";
+        case EXITED_ECM:
+            return "EXITED_ECM";
         case ERROR_UNSPECIFIED:
             return "ERROR_UNSPECIFIED";
         default: