add transactional video state APIs

Bug: 311265260
Test: CTS coverage
Change-Id: I4c76e414056c90643d65e72dcb51da7d81bf36b9
diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java
index 8c6e101..afd34fc 100644
--- a/telecomm/java/android/telecom/CallAttributes.java
+++ b/telecomm/java/android/telecom/CallAttributes.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -24,6 +25,8 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.server.telecom.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
@@ -113,7 +116,8 @@
     public static final int VIDEO_CALL = 2;
 
     /** @hide */
-    @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true)
+    @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER,
+            SUPPORTS_VIDEO_CALLING}, flag = true)
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallCapability {
     }
@@ -133,6 +137,12 @@
      * The call can be completely transferred from one endpoint to another.
      */
     public static final int SUPPORTS_TRANSFER = 1 << 3;
+    /**
+     * The call supports video calling. This allows clients to gate video calling on a per call
+     * basis as opposed to re-registering the phone account.
+     */
+    @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
+    public static final int SUPPORTS_VIDEO_CALLING = 1 << 4;
 
     /**
      * Build an instance of {@link CallAttributes}. In order to build a valid instance, a
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index a1407869..808a575 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -293,12 +293,50 @@
         try {
             mServerInterface.setMuteState(isMuted,
                     new CallControlResultReceiver("requestMuteState", executor, callback));
-
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
+     /**
+     * Request a new video state for the ongoing call. This can only be changed if the application
+     * has registered a {@link PhoneAccount} with the
+     * {@link PhoneAccount#CAPABILITY_SUPPORTS_VIDEO_CALLING} and set the
+     * {@link CallAttributes#SUPPORTS_VIDEO_CALLING} when adding the call via
+     * {@link TelecomManager#addCall(CallAttributes, Executor, OutcomeReceiver,
+     *                                                      CallControlCallback, CallEventCallback)}
+     *
+     * @param videoState to report to Telecom. To see the valid argument to pass,
+      *                   see {@link CallAttributes.CallType}.
+     * @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.
+     *
+     *                   {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+     *                   switched the video state.
+     *
+     *                   {@link OutcomeReceiver#onError} will be called if Telecom has failed to set
+     *                   the new video state.  A {@link CallException} will be passed
+     *                   that details why the operation failed.
+     * @throws IllegalArgumentException if the argument passed for videoState is invalid.  To see a
+     * list of valid states, see {@link CallAttributes.CallType}.
+     */
+     @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
+     public void requestVideoState(@CallAttributes.CallType int videoState,
+             @CallbackExecutor @NonNull Executor executor,
+             @NonNull OutcomeReceiver<Void, CallException> callback) {
+         validateVideoState(videoState);
+         Objects.requireNonNull(executor);
+         Objects.requireNonNull(callback);
+         try {
+             mServerInterface.requestVideoState(videoState, mCallId,
+                     new CallControlResultReceiver("requestVideoState", executor, callback));
+         } catch (RemoteException e) {
+             throw e.rethrowAsRuntimeException();
+         }
+     }
+
     /**
      * Raises an event to the {@link android.telecom.InCallService} implementations tracking this
      * call via {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)}.
diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java
index a41c011..b0438bf 100644
--- a/telecomm/java/android/telecom/CallEventCallback.java
+++ b/telecomm/java/android/telecom/CallEventCallback.java
@@ -16,9 +16,12 @@
 
 package android.telecom;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.os.Bundle;
 
+import com.android.server.telecom.flags.Flags;
+
 import java.util.List;
 
 /**
@@ -51,6 +54,14 @@
     void onMuteStateChanged(boolean isMuted);
 
     /**
+     * Called when the video state changes.
+     *
+     * @param videoState The current video state.
+     */
+    @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
+    default void onVideoStateChanged(@CallAttributes.CallType int videoState) {}
+
+    /**
      * Telecom is informing the client user requested call streaming but the stream can't be
      * started.
      *
diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
index 467e89c..a2c6086 100644
--- a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
+++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
@@ -33,6 +33,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.server.telecom.flags.Flags;
+
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
@@ -148,6 +150,7 @@
         private static final String ON_REQ_ENDPOINT_CHANGE = "onRequestEndpointChange";
         private static final String ON_AVAILABLE_CALL_ENDPOINTS = "onAvailableCallEndpointsChanged";
         private static final String ON_MUTE_STATE_CHANGED = "onMuteStateChanged";
+        private static final String ON_VIDEO_STATE_CHANGED = "onVideoStateChanged";
         private static final String ON_CALL_STREAMING_FAILED = "onCallStreamingFailed";
         private static final String ON_EVENT = "onEvent";
 
@@ -261,6 +264,11 @@
             handleEventCallback(callId, ON_MUTE_STATE_CHANGED, isMuted);
         }
 
+        @Override
+        public void onVideoStateChanged(String callId, int videoState) {
+            handleEventCallback(callId, ON_VIDEO_STATE_CHANGED, videoState);
+        }
+
         public void handleEventCallback(String callId, String action, Object arg) {
             Log.d(TAG, TextUtils.formatSimple("hEC: [%s], callId=[%s]", action, callId));
             // lookup the callEventCallback associated with the particular call
@@ -281,6 +289,11 @@
                             case ON_MUTE_STATE_CHANGED:
                                 callback.onMuteStateChanged((boolean) arg);
                                 break;
+                            case ON_VIDEO_STATE_CHANGED:
+                                if (Flags.transactionalVideoState()) {
+                                    callback.onVideoStateChanged((int) arg);
+                                }
+                                break;
                             case ON_CALL_STREAMING_FAILED:
                                 callback.onCallStreamingFailed((int) arg /* reason */);
                                 break;
diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
index 372e4a12..ac49660 100644
--- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
@@ -34,4 +34,5 @@
     void requestCallEndpointChange(in CallEndpoint callEndpoint, in ResultReceiver callback);
     void setMuteState(boolean isMuted, in ResultReceiver callback);
     void sendEvent(String callId, String event, in Bundle extras);
+    void requestVideoState(int videoState, String callId, in ResultReceiver callback);
 }
\ 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 213cafb..e4d6b0c 100644
--- a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
@@ -45,6 +45,8 @@
     void onCallEndpointChanged(String callId, in CallEndpoint endpoint);
     void onAvailableCallEndpointsChanged(String callId, in List<CallEndpoint> endpoint);
     void onMuteStateChanged(String callId, boolean isMuted);
+    // -- Video Related
+    void onVideoStateChanged(String callId, int videoState);
     // -- Events
     void onEvent(String callId, String event, in Bundle extras);
     // hidden methods that help with cleanup