You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
607 lines
21 KiB
607 lines
21 KiB
/*
|
|
* Copyright (C) 2014 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.Nullable;
|
|
import android.annotation.SystemApi;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.RemoteException;
|
|
|
|
import com.android.internal.telecom.IConnectionService;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
|
|
|
/**
|
|
* A conference provided to a {@link ConnectionService} by another {@code ConnectionService} through
|
|
* {@link ConnectionService#conferenceRemoteConnections}. Once created, a {@code RemoteConference}
|
|
* can be used to control the conference call or monitor changes through
|
|
* {@link RemoteConnection.Callback}.
|
|
*
|
|
* @see ConnectionService#onRemoteConferenceAdded
|
|
*/
|
|
public final class RemoteConference {
|
|
|
|
/**
|
|
* Callback base class for {@link RemoteConference}.
|
|
*/
|
|
public abstract static class Callback {
|
|
/**
|
|
* Invoked when the state of this {@code RemoteConferece} has changed. See
|
|
* {@link #getState()}.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param oldState The previous state of the {@code RemoteConference}.
|
|
* @param newState The new state of the {@code RemoteConference}.
|
|
*/
|
|
public void onStateChanged(RemoteConference conference, int oldState, int newState) {}
|
|
|
|
/**
|
|
* Invoked when this {@code RemoteConference} is disconnected.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param disconnectCause The ({@see DisconnectCause}) associated with this failed
|
|
* conference.
|
|
*/
|
|
public void onDisconnected(RemoteConference conference, DisconnectCause disconnectCause) {}
|
|
|
|
/**
|
|
* Invoked when a {@link RemoteConnection} is added to the conference call.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param connection The {@link RemoteConnection} being added.
|
|
*/
|
|
public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {}
|
|
|
|
/**
|
|
* Invoked when a {@link RemoteConnection} is removed from the conference call.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param connection The {@link RemoteConnection} being removed.
|
|
*/
|
|
public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {}
|
|
|
|
/**
|
|
* Indicates that the call capabilities of this {@code RemoteConference} have changed.
|
|
* See {@link #getConnectionCapabilities()}.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param connectionCapabilities The new capabilities of the {@code RemoteConference}.
|
|
*/
|
|
public void onConnectionCapabilitiesChanged(
|
|
RemoteConference conference,
|
|
int connectionCapabilities) {}
|
|
|
|
/**
|
|
* Indicates that the call properties of this {@code RemoteConference} have changed.
|
|
* See {@link #getConnectionProperties()}.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param connectionProperties The new properties of the {@code RemoteConference}.
|
|
*/
|
|
public void onConnectionPropertiesChanged(
|
|
RemoteConference conference,
|
|
int connectionProperties) {}
|
|
|
|
|
|
/**
|
|
* Invoked when the set of {@link RemoteConnection}s which can be added to this conference
|
|
* call have changed.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param conferenceableConnections The list of conferenceable {@link RemoteConnection}s.
|
|
*/
|
|
public void onConferenceableConnectionsChanged(
|
|
RemoteConference conference,
|
|
List<RemoteConnection> conferenceableConnections) {}
|
|
|
|
/**
|
|
* Indicates that this {@code RemoteConference} has been destroyed. No further requests
|
|
* should be made to the {@code RemoteConference}, and references to it should be cleared.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
*/
|
|
public void onDestroyed(RemoteConference conference) {}
|
|
|
|
/**
|
|
* Handles changes to the {@code RemoteConference} extras.
|
|
*
|
|
* @param conference The {@code RemoteConference} invoking this method.
|
|
* @param extras The extras containing other information associated with the conference.
|
|
*/
|
|
public void onExtrasChanged(RemoteConference conference, @Nullable Bundle extras) {}
|
|
}
|
|
|
|
private final String mId;
|
|
private final IConnectionService mConnectionService;
|
|
|
|
private final Set<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArraySet<>();
|
|
private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>();
|
|
private final List<RemoteConnection> mUnmodifiableChildConnections =
|
|
Collections.unmodifiableList(mChildConnections);
|
|
private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
|
|
private final List<RemoteConnection> mUnmodifiableConferenceableConnections =
|
|
Collections.unmodifiableList(mConferenceableConnections);
|
|
|
|
private int mState = Connection.STATE_NEW;
|
|
private DisconnectCause mDisconnectCause;
|
|
private int mConnectionCapabilities;
|
|
private int mConnectionProperties;
|
|
private Bundle mExtras;
|
|
|
|
/** @hide */
|
|
RemoteConference(String id, IConnectionService connectionService) {
|
|
mId = id;
|
|
mConnectionService = connectionService;
|
|
}
|
|
|
|
/** @hide */
|
|
RemoteConference(DisconnectCause disconnectCause) {
|
|
mId = "NULL";
|
|
mConnectionService = null;
|
|
mState = Connection.STATE_DISCONNECTED;
|
|
mDisconnectCause = disconnectCause;
|
|
}
|
|
|
|
/** @hide */
|
|
String getId() {
|
|
return mId;
|
|
}
|
|
|
|
/** @hide */
|
|
void setDestroyed() {
|
|
for (RemoteConnection connection : mChildConnections) {
|
|
connection.setConference(null);
|
|
}
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onDestroyed(conference);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void setState(final int newState) {
|
|
if (newState != Connection.STATE_ACTIVE &&
|
|
newState != Connection.STATE_HOLDING &&
|
|
newState != Connection.STATE_DISCONNECTED) {
|
|
Log.w(this, "Unsupported state transition for Conference call.",
|
|
Connection.stateToString(newState));
|
|
return;
|
|
}
|
|
|
|
if (mState != newState) {
|
|
final int oldState = mState;
|
|
mState = newState;
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onStateChanged(conference, oldState, newState);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void addConnection(final RemoteConnection connection) {
|
|
if (!mChildConnections.contains(connection)) {
|
|
mChildConnections.add(connection);
|
|
connection.setConference(this);
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onConnectionAdded(conference, connection);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void removeConnection(final RemoteConnection connection) {
|
|
if (mChildConnections.contains(connection)) {
|
|
mChildConnections.remove(connection);
|
|
connection.setConference(null);
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onConnectionRemoved(conference, connection);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void setConnectionCapabilities(final int connectionCapabilities) {
|
|
if (mConnectionCapabilities != connectionCapabilities) {
|
|
mConnectionCapabilities = connectionCapabilities;
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onConnectionCapabilitiesChanged(
|
|
conference, mConnectionCapabilities);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void setConnectionProperties(final int connectionProperties) {
|
|
if (mConnectionProperties != connectionProperties) {
|
|
mConnectionProperties = connectionProperties;
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onConnectionPropertiesChanged(
|
|
conference, mConnectionProperties);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
|
|
mConferenceableConnections.clear();
|
|
mConferenceableConnections.addAll(conferenceableConnections);
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onConferenceableConnectionsChanged(
|
|
conference, mUnmodifiableConferenceableConnections);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void setDisconnected(final DisconnectCause disconnectCause) {
|
|
if (mState != Connection.STATE_DISCONNECTED) {
|
|
mDisconnectCause = disconnectCause;
|
|
setState(Connection.STATE_DISCONNECTED);
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onDisconnected(conference, disconnectCause);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
void putExtras(final Bundle extras) {
|
|
if (extras == null) {
|
|
return;
|
|
}
|
|
if (mExtras == null) {
|
|
mExtras = new Bundle();
|
|
}
|
|
mExtras.putAll(extras);
|
|
|
|
notifyExtrasChanged();
|
|
}
|
|
|
|
/** @hide */
|
|
void removeExtras(List<String> keys) {
|
|
if (mExtras == null || keys == null || keys.isEmpty()) {
|
|
return;
|
|
}
|
|
for (String key : keys) {
|
|
mExtras.remove(key);
|
|
}
|
|
|
|
notifyExtrasChanged();
|
|
}
|
|
|
|
private void notifyExtrasChanged() {
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
final RemoteConference conference = this;
|
|
final Callback callback = record.getCallback();
|
|
record.getHandler().post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
callback.onExtrasChanged(conference, mExtras);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the list of {@link RemoteConnection}s contained in this conference.
|
|
*
|
|
* @return A list of child connections.
|
|
*/
|
|
public final List<RemoteConnection> getConnections() {
|
|
return mUnmodifiableChildConnections;
|
|
}
|
|
|
|
/**
|
|
* Gets the state of the conference call. See {@link Connection} for valid values.
|
|
*
|
|
* @return A constant representing the state the conference call is currently in.
|
|
*/
|
|
public final int getState() {
|
|
return mState;
|
|
}
|
|
|
|
/**
|
|
* Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
|
|
* {@link Connection} for valid values.
|
|
*
|
|
* @return A bitmask of the capabilities of the conference call.
|
|
*/
|
|
public final int getConnectionCapabilities() {
|
|
return mConnectionCapabilities;
|
|
}
|
|
|
|
/**
|
|
* Returns the properties of the conference. See {@code PROPERTY_*} constants in class
|
|
* {@link Connection} for valid values.
|
|
*
|
|
* @return A bitmask of the properties of the conference call.
|
|
*/
|
|
public final int getConnectionProperties() {
|
|
return mConnectionProperties;
|
|
}
|
|
|
|
/**
|
|
* Obtain the extras associated with this {@code RemoteConnection}.
|
|
*
|
|
* @return The extras for this connection.
|
|
*/
|
|
public final Bundle getExtras() {
|
|
return mExtras;
|
|
}
|
|
|
|
/**
|
|
* Disconnects the conference call as well as the child {@link RemoteConnection}s.
|
|
*/
|
|
public void disconnect() {
|
|
try {
|
|
mConnectionService.disconnect(mId, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the specified {@link RemoteConnection} from the conference. This causes the
|
|
* {@link RemoteConnection} to become a standalone connection. This is a no-op if the
|
|
* {@link RemoteConnection} does not belong to this conference.
|
|
*
|
|
* @param connection The remote-connection to remove.
|
|
*/
|
|
public void separate(RemoteConnection connection) {
|
|
if (mChildConnections.contains(connection)) {
|
|
try {
|
|
mConnectionService.splitFromConference(connection.getId(), null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merges all {@link RemoteConnection}s of this conference into a single call. This should be
|
|
* invoked only if the conference contains the capability
|
|
* {@link Connection#CAPABILITY_MERGE_CONFERENCE}, otherwise it is a no-op. The presence of said
|
|
* capability indicates that the connections of this conference, despite being part of the
|
|
* same conference object, are yet to have their audio streams merged; this is a common pattern
|
|
* for CDMA conference calls, but the capability is not used for GSM and SIP conference calls.
|
|
* Invoking this method will cause the unmerged child connections to merge their audio
|
|
* streams.
|
|
*/
|
|
public void merge() {
|
|
try {
|
|
mConnectionService.mergeConference(mId, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Swaps the active audio stream between the conference's child {@link RemoteConnection}s.
|
|
* This should be invoked only if the conference contains the capability
|
|
* {@link Connection#CAPABILITY_SWAP_CONFERENCE}, otherwise it is a no-op. This is only used by
|
|
* {@link ConnectionService}s that create conferences for connections that do not yet have
|
|
* their audio streams merged; this is a common pattern for CDMA conference calls, but the
|
|
* capability is not used for GSM and SIP conference calls. Invoking this method will change the
|
|
* active audio stream to a different child connection.
|
|
*/
|
|
public void swap() {
|
|
try {
|
|
mConnectionService.swapConference(mId, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Puts the conference on hold.
|
|
*/
|
|
public void hold() {
|
|
try {
|
|
mConnectionService.hold(mId, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unholds the conference call.
|
|
*/
|
|
public void unhold() {
|
|
try {
|
|
mConnectionService.unhold(mId, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link DisconnectCause} for the conference if it is in the state
|
|
* {@link Connection#STATE_DISCONNECTED}. If the conference is not disconnected, this will
|
|
* return null.
|
|
*
|
|
* @return The disconnect cause.
|
|
*/
|
|
public DisconnectCause getDisconnectCause() {
|
|
return mDisconnectCause;
|
|
}
|
|
|
|
/**
|
|
* Requests that the conference start playing the specified DTMF tone.
|
|
*
|
|
* @param digit The digit for which to play a DTMF tone.
|
|
*/
|
|
public void playDtmfTone(char digit) {
|
|
try {
|
|
mConnectionService.playDtmfTone(mId, digit, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops the most recent request to play a DTMF tone.
|
|
*
|
|
* @see #playDtmfTone
|
|
*/
|
|
public void stopDtmfTone() {
|
|
try {
|
|
mConnectionService.stopDtmfTone(mId, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request to change the conference's audio routing to the specified state. The specified state
|
|
* can include audio routing (Bluetooth, Speaker, etc) and muting state.
|
|
*
|
|
* @see android.telecom.AudioState
|
|
* @deprecated Use {@link #setCallAudioState(CallAudioState)} instead.
|
|
* @hide
|
|
*/
|
|
@SystemApi
|
|
@Deprecated
|
|
public void setAudioState(AudioState state) {
|
|
setCallAudioState(new CallAudioState(state));
|
|
}
|
|
|
|
/**
|
|
* Request to change the conference's audio routing to the specified state. The specified state
|
|
* can include audio routing (Bluetooth, Speaker, etc) and muting state.
|
|
*/
|
|
public void setCallAudioState(CallAudioState state) {
|
|
try {
|
|
mConnectionService.onCallAudioStateChanged(mId, state, null /*Session.Info*/);
|
|
} catch (RemoteException e) {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a list of independent connections that can me merged with this conference.
|
|
*
|
|
* @return A list of conferenceable connections.
|
|
*/
|
|
public List<RemoteConnection> getConferenceableConnections() {
|
|
return mUnmodifiableConferenceableConnections;
|
|
}
|
|
|
|
/**
|
|
* Register a callback through which to receive state updates for this conference.
|
|
*
|
|
* @param callback The callback to notify of state changes.
|
|
*/
|
|
public final void registerCallback(Callback callback) {
|
|
registerCallback(callback, new Handler());
|
|
}
|
|
|
|
/**
|
|
* Registers a callback through which to receive state updates for this conference.
|
|
* Callbacks will be notified using the specified handler, if provided.
|
|
*
|
|
* @param callback The callback to notify of state changes.
|
|
* @param handler The handler on which to execute the callbacks.
|
|
*/
|
|
public final void registerCallback(Callback callback, Handler handler) {
|
|
unregisterCallback(callback);
|
|
if (callback != null && handler != null) {
|
|
mCallbackRecords.add(new CallbackRecord(callback, handler));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregisters a previously registered callback.
|
|
*
|
|
* @see #registerCallback
|
|
*
|
|
* @param callback The callback to unregister.
|
|
*/
|
|
public final void unregisterCallback(Callback callback) {
|
|
if (callback != null) {
|
|
for (CallbackRecord<Callback> record : mCallbackRecords) {
|
|
if (record.getCallback() == callback) {
|
|
mCallbackRecords.remove(record);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a {@link RemoteConference} represents a failure, and which will
|
|
* be in {@link Connection#STATE_DISCONNECTED}.
|
|
*
|
|
* @param disconnectCause The disconnect cause.
|
|
* @return a failed {@link RemoteConference}
|
|
* @hide
|
|
*/
|
|
public static RemoteConference failure(DisconnectCause disconnectCause) {
|
|
return new RemoteConference(disconnectCause);
|
|
}
|
|
}
|