add capability to send and receive events

bug: 269512817
Test: CTS
Change-Id: I5f4561192b7c8ab978754e3a7da6549d35c61b1d
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index 2135e27..6b2bea0 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -147,10 +147,8 @@
      *                        <li>{@link DisconnectCause#REJECTED}</li>
      *                        <li>{@link DisconnectCause#MISSED}</li>
      *                        </ul>
-     *
      * @param executor        The {@link Executor} on which the {@link OutcomeReceiver} callback
      *                        will be called on.
-     *
      * @param callback        That will be completed on the Telecom side that details success or
      *                        failure of the requested operation.
      *
@@ -254,6 +252,36 @@
     }
 
     /**
+     * Raises an event to the {@link android.telecom.InCallService} implementations tracking this
+     * call via {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+     * These events and the associated extra keys for the {@code Bundle} parameter are defined
+     * in Android X. This API is used to relay additional information about a call other than
+     * what is specified in the {@link android.telecom.CallAttributes} to
+     * {@link android.telecom.InCallService}s. This might include, for example, a change to the list
+     * of participants in a meeting, or the name of the speakers who have their hand raised. Where
+     * appropriate, the {@link InCallService}s tracking this call may choose to render this
+     * additional information about the call. An automotive calling UX, for example may have enough
+     * screen real estate to indicate the number of participants in a meeting, but to prevent
+     * distractions could suppress the list of participants.
+     *
+     * @param event  that is defined in AndroidX (ex. The number of participants changed)
+     * @param extras the updated value in relation to the event (ex. 4 participants)
+     */
+    public void sendEvent(@NonNull String event, @NonNull Bundle extras) {
+        Objects.requireNonNull(event);
+        Objects.requireNonNull(extras);
+        if (mServerInterface != null) {
+            try {
+                mServerInterface.sendEvent(mCallId, event, extras);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        } else {
+            throw new IllegalStateException(INTERFACE_ERROR_MSG);
+        }
+    }
+
+    /**
      * Since {@link OutcomeReceiver}s cannot be passed via AIDL, a ResultReceiver (which can) must
      * wrap the Clients {@link OutcomeReceiver} passed in and await for the Telecom Server side
      * response in {@link ResultReceiver#onReceiveResult(int, Bundle)}.
diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java
index bfe3685..d96c406 100644
--- a/telecomm/java/android/telecom/CallEventCallback.java
+++ b/telecomm/java/android/telecom/CallEventCallback.java
@@ -17,6 +17,7 @@
 package android.telecom;
 
 import android.annotation.NonNull;
+import android.os.Bundle;
 
 import java.util.List;
 
@@ -56,4 +57,17 @@
      * @param reason Code to indicate the reason of this failure
      */
     void onCallStreamingFailed(@CallStreamingService.StreamingFailedReason int reason);
+
+    /**
+     * Informs this {@link android.telecom.CallEventCallback} on events raised from a
+     * {@link android.telecom.InCallService} presenting this call. The event key and extra values
+     * are defined in AndroidX. This enables alternative calling surfaces, such as an automotive
+     * UI, to relay requests to perform other non-standard call actions to the app. For example,
+     * an automotive calling solution may offer the ability for the user to raise their hand
+     * during a meeting.
+     *
+     * @param event that is defined in AndroidX (ex. the number of participants changed)
+     * @param extras the updated value in relation to the event (ex. 4 participants)
+     */
+    void onEvent(@NonNull String event, @NonNull Bundle extras);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
index 7bba1eb..e44e2b3 100644
--- a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
+++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
@@ -19,6 +19,7 @@
 import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
 
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.OutcomeReceiver;
 import android.os.ResultReceiver;
 import android.telecom.CallAttributes;
@@ -148,6 +149,7 @@
         private static final String ON_AVAILABLE_CALL_ENDPOINTS = "onAvailableCallEndpointsChanged";
         private static final String ON_MUTE_STATE_CHANGED = "onMuteStateChanged";
         private static final String ON_CALL_STREAMING_FAILED = "onCallStreamingFailed";
+        private static final String ON_EVENT = "onEvent";
 
         private void handleHandshakeCallback(String action, String callId, int code,
                 ResultReceiver ackResultReceiver) {
@@ -314,5 +316,23 @@
             Log.i(TAG, TextUtils.formatSimple("oCSF: id=[%s], reason=[%s]", callId, reason));
             handleEventCallback(callId, ON_CALL_STREAMING_FAILED, reason);
         }
+
+        @Override
+        public void onEvent(String callId, String event, Bundle extras) {
+            // lookup the callEventCallback associated with the particular call
+            TransactionalCall call = mCallIdToTransactionalCall.get(callId);
+            if (call != null) {
+                CallEventCallback callback = call.getCallStateCallback();
+                Executor executor = call.getExecutor();
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> {
+                        callback.onEvent(event, extras);
+                    });
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
     };
 }
diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
index b78a77e..3e651e9 100644
--- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.telecom;
 
+import android.os.Bundle;
 import android.telecom.CallControl;
 import android.telecom.CallEndpoint;
 import android.telecom.DisconnectCause;
@@ -30,4 +31,5 @@
     void disconnect(String callId, in DisconnectCause disconnectCause, in ResultReceiver callback);
     void startCallStreaming(String callId, in ResultReceiver callback);
     void requestCallEndpointChange(in CallEndpoint callEndpoint, in ResultReceiver callback);
+    void sendEvent(String callId, String event, in Bundle extras);
 }
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
index fef5e9e..dd61d17 100644
--- a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.telecom;
 
+import android.os.Bundle;
 import android.telecom.CallControl;
 import android.telecom.CallEndpoint;
 import com.android.internal.telecom.ICallControl;
@@ -44,6 +45,8 @@
     void onCallEndpointChanged(String callId, in CallEndpoint endpoint);
     void onAvailableCallEndpointsChanged(String callId, in List<CallEndpoint> endpoint);
     void onMuteStateChanged(String callId, boolean isMuted);
+    // -- Events
+    void onEvent(String callId, String event, in Bundle extras);
     // hidden methods that help with cleanup
     void removeCallFromTransactionalServiceWrapper(String callId);
 }
\ No newline at end of file