Platform Telecom Transactional APIs

Telecom Transactional APIs are defined as APIs that leverage
android.os.OutcomeReceivers.  The receivers are to be completed by
Telecom for CallControl opertaions and the Client for CallEventCallback
operations.  Doing so ensures the client and telecom are in sync with
call states.  Also, these APIs are more lightweight than the
ConnectionService way of starting a self-managed call.

bug: 249779561
Test: atest android.telecom.cts.TransactionalApisTest

Change-Id: Icb09b2874d599a40afca8b7e960b14ca1bca606d
diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java
new file mode 100644
index 0000000..6d87981
--- /dev/null
+++ b/telecomm/java/android/telecom/CallAttributes.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 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.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * CallAttributes represents a set of properties that define a new Call.  Apps should build an
+ * instance of this class and use {@link TelecomManager#addCall} to start a new call with Telecom.
+ *
+ * <p>
+ * Apps should first register a {@link PhoneAccount} via {@link TelecomManager#registerPhoneAccount}
+ * and use the same {@link PhoneAccountHandle} registered with Telecom when creating an
+ * instance of CallAttributes.
+ */
+public final class CallAttributes implements Parcelable {
+
+    /** PhoneAccountHandle associated with the App managing calls **/
+    private final PhoneAccountHandle mPhoneAccountHandle;
+
+    /** Display name of the person on the other end of the call **/
+    private final CharSequence mDisplayName;
+
+    /** Address of the call. Note, this can be extended to a meeting link **/
+    private final Uri mAddress;
+
+    /** The direction (Outgoing/Incoming) of the new Call **/
+    @Direction private final int mDirection;
+
+    /** Information related to data being transmitted (voice, video, etc. ) **/
+    @CallType private final int mCallType;
+
+    /** Allows a package to opt into capabilities on the telecom side, on a per-call basis **/
+    @CallCapability private final int mCallCapabilities;
+
+    /** @hide **/
+    public static final String CALL_CAPABILITIES_KEY = "TelecomCapabilities";
+
+    private CallAttributes(@NonNull PhoneAccountHandle phoneAccountHandle,
+            @NonNull CharSequence displayName,
+            @NonNull Uri address,
+            int direction,
+            int callType,
+            int callCapabilities) {
+        mPhoneAccountHandle = phoneAccountHandle;
+        mDisplayName = displayName;
+        mAddress = address;
+        mDirection = direction;
+        mCallType = callType;
+        mCallCapabilities = callCapabilities;
+    }
+
+    /** @hide */
+    @IntDef(value = {DIRECTION_INCOMING, DIRECTION_OUTGOING})
+    public @interface Direction {
+    }
+    /**
+     * Indicates that the call is an incoming call.
+     */
+    public static final int DIRECTION_INCOMING = 1;
+    /**
+     * Indicates that the call is an outgoing call.
+     */
+    public static final int DIRECTION_OUTGOING = 2;
+
+    /** @hide */
+    @IntDef(value = {AUDIO_CALL, VIDEO_CALL})
+    public @interface CallType {
+    }
+    /**
+     * Used when answering or dialing a call to indicate that the call does not have a video
+     * component
+     */
+    public static final int AUDIO_CALL = 1;
+    /**
+     * Indicates video transmission is supported
+     */
+    public static final int VIDEO_CALL = 2;
+
+    /** @hide */
+    @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true)
+    public @interface CallCapability {
+    }
+    /**
+     * The call being created can be set to inactive (traditionally referred to as hold).  This
+     * means that once a new call goes active, if the active call needs to be held in order to
+     * place or receive an incoming call, the active call will be placed on hold.  otherwise, the
+     * active call may be disconnected.
+     */
+    public static final int SUPPORTS_SET_INACTIVE = 1 << 1;
+    /**
+     * The call can be streamed from a root device to another device to continue the call without
+     * completely transferring it.
+     */
+    public static final int SUPPORTS_STREAM = 1 << 2;
+    /**
+     * The call can be completely transferred from one endpoint to another
+     */
+    public static final int SUPPORTS_TRANSFER = 1 << 3;
+
+    /**
+     * Build an instance of {@link CallAttributes}. In order to build a valid instance, a
+     * {@link PhoneAccountHandle}, call {@link Direction}, display name, and {@link Uri} address
+     * are required.
+     *
+     * <p>
+     * Note: Pass in the same {@link PhoneAccountHandle} that was used to register a
+     * {@link PhoneAccount} with Telecom. see {@link TelecomManager#registerPhoneAccount}
+     */
+    public static final class Builder {
+        // required and final fields
+        private final PhoneAccountHandle mPhoneAccountHandle;
+        @Direction private final int mDirection;
+        private final CharSequence mDisplayName;
+        private final Uri mAddress;
+        // optional fields
+        @CallType private int mCallType = CallAttributes.AUDIO_CALL;
+        @CallCapability private int mCallCapabilities = SUPPORTS_SET_INACTIVE;
+
+        /**
+         * Constructor for the CallAttributes.Builder class
+         *
+         * @param phoneAccountHandle that belongs to package registered with Telecom
+         * @param callDirection of the new call that will be added to Telecom
+         * @param displayName of the caller for incoming calls or initiating user for outgoing calls
+         * @param address of the caller for incoming calls or destination for outgoing calls
+         */
+        public Builder(@NonNull PhoneAccountHandle phoneAccountHandle,
+                @Direction int callDirection, @NonNull CharSequence displayName,
+                @NonNull Uri address) {
+            if (!isInRange(DIRECTION_INCOMING, DIRECTION_OUTGOING, callDirection)) {
+                throw new IllegalArgumentException(TextUtils.formatSimple("CallDirection=[%d] is"
+                                + " invalid. CallDirections value should be within [%d, %d]",
+                        callDirection, DIRECTION_INCOMING, DIRECTION_OUTGOING));
+            }
+            Objects.requireNonNull(phoneAccountHandle);
+            Objects.requireNonNull(displayName);
+            Objects.requireNonNull(address);
+            mPhoneAccountHandle = phoneAccountHandle;
+            mDirection = callDirection;
+            mDisplayName = displayName;
+            mAddress = address;
+        }
+
+        /**
+         * @param callType see {@link CallType} for valid arguments
+         * @return Builder
+         */
+        @NonNull
+        public Builder setCallType(@CallType int callType) {
+            if (!isInRange(AUDIO_CALL, VIDEO_CALL, callType)) {
+                throw new IllegalArgumentException(TextUtils.formatSimple("CallType=[%d] is"
+                                + " invalid. CallTypes value should be within [%d, %d]",
+                        callType, AUDIO_CALL, VIDEO_CALL));
+            }
+            mCallType = callType;
+            return this;
+        }
+
+        /**
+         * @param callCapabilities see {@link CallCapability} for valid arguments
+         * @return Builder
+         */
+        @NonNull
+        public Builder setCallCapabilities(@CallCapability int callCapabilities) {
+            mCallCapabilities = callCapabilities;
+            return this;
+        }
+
+        /**
+         * Build an instance of {@link CallAttributes} based on the last values passed to the
+         * setters or default values.
+         *
+         * @return an instance of {@link CallAttributes}
+         */
+        @NonNull
+        public CallAttributes build() {
+            return new CallAttributes(mPhoneAccountHandle, mDisplayName, mAddress, mDirection,
+                    mCallType, mCallCapabilities);
+        }
+
+        /** @hide */
+        private boolean isInRange(int floor, int ceiling, int value) {
+            return value >= floor && value <= ceiling;
+        }
+    }
+
+    /**
+     * The {@link PhoneAccountHandle} that should be registered to Telecom to allow calls.  The
+     * {@link PhoneAccountHandle} should be registered before creating a CallAttributes instance.
+     *
+     * @return the {@link PhoneAccountHandle} for this package that allows this call to be created
+     */
+    @NonNull public PhoneAccountHandle getPhoneAccountHandle() {
+        return mPhoneAccountHandle;
+    }
+
+    /**
+     * @return display name of the incoming caller or the person being called for an outgoing call
+     */
+    @NonNull public CharSequence getDisplayName() {
+        return mDisplayName;
+    }
+
+    /**
+     * @return address of the incoming caller
+     *           or the address of the person being called for an outgoing call
+     */
+    @NonNull public Uri getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * @return the direction of the new call.
+     */
+    public @Direction int getDirection() {
+        return mDirection;
+    }
+
+    /**
+     * @return Information related to data being transmitted (voice, video, etc. )
+     */
+    public @CallType int getCallType() {
+        return mCallType;
+    }
+
+    /**
+     * @return The allowed capabilities of the new call
+     */
+    public @CallCapability int getCallCapabilities() {
+        return mCallCapabilities;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeParcelable(mPhoneAccountHandle, flags);
+        dest.writeCharSequence(mDisplayName);
+        dest.writeParcelable(mAddress, flags);
+        dest.writeInt(mDirection);
+        dest.writeInt(mCallType);
+        dest.writeInt(mCallCapabilities);
+    }
+
+    /**
+     * Responsible for creating CallAttribute objects for deserialized Parcels.
+     */
+    public static final @android.annotation.NonNull
+            Parcelable.Creator<CallAttributes> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public CallAttributes createFromParcel(Parcel source) {
+                    return new CallAttributes(source.readParcelable(getClass().getClassLoader(),
+                            android.telecom.PhoneAccountHandle.class),
+                            source.readCharSequence(),
+                            source.readParcelable(getClass().getClassLoader(),
+                                    android.net.Uri.class),
+                            source.readInt(),
+                            source.readInt(),
+                            source.readInt());
+                }
+
+                @Override
+                public CallAttributes[] newArray(int size) {
+                    return new CallAttributes[size];
+                }
+            };
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("{ CallAttributes: [phoneAccountHandle: ")
+                .append(mPhoneAccountHandle)  /* PhoneAccountHandle#toString handles PII */
+                .append("], [contactName: ")
+                .append(Log.pii(mDisplayName))
+                .append("], [address=")
+                .append(Log.pii(mAddress))
+                .append("], [direction=")
+                .append(mDirection)
+                .append("], [callType=")
+                .append(mCallType)
+                .append("], [mCallCapabilities=")
+                .append(mCallCapabilities)
+                .append("]  }");
+
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || obj.getClass() != this.getClass()) {
+            return false;
+        }
+        CallAttributes that = (CallAttributes) obj;
+        return this.mDirection == that.mDirection
+                && this.mCallType == that.mCallType
+                && this.mCallCapabilities == that.mCallCapabilities
+                && Objects.equals(this.mPhoneAccountHandle, that.mPhoneAccountHandle)
+                && Objects.equals(this.mAddress, that.mAddress)
+                && Objects.equals(this.mDisplayName, that.mDisplayName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPhoneAccountHandle, mAddress, mDisplayName,
+                mDirection, mCallType, mCallCapabilities);
+    }
+}