Add support for rejecting Telecom call with a specified reason.

Piping through the reject with reason API.

Bug: 135929421
Test: Added new CTS test to validate API pathways.
Test: Ran existing telecom and telephony unit tests.
Test: Modified test dialer app to use the new reject API and verified that
the reject reason signals down to the modem and translates to the correct
reject cause.

Change-Id: I6782a502c62f1d0c6b6a8802bf17ea9b62621b34
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index e158230..b23e198 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -59,7 +59,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.IVideoProvider;
 import com.android.internal.util.Preconditions;
-import com.android.server.telecom.ui.DisconnectedCallNotifier;
 import com.android.server.telecom.ui.ToastFactory;
 
 import java.io.IOException;
@@ -2274,6 +2273,38 @@
     }
 
     /**
+     * Reject this Telecom call with the user-indicated reason.
+     * @param rejectReason The user-indicated reason fore rejecting the call.
+     */
+    public void reject(@android.telecom.Call.RejectReason int rejectReason) {
+        if (mState == CallState.SIMULATED_RINGING) {
+            // This handles the case where the user manually rejects a call that's in simulated
+            // ringing. Since the call is already active on the connectionservice side, we want to
+            // hangup, not reject.
+            // Since its simulated reason we can't pass along the reject reason.
+            setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
+            if (mConnectionService != null) {
+                mConnectionService.disconnect(this);
+            } else {
+                Log.e(this, new NullPointerException(),
+                        "reject call failed due to null CS callId=%s", getId());
+            }
+            Log.addEvent(this, LogUtils.Events.REQUEST_REJECT);
+        } else if (isRinging("reject")) {
+            // Ensure video state history tracks video state at time of rejection.
+            mVideoStateHistory |= mVideoState;
+
+            if (mConnectionService != null) {
+                mConnectionService.rejectWithReason(this, rejectReason);
+            } else {
+                Log.e(this, new NullPointerException(),
+                        "reject call failed due to null CS callId=%s", getId());
+            }
+            Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, rejectReason);
+        }
+    }
+
+    /**
      * Puts the call on hold if it is currently active.
      */
     @VisibleForTesting
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 024a863..0643d3a 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -2364,6 +2364,25 @@
     }
 
     /**
+     * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
+     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
+     * the user opting to reject said call.
+     */
+    @VisibleForTesting
+    public void rejectCall(Call call, @android.telecom.Call.RejectReason int rejectReason) {
+        if (!mCalls.contains(call)) {
+            Log.i(this, "Request to reject a non-existent call %s", call);
+        } else {
+            for (CallsManagerListener listener : mListeners) {
+                listener.onIncomingCallRejected(call, false /* rejectWithMessage */,
+                        null /* textMessage */);
+            }
+            call.reject(rejectReason);
+        }
+    }
+
+
+    /**
      * Instructs Telecom to play the specified DTMF tone within the specified call.
      *
      * @param digit The DTMF digit to play.
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index ca513da..248374d 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -1551,6 +1551,19 @@
         }
     }
 
+    /** @see IConnectionService#reject(String, Session.Info) */
+    void rejectWithReason(Call call, @android.telecom.Call.RejectReason int rejectReason) {
+        final String callId = mCallIdMapper.getCallId(call);
+        if (callId != null && isServiceValid("rejectReason")) {
+            try {
+                logOutgoing("rejectReason %s, %d", callId, rejectReason);
+
+                mServiceInterface.rejectWithReason(callId, rejectReason, Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
     void playDtmfTone(Call call, char digit) {
         final String callId = mCallIdMapper.getCallId(call);
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index 0c4c1db..3e2fa12 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -126,6 +126,32 @@
     }
 
     @Override
+    public void rejectCallWithReason(String callId,
+            @android.telecom.Call.RejectReason int rejectReason) {
+        try {
+            Log.startSession(LogUtils.Sessions.ICA_REJECT_CALL, mOwnerPackageName);
+
+            int callingUid = Binder.getCallingUid();
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.d(this, "rejectCallWithReason(%s,%d)", callId, rejectReason);
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.rejectCall(call, rejectReason);
+                    } else {
+                        Log.w(this, "rejectCallWithReason, unknown call id: %s", callId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
+
+    @Override
     public void playDtmfTone(String callId, char digit) {
         try {
             Log.startSession("ICA.pDT", mOwnerPackageName);
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index bcac5f1..315b074 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -306,6 +306,11 @@
             rejectedCallIds.add(callId);
         }
 
+        @Override public void rejectWithReason(java.lang.String callId, int rejectReason,
+                android.telecom.Logging.Session.Info sessionInfo) throws RemoteException {
+            rejectedCallIds.add(callId);
+        }
+
         @Override
         public void rejectWithMessage(String callId, String message,
                 Session.Info info) throws RemoteException {