Implement CallEndpoint representation and related APIs
Add CallEndpoint representation related APIs that request change CallEndpoint and to receive changed information
bug: 260952109
Test: CTS test
Change-Id: I60d0c1c73152a4c17f0227f101a844c83ecae1ee
diff --git a/telecomm/java/android/telecom/CallEndpoint.aidl b/telecomm/java/android/telecom/CallEndpoint.aidl
new file mode 100644
index 0000000..45b2249
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpoint.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable CallEndpoint;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/CallEndpoint.java b/telecomm/java/android/telecom/CallEndpoint.java
new file mode 100644
index 0000000..0b2211d
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpoint.java
@@ -0,0 +1,222 @@
+/*
+ * 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.os.Parcel;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Encapsulates the endpoint where call media can flow
+ */
+public final class CallEndpoint implements Parcelable {
+ /** @hide */
+ public static final int ENDPOINT_OPERATION_SUCCESS = 0;
+ /** @hide */
+ public static final int ENDPOINT_OPERATION_FAILED = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_UNKNOWN, TYPE_EARPIECE, TYPE_BLUETOOTH, TYPE_WIRED_HEADSET, TYPE_SPEAKER,
+ TYPE_STREAMING})
+ public @interface EndpointType {}
+
+ /** Indicates that the type of endpoint through which call media flows is unknown type. */
+ public static final int TYPE_UNKNOWN = -1;
+
+ /** Indicates that the type of endpoint through which call media flows is an earpiece. */
+ public static final int TYPE_EARPIECE = 1;
+
+ /** Indicates that the type of endpoint through which call media flows is a Bluetooth. */
+ public static final int TYPE_BLUETOOTH = 2;
+
+ /** Indicates that the type of endpoint through which call media flows is a wired headset. */
+ public static final int TYPE_WIRED_HEADSET = 3;
+
+ /** Indicates that the type of endpoint through which call media flows is a speakerphone. */
+ public static final int TYPE_SPEAKER = 4;
+
+ /** Indicates that the type of endpoint through which call media flows is an external. */
+ public static final int TYPE_STREAMING = 5;
+
+ private final CharSequence mName;
+ private final int mType;
+ private final ParcelUuid mIdentifier;
+
+ /**
+ * Constructor for a {@link CallEndpoint} object.
+ *
+ * @param name Human-readable name associated with the endpoint
+ * @param type The type of endpoint through which call media being routed
+ * Allowed values:
+ * {@link #TYPE_EARPIECE}
+ * {@link #TYPE_BLUETOOTH}
+ * {@link #TYPE_WIRED_HEADSET}
+ * {@link #TYPE_SPEAKER}
+ * {@link #TYPE_STREAMING}
+ * {@link #TYPE_UNKNOWN}
+ * @param id A unique identifier for this endpoint on the device
+ */
+ public CallEndpoint(@NonNull CharSequence name, @EndpointType int type,
+ @NonNull ParcelUuid id) {
+ this.mName = name;
+ this.mType = type;
+ this.mIdentifier = id;
+ }
+
+ /** @hide */
+ public CallEndpoint(@NonNull CharSequence name, @EndpointType int type) {
+ this(name, type, new ParcelUuid(UUID.randomUUID()));
+ }
+
+ /** @hide */
+ public CallEndpoint(CallEndpoint endpoint) {
+ mName = endpoint.getEndpointName();
+ mType = endpoint.getEndpointType();
+ mIdentifier = endpoint.getIdentifier();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof CallEndpoint)) {
+ return false;
+ }
+ CallEndpoint endpoint = (CallEndpoint) obj;
+ return getEndpointName().toString().contentEquals(endpoint.getEndpointName())
+ && getEndpointType() == endpoint.getEndpointType()
+ && getIdentifier().equals(endpoint.getIdentifier());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mType, mIdentifier);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("[CallEndpoint Name: %s, Type: %s, Identifier: %s]",
+ mName.toString(), endpointTypeToString(mType), mIdentifier.toString());
+ }
+
+ /**
+ * @return Human-readable name associated with the endpoint
+ */
+ @NonNull
+ public CharSequence getEndpointName() {
+ return mName;
+ }
+
+ /**
+ * @return The type of endpoint through which call media being routed
+ */
+ @EndpointType
+ public int getEndpointType() {
+ return mType;
+ }
+
+ /**
+ * @return A unique identifier for this endpoint on the device
+ */
+ @NonNull
+ public ParcelUuid getIdentifier() {
+ return mIdentifier;
+ }
+
+ /**
+ * Converts the provided endpoint type into a human-readable string representation.
+ *
+ * @param endpointType to convert into a string.
+ * @return String representation of the provided endpoint type.
+ * @hide
+ */
+ @NonNull
+ public static String endpointTypeToString(int endpointType) {
+ switch (endpointType) {
+ case TYPE_EARPIECE:
+ return "EARPIECE";
+ case TYPE_BLUETOOTH:
+ return "BLUETOOTH";
+ case TYPE_WIRED_HEADSET:
+ return "WIRED_HEADSET";
+ case TYPE_SPEAKER:
+ return "SPEAKER";
+ case TYPE_STREAMING:
+ return "EXTERNAL";
+ default:
+ return "UNKNOWN (" + endpointType + ")";
+ }
+ }
+
+ /**
+ * Responsible for creating CallEndpoint objects for deserialized Parcels.
+ */
+ public static final @android.annotation.NonNull Parcelable.Creator<CallEndpoint> CREATOR =
+ new Parcelable.Creator<CallEndpoint>() {
+
+ @Override
+ public CallEndpoint createFromParcel(Parcel source) {
+ CharSequence name = source.readCharSequence();
+ int type = source.readInt();
+ ParcelUuid id = ParcelUuid.CREATOR.createFromParcel(source);
+
+ return new CallEndpoint(name, type, id);
+ }
+
+ @Override
+ public CallEndpoint[] newArray(int size) {
+ return new CallEndpoint[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel destination, int flags) {
+ destination.writeCharSequence(mName);
+ destination.writeInt(mType);
+ mIdentifier.writeToParcel(destination, flags);
+ }
+}
diff --git a/telecomm/java/android/telecom/CallEndpointException.aidl b/telecomm/java/android/telecom/CallEndpointException.aidl
new file mode 100644
index 0000000..19b43c4b4
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpointException.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 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;
+
+/**
+ * {@hide}
+ */
+parcelable CallEndpointException;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/CallEndpointException.java b/telecomm/java/android/telecom/CallEndpointException.java
new file mode 100644
index 0000000..e2238928
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEndpointException.java
@@ -0,0 +1,130 @@
+/*
+ * 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.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class represents a set of exceptions that can occur when requesting a
+ * {@link CallEndpoint} change.
+ */
+public final class CallEndpointException extends RuntimeException implements Parcelable {
+ /** @hide */
+ public static final String CHANGE_ERROR = "ChangeErrorKey";
+
+ /**
+ * The operation has failed because requested CallEndpoint does not exist.
+ */
+ public static final int ERROR_ENDPOINT_DOES_NOT_EXIST = 1;
+
+ /**
+ * The operation was not completed on time.
+ */
+ public static final int ERROR_REQUEST_TIME_OUT = 2;
+
+ /**
+ * The operation was canceled by another request.
+ */
+ public static final int ERROR_ANOTHER_REQUEST = 3;
+
+ /**
+ * The operation has failed due to an unknown or unspecified error.
+ */
+ public static final int ERROR_UNSPECIFIED = 4;
+
+ private int mCode = ERROR_UNSPECIFIED;
+ private final String mMessage;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mMessage);
+ dest.writeInt(mCode);
+ }
+
+ /**
+ * Responsible for creating CallEndpointException objects for deserialized Parcels.
+ */
+ public static final @android.annotation.NonNull Parcelable.Creator<CallEndpointException>
+ CREATOR = new Parcelable.Creator<>() {
+ @Override
+ public CallEndpointException createFromParcel(Parcel source) {
+ return new CallEndpointException(source.readString8(), source.readInt());
+ }
+
+ @Override
+ public CallEndpointException[] newArray(int size) {
+ return new CallEndpointException[size];
+ }
+ };
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ERROR_ENDPOINT_DOES_NOT_EXIST, ERROR_REQUEST_TIME_OUT, ERROR_ANOTHER_REQUEST,
+ ERROR_UNSPECIFIED})
+ public @interface CallEndpointErrorCode {
+ }
+
+ public CallEndpointException(@Nullable String message) {
+ super(getMessage(message, ERROR_UNSPECIFIED));
+ mMessage = message;
+ }
+
+ public CallEndpointException(@Nullable String message, @CallEndpointErrorCode int code) {
+ super(getMessage(message, code));
+ mCode = code;
+ mMessage = message;
+ }
+
+ public CallEndpointException(@Nullable String message, @CallEndpointErrorCode int code,
+ @Nullable Throwable cause) {
+ super(getMessage(message, code), cause);
+ mCode = code;
+ mMessage = message;
+ }
+
+
+ public @CallEndpointErrorCode int getCode() {
+ return mCode;
+ }
+
+ private static String getMessage(String message, int code) {
+ StringBuilder builder;
+ if (!TextUtils.isEmpty(message)) {
+ builder = new StringBuilder(message);
+ builder.append(" (code: ");
+ builder.append(code);
+ builder.append(")");
+ return builder.toString();
+ } else {
+ return "code: " + code;
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index f84dd7b..f803717 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -88,6 +88,7 @@
private String mTelecomCallId;
private PhoneAccountHandle mPhoneAccount;
private CallAudioState mCallAudioState;
+ private CallEndpoint mCallEndpoint;
private int mState = Connection.STATE_NEW;
private DisconnectCause mDisconnectCause;
private int mConnectionCapabilities;
@@ -223,12 +224,26 @@
* @return The audio state of the conference, describing how its audio is currently
* being routed by the system. This is {@code null} if this Conference
* does not directly know about its audio state.
+ * @deprecated Use {@link #getCurrentCallEndpoint()},
+ * {@link #onAvailableCallEndpointsChanged(List)} and
+ * {@link #onMuteStateChanged(boolean)} instead.
*/
+ @Deprecated
public final CallAudioState getCallAudioState() {
return mCallAudioState;
}
/**
+ * Obtains the current CallEndpoint.
+ *
+ * @return An object encapsulating the CallEndpoint.
+ */
+ @NonNull
+ public final CallEndpoint getCurrentCallEndpoint() {
+ return mCallEndpoint;
+ }
+
+ /**
* Returns VideoProvider of the primary call. This can be null.
*/
public VideoProvider getVideoProvider() {
@@ -314,10 +329,35 @@
* value.
*
* @param state The new call audio state.
+ * @deprecated Use {@link #onCallEndpointChanged(CallEndpoint)},
+ * {@link #onAvailableCallEndpointsChanged(List)} and
+ * {@link #onMuteStateChanged(boolean)} instead.
*/
+ @Deprecated
public void onCallAudioStateChanged(CallAudioState state) {}
/**
+ * Notifies the {@link Conference} that the audio endpoint has been changed.
+ *
+ * @param callEndpoint The new call endpoint.
+ */
+ public void onCallEndpointChanged(@NonNull CallEndpoint callEndpoint) {}
+
+ /**
+ * Notifies the {@link Conference} that the available call endpoints have been changed.
+ *
+ * @param availableEndpoints The available call endpoints.
+ */
+ public void onAvailableCallEndpointsChanged(@NonNull List<CallEndpoint> availableEndpoints) {}
+
+ /**
+ * Notifies the {@link Conference} that its audio mute state has been changed.
+ *
+ * @param isMuted The new mute state.
+ */
+ public void onMuteStateChanged(boolean isMuted) {}
+
+ /**
* Notifies the {@link Conference} that a {@link Connection} has been added to it.
*
* @param connection The newly added connection.
@@ -730,6 +770,40 @@
onCallAudioStateChanged(state);
}
+ /**
+ * Inform this Conference that the audio endpoint has been changed.
+ *
+ * @param endpoint The new call endpoint.
+ * @hide
+ */
+ final void setCallEndpoint(CallEndpoint endpoint) {
+ Log.d(this, "setCallEndpoint %s", endpoint);
+ mCallEndpoint = endpoint;
+ onCallEndpointChanged(endpoint);
+ }
+
+ /**
+ * Inform this Conference that the available call endpoints have been changed.
+ *
+ * @param availableEndpoints The available call endpoints.
+ * @hide
+ */
+ final void setAvailableCallEndpoints(List<CallEndpoint> availableEndpoints) {
+ Log.d(this, "setAvailableCallEndpoints");
+ onAvailableCallEndpointsChanged(availableEndpoints);
+ }
+
+ /**
+ * Inform this Conference that its audio mute state has been changed.
+ *
+ * @param isMuted The new mute state.
+ * @hide
+ */
+ final void setMuteState(boolean isMuted) {
+ Log.d(this, "setMuteState %s", isMuted);
+ onMuteStateChanged(isMuted);
+ }
+
private void setState(int newState) {
if (mState != newState) {
int oldState = mState;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 568c8ab..4656226 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.MODIFY_PHONE_STATE;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -39,6 +40,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.OutcomeReceiver;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -69,6 +71,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
/**
* Represents a phone call or connection to a remote endpoint that carries voice and/or video
@@ -1280,6 +1283,8 @@
/** @hide */
public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) {}
public void onConnectionTimeReset(Connection c) {}
+ public void onEndpointChanged(Connection c, CallEndpoint endpoint, Executor executor,
+ OutcomeReceiver<Void, CallEndpointException> callback) {}
}
/**
@@ -2162,6 +2167,7 @@
private PhoneAccountHandle mPhoneAccountHandle;
private int mState = STATE_NEW;
private CallAudioState mCallAudioState;
+ private CallEndpoint mCallEndpoint;
private Uri mAddress;
private int mAddressPresentation;
private String mCallerDisplayName;
@@ -2290,7 +2296,11 @@
* @return The audio state of the connection, describing how its audio is currently
* being routed by the system. This is {@code null} if this Connection
* does not directly know about its audio state.
+ * @deprecated Use {@link #getCurrentCallEndpoint()},
+ * {@link #onAvailableCallEndpointsChanged(List)} and
+ * {@link #onMuteStateChanged(boolean)} instead.
*/
+ @Deprecated
public final CallAudioState getCallAudioState() {
return mCallAudioState;
}
@@ -2457,6 +2467,43 @@
}
/**
+ * Inform this Connection that the audio endpoint has been changed.
+ *
+ * @param endpoint The new call endpoint.
+ * @hide
+ */
+ final void setCallEndpoint(CallEndpoint endpoint) {
+ checkImmutable();
+ Log.d(this, "setCallEndpoint %s", endpoint);
+ mCallEndpoint = endpoint;
+ onCallEndpointChanged(endpoint);
+ }
+
+ /**
+ * Inform this Connection that the available call endpoints have been changed.
+ *
+ * @param availableEndpoints The available call endpoints.
+ * @hide
+ */
+ final void setAvailableCallEndpoints(List<CallEndpoint> availableEndpoints) {
+ checkImmutable();
+ Log.d(this, "setAvailableCallEndpoints");
+ onAvailableCallEndpointsChanged(availableEndpoints);
+ }
+
+ /**
+ * Inform this Connection that its audio mute state has been changed.
+ *
+ * @param isMuted The new mute state.
+ * @hide
+ */
+ final void setMuteState(boolean isMuted) {
+ checkImmutable();
+ Log.d(this, "setMuteState %s", isMuted);
+ onMuteStateChanged(isMuted);
+ }
+
+ /**
* @param state An integer value of a {@code STATE_*} constant.
* @return A string representation of the value.
*/
@@ -3081,7 +3128,10 @@
* @param route The audio route to use (one of {@link CallAudioState#ROUTE_BLUETOOTH},
* {@link CallAudioState#ROUTE_EARPIECE}, {@link CallAudioState#ROUTE_SPEAKER}, or
* {@link CallAudioState#ROUTE_WIRED_HEADSET}).
+ * @deprecated Use {@link #requestCallEndpointChange(CallEndpoint, Executor, OutcomeReceiver)}
+ * instead.
*/
+ @Deprecated
public final void setAudioRoute(int route) {
for (Listener l : mListeners) {
l.onAudioRouteChanged(this, route, null);
@@ -3101,7 +3151,10 @@
* <p>
* See also {@link InCallService#requestBluetoothAudio(BluetoothDevice)}
* @param bluetoothDevice The bluetooth device to connect to.
+ * @deprecated Use {@link #requestCallEndpointChange(CallEndpoint, Executor, OutcomeReceiver)}
+ * instead.
*/
+ @Deprecated
public void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
for (Listener l : mListeners) {
l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH,
@@ -3110,6 +3163,40 @@
}
/**
+ * Request audio routing to a specific CallEndpoint. Clients should not define their own
+ * CallEndpoint when requesting a change. Instead, the new endpoint should be one of the valid
+ * endpoints provided by {@link #onAvailableCallEndpointsChanged(List)}.
+ * When this request is honored, there will be change to the {@link #getCurrentCallEndpoint()}.
+ * <p>
+ * Used by self-managed {@link ConnectionService}s which wish to change the CallEndpoint for a
+ * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+ * <p>
+ * See also
+ * {@link InCallService#requestCallEndpointChange(CallEndpoint, Executor, OutcomeReceiver)}.
+ *
+ * @param endpoint The call endpoint to use.
+ * @param executor The executor of where the callback will execute.
+ * @param callback The callback to notify the result of the endpoint change.
+ */
+ public final void requestCallEndpointChange(@NonNull CallEndpoint endpoint,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<Void, CallEndpointException> callback) {
+ for (Listener l : mListeners) {
+ l.onEndpointChanged(this, endpoint, executor, callback);
+ }
+ }
+
+ /**
+ * Obtains the current CallEndpoint.
+ *
+ * @return An object encapsulating the CallEndpoint.
+ */
+ @NonNull
+ public final CallEndpoint getCurrentCallEndpoint() {
+ return mCallEndpoint;
+ }
+
+ /**
* Informs listeners that a previously requested RTT session via
* {@link ConnectionRequest#isRequestingRtt()} or
* {@link #onStartRtt(RttTextStream)} has succeeded.
@@ -3160,10 +3247,35 @@
* Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
*
* @param state The new connection audio state.
+ * @deprecated Use {@link #onCallEndpointChanged(CallEndpoint)},
+ * {@link #onAvailableCallEndpointsChanged(List)} and
+ * {@link #onMuteStateChanged(boolean)} instead.
*/
+ @Deprecated
public void onCallAudioStateChanged(CallAudioState state) {}
/**
+ * Notifies this Connection that the audio endpoint has been changed.
+ *
+ * @param callEndpoint The current CallEndpoint.
+ */
+ public void onCallEndpointChanged(@NonNull CallEndpoint callEndpoint) {}
+
+ /**
+ * Notifies this Connection that the available call endpoints have been changed.
+ *
+ * @param availableEndpoints The set of available CallEndpoint.
+ */
+ public void onAvailableCallEndpointsChanged(@NonNull List<CallEndpoint> availableEndpoints) {}
+
+ /**
+ * Notifies this Connection that its audio mute state has been changed.
+ *
+ * @param isMuted The current mute state.
+ */
+ public void onMuteStateChanged(boolean isMuted) {}
+
+ /**
* Inform this Connection when it will or will not be tracked by an {@link InCallService} which
* can provide an InCall UI.
* This is primarily intended for use by Connections reported by self-managed
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index f341bc2..4d6caf8 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -31,6 +31,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.telecom.Logging.Session;
@@ -48,6 +49,7 @@
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
/**
* An abstract service that should be implemented by any apps which either:
@@ -164,6 +166,9 @@
private static final String SESSION_CREATE_CONF = "CS.crConf";
private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC";
private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF";
+ private static final String SESSION_CALL_ENDPOINT_CHANGED = "CS.oCEC";
+ private static final String SESSION_AVAILABLE_CALL_ENDPOINTS_CHANGED = "CS.oACEC";
+ private static final String SESSION_MUTE_STATE_CHANGED = "CS.oMSC";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -208,6 +213,9 @@
private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
private static final int MSG_ON_USING_ALTERNATIVE_UI = 43;
private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44;
+ private static final int MSG_ON_CALL_ENDPOINT_CHANGED = 45;
+ private static final int MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED = 46;
+ private static final int MSG_ON_MUTE_STATE_CHANGED = 47;
private static Connection sNullConnection;
@@ -592,6 +600,51 @@
}
@Override
+ public void onCallEndpointChanged(String callId, CallEndpoint callEndpoint,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CALL_ENDPOINT_CHANGED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = callEndpoint;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_CALL_ENDPOINT_CHANGED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onAvailableCallEndpointsChanged(String callId,
+ List<CallEndpoint> availableCallEndpoints, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_AVAILABLE_CALL_ENDPOINTS_CHANGED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = availableCallEndpoints;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED, args)
+ .sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onMuteStateChanged(String callId, boolean isMuted, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_MUTE_STATE_CHANGED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = isMuted;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_MUTE_STATE_CHANGED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI);
@@ -1527,6 +1580,48 @@
case MSG_CONNECTION_SERVICE_FOCUS_LOST:
onConnectionServiceFocusLost();
break;
+ case MSG_ON_CALL_ENDPOINT_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
+ try {
+ String callId = (String) args.arg1;
+ CallEndpoint callEndpoint = (CallEndpoint) args.arg2;
+ onCallEndpointChanged(callId, callEndpoint);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
+ try {
+ String callId = (String) args.arg1;
+ List<CallEndpoint> availableCallEndpoints = (List<CallEndpoint>) args.arg2;
+ onAvailableCallEndpointsChanged(callId, availableCallEndpoints);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_ON_MUTE_STATE_CHANGED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_CALL_AUDIO_SC);
+ try {
+ String callId = (String) args.arg1;
+ boolean isMuted = (boolean) args.arg2;
+ onMuteStateChanged(callId, isMuted);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
default:
break;
}
@@ -1916,6 +2011,15 @@
mAdapter.resetConnectionTime(id);
}
}
+
+ @Override
+ public void onEndpointChanged(Connection c, CallEndpoint endpoint, Executor executor,
+ OutcomeReceiver<Void, CallEndpointException> callback) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.requestCallEndpointChange(id, endpoint, executor, callback);
+ }
+ }
};
/** {@inheritDoc} */
@@ -2313,6 +2417,36 @@
}
}
+ private void onCallEndpointChanged(String callId, CallEndpoint callEndpoint) {
+ Log.i(this, "onCallEndpointChanged %s %s", callId, callEndpoint);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onCallEndpointChanged").setCallEndpoint(callEndpoint);
+ } else {
+ findConferenceForAction(callId, "onCallEndpointChanged").setCallEndpoint(callEndpoint);
+ }
+ }
+
+ private void onAvailableCallEndpointsChanged(String callId,
+ List<CallEndpoint> availableCallEndpoints) {
+ Log.i(this, "onAvailableCallEndpointsChanged %s", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onAvailableCallEndpointsChanged")
+ .setAvailableCallEndpoints(availableCallEndpoints);
+ } else {
+ findConferenceForAction(callId, "onAvailableCallEndpointsChanged")
+ .setAvailableCallEndpoints(availableCallEndpoints);
+ }
+ }
+
+ private void onMuteStateChanged(String callId, boolean isMuted) {
+ Log.i(this, "onMuteStateChanged %s %s", callId, isMuted);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "onMuteStateChanged").setMuteState(isMuted);
+ } else {
+ findConferenceForAction(callId, "onMuteStateChanged").setMuteState(isMuted);
+ }
+ }
+
private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) {
Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi);
if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index f8a6cf0..39928ef 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -17,9 +17,12 @@
package android.telecom;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder.DeathRecipient;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import com.android.internal.telecom.IConnectionServiceAdapter;
import com.android.internal.telecom.RemoteServiceCallback;
@@ -29,6 +32,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
/**
* Provides methods for IConnectionService implementations to interact with the system phone app.
@@ -567,6 +571,41 @@
}
}
+ /**
+ * Sets the call endpoint associated with a {@link Connection}.
+ *
+ * @param callId The unique ID of the call.
+ * @param endpoint The new call endpoint (see {@link CallEndpoint}).
+ * @param executor The executor of where the callback will execute.
+ * @param callback The callback to notify the result of the endpoint change.
+ */
+ void requestCallEndpointChange(String callId, CallEndpoint endpoint, Executor executor,
+ OutcomeReceiver<Void, CallEndpointException> callback) {
+ Log.v(this, "requestCallEndpointChange");
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.requestCallEndpointChange(callId, endpoint, new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle result) {
+ super.onReceiveResult(resultCode, result);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (resultCode == CallEndpoint.ENDPOINT_OPERATION_SUCCESS) {
+ executor.execute(() -> callback.onResult(null));
+ } else {
+ executor.execute(() -> callback.onError(result.getParcelable(
+ CallEndpointException.CHANGE_ERROR,
+ CallEndpointException.class)));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }}, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ Log.d(this, "Remote exception calling requestCallEndpointChange");
+ }
+ }
+ }
/**
* Informs Telecom of a connection level event.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 6c1ea32..c95e14f 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -21,6 +21,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.telecom.Logging.Session;
import com.android.internal.os.SomeArgs;
@@ -692,6 +693,12 @@
args.arg2 = sessionInfo;
mHandler.obtainMessage(MSG_SET_CALL_DIRECTION, args).sendToTarget();
}
+
+ @Override
+ public void requestCallEndpointChange(String callId, CallEndpoint endpoint,
+ ResultReceiver callback, Session.Info sessionInfo) {
+ // Do nothing
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index ab35aff..7770145 100755
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -19,12 +19,16 @@
import android.annotation.NonNull;
import android.bluetooth.BluetoothDevice;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import com.android.internal.telecom.IInCallAdapter;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Receives commands from {@link InCallService} implementations which should be executed by
@@ -227,6 +231,39 @@
}
/**
+ * Request audio routing to a specific CallEndpoint.. See {@link CallEndpoint}.
+ *
+ * @param endpoint The call endpoint to use.
+ * @param executor The executor of where the callback will execute.
+ * @param callback The callback to notify the result of the endpoint change.
+ */
+ public void requestCallEndpointChange(CallEndpoint endpoint, Executor executor,
+ OutcomeReceiver<Void, CallEndpointException> callback) {
+ try {
+ mAdapter.requestCallEndpointChange(endpoint, new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle result) {
+ super.onReceiveResult(resultCode, result);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (resultCode == CallEndpoint.ENDPOINT_OPERATION_SUCCESS) {
+ executor.execute(() -> callback.onResult(null));
+ } else {
+ executor.execute(() -> callback.onError(
+ result.getParcelable(CallEndpointException.CHANGE_ERROR,
+ CallEndpointException.class)));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.d(this, "Remote exception calling requestCallEndpointChange");
+ }
+ }
+
+ /**
* Instructs Telecom to play a dual-tone multi-frequency signaling (DTMF) tone in a call.
*
* Any other currently playing DTMF tone in the specified call is immediately stopped.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 64a86db..13a0458 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
@@ -31,6 +32,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.OutcomeReceiver;
import android.view.Surface;
import com.android.internal.os.SomeArgs;
@@ -39,6 +41,8 @@
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* This service is implemented by an app that wishes to provide functionality for managing
@@ -269,6 +273,11 @@
private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
private static final int MSG_ON_HANDOVER_FAILED = 12;
private static final int MSG_ON_HANDOVER_COMPLETE = 13;
+ private static final int MSG_ON_CALL_ENDPOINT_CHANGED = 14;
+ private static final int MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED = 15;
+ private static final int MSG_ON_MUTE_STATE_CHANGED = 16;
+
+ private CallEndpoint mCallEndpoint;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -350,6 +359,23 @@
mPhone.internalOnHandoverComplete(callId);
break;
}
+ case MSG_ON_CALL_ENDPOINT_CHANGED: {
+ CallEndpoint endpoint = (CallEndpoint) msg.obj;
+ if (!Objects.equals(mCallEndpoint, endpoint)) {
+ mCallEndpoint = endpoint;
+ InCallService.this.onCallEndpointChanged(mCallEndpoint);
+ }
+ break;
+ }
+ case MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED: {
+ InCallService.this.onAvailableCallEndpointsChanged(
+ (List<CallEndpoint>) msg.obj);
+ break;
+ }
+ case MSG_ON_MUTE_STATE_CHANGED: {
+ InCallService.this.onMuteStateChanged((boolean) msg.obj);
+ break;
+ }
default:
break;
}
@@ -392,6 +418,22 @@
}
@Override
+ public void onCallEndpointChanged(CallEndpoint callEndpoint) {
+ mHandler.obtainMessage(MSG_ON_CALL_ENDPOINT_CHANGED, callEndpoint).sendToTarget();
+ }
+
+ @Override
+ public void onAvailableCallEndpointsChanged(List<CallEndpoint> availableEndpoints) {
+ mHandler.obtainMessage(MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED, availableEndpoints)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onMuteStateChanged(boolean isMuted) {
+ mHandler.obtainMessage(MSG_ON_MUTE_STATE_CHANGED, isMuted).sendToTarget();
+ }
+
+ @Override
public void bringToForeground(boolean showDialpad) {
mHandler.obtainMessage(MSG_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0).sendToTarget();
}
@@ -559,7 +601,11 @@
*
* @return An object encapsulating the audio state. Returns null if the service is not
* fully initialized.
+ * @deprecated Use {@link #getCurrentCallEndpoint()},
+ * {@link #onAvailableCallEndpointsChanged(List)} and
+ * {@link #onMuteStateChanged(boolean)} instead.
*/
+ @Deprecated
public final CallAudioState getCallAudioState() {
return mPhone == null ? null : mPhone.getCallAudioState();
}
@@ -581,7 +627,10 @@
* be change to the {@link #getCallAudioState()}.
*
* @param route The audio route to use.
+ * @deprecated Use {@link #requestCallEndpointChange(CallEndpoint, Executor, OutcomeReceiver)}
+ * instead.
*/
+ @Deprecated
public final void setAudioRoute(int route) {
if (mPhone != null) {
mPhone.setAudioRoute(route);
@@ -596,7 +645,10 @@
* {@link CallAudioState#getSupportedBluetoothDevices()}
*
* @param bluetoothDevice The bluetooth device to connect to.
+ * @deprecated Use {@link #requestCallEndpointChange(CallEndpoint, Executor, OutcomeReceiver)}
+ * instead.
*/
+ @Deprecated
public final void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
if (mPhone != null) {
mPhone.requestBluetoothAudio(bluetoothDevice.getAddress());
@@ -604,6 +656,34 @@
}
/**
+ * Request audio routing to a specific CallEndpoint. Clients should not define their own
+ * CallEndpoint when requesting a change. Instead, the new endpoint should be one of the valid
+ * endpoints provided by {@link #onAvailableCallEndpointsChanged(List)}.
+ * When this request is honored, there will be change to the {@link #getCurrentCallEndpoint()}.
+ *
+ * @param endpoint The call endpoint to use.
+ * @param executor The executor of where the callback will execute.
+ * @param callback The callback to notify the result of the endpoint change.
+ */
+ public final void requestCallEndpointChange(@NonNull CallEndpoint endpoint,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<Void, CallEndpointException> callback) {
+ if (mPhone != null) {
+ mPhone.requestCallEndpointChange(endpoint, executor, callback);
+ }
+ }
+
+ /**
+ * Obtains the current CallEndpoint.
+ *
+ * @return An object encapsulating the CallEndpoint.
+ */
+ @NonNull
+ public final CallEndpoint getCurrentCallEndpoint() {
+ return mCallEndpoint;
+ }
+
+ /**
* Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
* to start displaying in-call information to the user. Each instance of {@code InCallService}
* will have only one {@code Phone}, and this method will be called exactly once in the lifetime
@@ -648,11 +728,39 @@
* Called when the audio state changes.
*
* @param audioState The new {@link CallAudioState}.
+ * @deprecated Use {@link #onCallEndpointChanged(CallEndpoint)},
+ * {@link #onAvailableCallEndpointsChanged(List)} and
+ * {@link #onMuteStateChanged(boolean)} instead.
*/
+ @Deprecated
public void onCallAudioStateChanged(CallAudioState audioState) {
}
/**
+ * Called when the current CallEndpoint changes.
+ *
+ * @param callEndpoint The current CallEndpoint {@link CallEndpoint}.
+ */
+ public void onCallEndpointChanged(@NonNull CallEndpoint callEndpoint) {
+ }
+
+ /**
+ * Called when the available CallEndpoint changes.
+ *
+ * @param availableEndpoints The set of available CallEndpoint {@link CallEndpoint}.
+ */
+ public void onAvailableCallEndpointsChanged(@NonNull List<CallEndpoint> availableEndpoints) {
+ }
+
+ /**
+ * Called when the mute state changes.
+ *
+ * @param isMuted The current mute state.
+ */
+ public void onMuteStateChanged(boolean isMuted) {
+ }
+
+ /**
* Called to bring the in-call screen to the foreground. The in-call experience should
* respond immediately by coming to the foreground to inform the user of the state of
* ongoing {@code Call}s.
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index bc0a146..95a8e16 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -16,11 +16,14 @@
package android.telecom;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.bluetooth.BluetoothDevice;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Bundle;
+import android.os.OutcomeReceiver;
import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
@@ -30,6 +33,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
/**
* A unified virtual device providing a means of voice (and other) communication on a device.
@@ -378,6 +382,21 @@
}
/**
+ * Request audio routing to a specific CallEndpoint. When this request is honored, there will
+ * be change to the {@link #getCurrentCallEndpoint()}.
+ *
+ * @param endpoint The call endpoint to use.
+ * @param executor The executor of where the callback will execute.
+ * @param callback The callback to notify the result of the endpoint change.
+ * @hide
+ */
+ public void requestCallEndpointChange(@NonNull CallEndpoint endpoint,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<Void, CallEndpointException> callback) {
+ mInCallAdapter.requestCallEndpointChange(endpoint, executor, callback);
+ }
+
+ /**
* Turns the proximity sensor on. When this request is made, the proximity sensor will
* become active, and the touch screen and display will be turned off when the user's face
* is detected to be in close proximity to the screen. This operation is a no-op on devices
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 7a6fddb..8b2b51e 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -36,12 +36,10 @@
import com.android.internal.telecom.IVideoProvider;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
/**
* A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index efe35d2..6561732 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.telecom.Logging.Session;
import com.android.internal.telecom.IConnectionService;
@@ -510,6 +511,12 @@
public void setCallDirection(String callId, int direction, Session.Info sessionInfo) {
// Do nothing
}
+
+ @Override
+ public void requestCallEndpointChange(String callId, CallEndpoint endpoint,
+ ResultReceiver callback, Session.Info sessionInfo) {
+ // Do nothing
+ }
};
private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index d72f8aa..29617f2 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -20,6 +20,7 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
+import android.telecom.CallEndpoint;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
@@ -98,6 +99,14 @@
void onCallAudioStateChanged(String activeCallId, in CallAudioState callAudioState,
in Session.Info sessionInfo);
+ void onCallEndpointChanged(String activeCallId, in CallEndpoint callEndpoint,
+ in Session.Info sessionInfo);
+
+ void onAvailableCallEndpointsChanged(String activeCallId,
+ in List<CallEndpoint> availableCallEndpoints, in Session.Info sessionInfo);
+
+ void onMuteStateChanged(String activeCallId, boolean isMuted, in Session.Info sessionInfo);
+
void playDtmfTone(String callId, char digit, in Session.Info sessionInfo);
void stopDtmfTone(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 3fd7f949..6838fbd 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -19,6 +19,8 @@
import android.app.PendingIntent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.telecom.CallEndpoint;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
import android.telecom.Logging.Session;
@@ -113,6 +115,9 @@
void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
in Session.Info sessionInfo);
+ void requestCallEndpointChange(String callId, in CallEndpoint endpoint,
+ in ResultReceiver callback, in Session.Info sessionInfo);
+
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index edf1cf4..e381ce8 100755
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -18,7 +18,9 @@
import android.net.Uri;
import android.os.Bundle;
+import android.os.ResultReceiver;
import android.telecom.PhoneAccountHandle;
+import android.telecom.CallEndpoint;
/**
* Internal remote callback interface for in-call services.
@@ -50,6 +52,8 @@
void setAudioRoute(int route, String bluetoothAddress);
+ void requestCallEndpointChange(in CallEndpoint endpoint, in ResultReceiver callback);
+
void enterBackgroundAudioProcessing(String callId);
void exitBackgroundAudioProcessing(String callId, boolean shouldRing);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index b9563fa..bac295a 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -19,6 +19,7 @@
import android.app.PendingIntent;
import android.os.Bundle;
import android.telecom.CallAudioState;
+import android.telecom.CallEndpoint;
import android.telecom.ParcelableCall;
import com.android.internal.telecom.IInCallAdapter;
@@ -43,6 +44,12 @@
void onCallAudioStateChanged(in CallAudioState callAudioState);
+ void onCallEndpointChanged(in CallEndpoint callEndpoint);
+
+ void onAvailableCallEndpointsChanged(in List<CallEndpoint> availableCallEndpoints);
+
+ void onMuteStateChanged(boolean isMuted);
+
void bringToForeground(boolean showDialpad);
void onCanAddCallChanged(boolean canAddCall);