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
This commit is contained in:
Junho
2022-11-24 09:26:37 +00:00
parent e2c82f46d7
commit edf3d8260b
19 changed files with 1012 additions and 12 deletions

View File

@@ -40978,6 +40978,36 @@ package android.telecom {
field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
}
public final class CallEndpoint implements android.os.Parcelable {
ctor public CallEndpoint(@NonNull CharSequence, int, @NonNull android.os.ParcelUuid);
method public int describeContents();
method @NonNull public CharSequence getEndpointName();
method public int getEndpointType();
method @NonNull public android.os.ParcelUuid getIdentifier();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallEndpoint> CREATOR;
field public static final int TYPE_BLUETOOTH = 2; // 0x2
field public static final int TYPE_EARPIECE = 1; // 0x1
field public static final int TYPE_SPEAKER = 4; // 0x4
field public static final int TYPE_STREAMING = 5; // 0x5
field public static final int TYPE_UNKNOWN = -1; // 0xffffffff
field public static final int TYPE_WIRED_HEADSET = 3; // 0x3
}
public final class CallEndpointException extends java.lang.RuntimeException implements android.os.Parcelable {
ctor public CallEndpointException(@Nullable String);
ctor public CallEndpointException(@Nullable String, int);
ctor public CallEndpointException(@Nullable String, int, @Nullable Throwable);
method public int describeContents();
method public int getCode();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallEndpointException> CREATOR;
field public static final int ERROR_ANOTHER_REQUEST = 3; // 0x3
field public static final int ERROR_ENDPOINT_DOES_NOT_EXIST = 1; // 0x1
field public static final int ERROR_REQUEST_TIME_OUT = 2; // 0x2
field public static final int ERROR_UNSPECIFIED = 4; // 0x4
}
public abstract class CallRedirectionService extends android.app.Service {
ctor public CallRedirectionService();
method public final void cancelCall();
@@ -41027,13 +41057,14 @@ package android.telecom {
method public final boolean addConnection(android.telecom.Connection);
method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
method public final void destroy();
method public final android.telecom.CallAudioState getCallAudioState();
method @Deprecated public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
method public final int getConnectionProperties();
method public final long getConnectionStartElapsedRealtimeMillis();
method @IntRange(from=0) public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method @NonNull public final android.telecom.CallEndpoint getCurrentCallEndpoint();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
@@ -41044,13 +41075,16 @@ package android.telecom {
method public final boolean isRingbackRequested();
method public void onAddConferenceParticipants(@NonNull java.util.List<android.net.Uri>);
method public void onAnswer(int);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onAvailableCallEndpointsChanged(@NonNull java.util.List<android.telecom.CallEndpoint>);
method @Deprecated public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEndpointChanged(@NonNull android.telecom.CallEndpoint);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
method public void onExtrasChanged(android.os.Bundle);
method public void onHold();
method public void onMerge(android.telecom.Connection);
method public void onMerge();
method public void onMuteStateChanged(boolean);
method public void onPlayDtmfTone(char);
method public void onReject();
method public void onSeparate(android.telecom.Connection);
@@ -41093,7 +41127,7 @@ package android.telecom {
method public final android.net.Uri getAddress();
method public final int getAddressPresentation();
method public final boolean getAudioModeIsVoip();
method public final android.telecom.CallAudioState getCallAudioState();
method @Deprecated public final android.telecom.CallAudioState getCallAudioState();
method public final String getCallerDisplayName();
method public final int getCallerDisplayNamePresentation();
method public final int getCallerNumberVerificationStatus();
@@ -41101,6 +41135,7 @@ package android.telecom {
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
method public final int getConnectionProperties();
method @NonNull public final android.telecom.CallEndpoint getCurrentCallEndpoint();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
method public final int getState();
@@ -41114,13 +41149,16 @@ package android.telecom {
method public void onAddConferenceParticipants(@NonNull java.util.List<android.net.Uri>);
method public void onAnswer(int);
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onAvailableCallEndpointsChanged(@NonNull java.util.List<android.telecom.CallEndpoint>);
method @Deprecated public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEndpointChanged(@NonNull android.telecom.CallEndpoint);
method public void onCallEvent(String, android.os.Bundle);
method public void onDeflect(android.net.Uri);
method public void onDisconnect();
method public void onExtrasChanged(android.os.Bundle);
method public void onHandoverComplete();
method public void onHold();
method public void onMuteStateChanged(boolean);
method public void onPlayDtmfTone(char);
method public void onPostDialContinue(boolean);
method public void onPullExternalCall();
@@ -41141,7 +41179,8 @@ package android.telecom {
method public final void putExtras(@NonNull android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
method public void requestBluetoothAudio(@NonNull android.bluetooth.BluetoothDevice);
method @Deprecated public void requestBluetoothAudio(@NonNull android.bluetooth.BluetoothDevice);
method public final void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallEndpointException>);
method public void sendConnectionEvent(String, android.os.Bundle);
method public final void sendRemoteRttRequest();
method public final void sendRttInitiationFailure(int);
@@ -41150,7 +41189,7 @@ package android.telecom {
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
method public final void setAudioRoute(int);
method @Deprecated public final void setAudioRoute(int);
method public final void setCallerDisplayName(String, int);
method public final void setCallerNumberVerificationStatus(int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
@@ -41402,18 +41441,23 @@ package android.telecom {
public abstract class InCallService extends android.app.Service {
ctor public InCallService();
method public final boolean canAddCall();
method public final android.telecom.CallAudioState getCallAudioState();
method @Deprecated public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Call> getCalls();
method @NonNull public final android.telecom.CallEndpoint getCurrentCallEndpoint();
method public void onAvailableCallEndpointsChanged(@NonNull java.util.List<android.telecom.CallEndpoint>);
method public android.os.IBinder onBind(android.content.Intent);
method public void onBringToForeground(boolean);
method public void onCallAdded(android.telecom.Call);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method @Deprecated public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEndpointChanged(@NonNull android.telecom.CallEndpoint);
method public void onCallRemoved(android.telecom.Call);
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, String, android.os.Bundle);
method public void onMuteStateChanged(boolean);
method public void onSilenceRinger();
method public final void requestBluetoothAudio(@NonNull android.bluetooth.BluetoothDevice);
method public final void setAudioRoute(int);
method @Deprecated public final void requestBluetoothAudio(@NonNull android.bluetooth.BluetoothDevice);
method public final void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallEndpointException>);
method @Deprecated public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final String SERVICE_INTERFACE = "android.telecom.InCallService";
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -88,6 +88,7 @@ public abstract class Conference extends Conferenceable {
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,11 +224,25 @@ public abstract class Conference extends Conferenceable {
* @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.
*/
@@ -314,9 +329,34 @@ public abstract class Conference extends Conferenceable {
* 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.
*
@@ -730,6 +770,40 @@ public abstract class Conference extends Conferenceable {
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;

View File

@@ -19,6 +19,7 @@ package android.telecom;
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.Handler;
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.List;
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 @@ public abstract class Connection extends Conferenceable {
/** @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 @@ public abstract class Connection extends Conferenceable {
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 @@ public abstract class Connection extends Conferenceable {
* @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;
}
@@ -2456,6 +2466,43 @@ public abstract class Connection extends Conferenceable {
onCallAudioStateChanged(state);
}
/**
* 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 @@ public abstract class Connection extends Conferenceable {
* @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 @@ public abstract class Connection extends Conferenceable {
* <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,
@@ -3109,6 +3162,40 @@ public abstract class Connection extends Conferenceable {
}
}
/**
* 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
@@ -3160,9 +3247,34 @@ public abstract class Connection extends Conferenceable {
* 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.

View File

@@ -31,6 +31,7 @@ import android.os.Handler;
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.List;
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 @@ public abstract class ConnectionService extends Service {
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 @@ public abstract class ConnectionService extends Service {
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;
@@ -591,6 +599,51 @@ public abstract class ConnectionService extends Service {
}
}
@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) {
@@ -1527,6 +1580,48 @@ public abstract class ConnectionService extends Service {
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 @@ public abstract class ConnectionService extends Service {
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 @@ public abstract class ConnectionService extends Service {
}
}
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)) {

View File

@@ -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.Iterator;
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 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
}
/**
* 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.

View File

@@ -21,6 +21,7 @@ import android.os.Bundle;
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 @@ final class ConnectionServiceAdapterServant {
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) {

View File

@@ -19,12 +19,16 @@ package android.telecom;
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
@@ -226,6 +230,39 @@ public final class InCallAdapter {
}
}
/**
* 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.
*

View File

@@ -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.Handler;
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 com.android.internal.telecom.IInCallService;
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 @@ public abstract class InCallService extends Service {
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 @@ public abstract class InCallService extends Service {
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;
}
@@ -391,6 +417,22 @@ public abstract class InCallService extends Service {
mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, callAudioState).sendToTarget();
}
@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 @@ public abstract class InCallService extends Service {
*
* @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 @@ public abstract class InCallService extends Service {
* 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,13 +645,44 @@ public abstract class InCallService extends Service {
* {@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());
}
}
/**
* 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}
@@ -648,10 +728,38 @@ public abstract class InCallService extends Service {
* 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

View File

@@ -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.List;
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.
@@ -377,6 +381,21 @@ public final class Phone {
mInCallAdapter.requestBluetoothAudio(bluetoothAddress);
}
/**
* 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

View File

@@ -36,12 +36,10 @@ import com.android.internal.telecom.IVideoCallback;
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}

View File

@@ -21,6 +21,7 @@ import android.os.Bundle;
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 @@ final class RemoteConnectionService {
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 =

View File

@@ -20,6 +20,7 @@ import android.net.Uri;
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 @@ oneway interface IConnectionService {
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);

View File

@@ -19,6 +19,8 @@ package com.android.internal.telecom;
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 @@ oneway interface IConnectionServiceAdapter {
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);

View File

@@ -18,7 +18,9 @@ package com.android.internal.telecom;
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 @@ oneway interface IInCallAdapter {
void setAudioRoute(int route, String bluetoothAddress);
void requestCallEndpointChange(in CallEndpoint endpoint, in ResultReceiver callback);
void enterBackgroundAudioProcessing(String callId);
void exitBackgroundAudioProcessing(String callId, boolean shouldRing);

View File

@@ -19,6 +19,7 @@ package com.android.internal.telecom;
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 @@ oneway interface IInCallService {
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);