diff options
Diffstat (limited to 'telecomm')
11 files changed, 657 insertions, 133 deletions
diff --git a/telecomm/java/android/telecomm/AudioState.java b/telecomm/java/android/telecomm/AudioState.java index dc28b16..491af14 100644 --- a/telecomm/java/android/telecomm/AudioState.java +++ b/telecomm/java/android/telecomm/AudioState.java @@ -56,14 +56,12 @@ public final class AudioState implements Parcelable { /** Bit vector of all routes supported by this call. */ public final int supportedRouteMask; - /** @hide */ public AudioState(boolean isMuted, int route, int supportedRouteMask) { this.isMuted = isMuted; this.route = route; this.supportedRouteMask = supportedRouteMask; } - /** @hide */ public AudioState(AudioState state) { isMuted = state.isMuted; route = state.route; diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java index 7223574..d90ec13 100644 --- a/telecomm/java/android/telecomm/Call.java +++ b/telecomm/java/android/telecomm/Call.java @@ -363,6 +363,7 @@ public final class Call { private final Phone mPhone; private final String mTelecommCallId; private final InCallAdapter mInCallAdapter; + private final List<String> mChildrenIds = new ArrayList<>(); private final List<Call> mChildren = new ArrayList<>(); private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren); private final List<Listener> mListeners = new CopyOnWriteArrayList<>(); @@ -370,7 +371,8 @@ public final class Call { private final List<Call> mUnmodifiableConferenceableCalls = Collections.unmodifiableList(mConferenceableCalls); - private Call mParent = null; + private boolean mChildrenCached; + private String mParentId = null; private int mState; private List<String> mCannedTextResponses = null; private String mRemainingPostDialSequence; @@ -513,7 +515,10 @@ public final class Call { * child of any conference {@code Call}s. */ public Call getParent() { - return mParent; + if (mParentId != null) { + return mPhone.internalGetCallByTelecommId(mParentId); + } + return null; } /** @@ -523,6 +528,21 @@ public final class Call { * {@code List} otherwise. */ public List<Call> getChildren() { + if (!mChildrenCached) { + mChildrenCached = true; + mChildren.clear(); + + for(String id : mChildrenIds) { + Call call = mPhone.internalGetCallByTelecommId(id); + if (call == null) { + // At least one child was still not found, so do not save true for "cached" + mChildrenCached = false; + } else { + mChildren.add(call); + } + } + } + return mUnmodifiableChildren; } @@ -648,16 +668,18 @@ public final class Call { mState = state; } - if (parcelableCall.getParentCallId() != null) { - mParent = mPhone.internalGetCallByTelecommId(parcelableCall.getParentCallId()); + String parentId = parcelableCall.getParentCallId(); + boolean parentChanged = !Objects.equals(mParentId, parentId); + if (parentChanged) { + mParentId = parentId; } - mChildren.clear(); - if (parcelableCall.getChildCallIds() != null) { - for (int i = 0; i < parcelableCall.getChildCallIds().size(); i++) { - mChildren.add(mPhone.internalGetCallByTelecommId( - parcelableCall.getChildCallIds().get(i))); - } + List<String> childCallIds = parcelableCall.getChildCallIds(); + boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds); + if (childrenChanged) { + mChildrenIds.clear(); + mChildrenIds.addAll(parcelableCall.getChildCallIds()); + mChildrenCached = false; } List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds(); @@ -689,6 +711,12 @@ public final class Call { if (videoCallChanged) { fireVideoCallChanged(mVideoCall); } + if (parentChanged) { + fireParentChanged(getParent()); + } + if (childrenChanged) { + fireChildrenChanged(getChildren()); + } // If we have transitioned to DISCONNECTED, that means we need to notify clients and // remove ourselves from the Phone. Note that we do this after completing all state updates diff --git a/telecomm/java/android/telecomm/Conference.java b/telecomm/java/android/telecomm/Conference.java new file mode 100644 index 0000000..34b9dae --- /dev/null +++ b/telecomm/java/android/telecomm/Conference.java @@ -0,0 +1,238 @@ +/* + * 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.telecomm; + +import android.telephony.DisconnectCause; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Represents a conference call which can contain any number of {@link Connection} objects. + */ +public abstract class Conference { + + /** @hide */ + public abstract static class Listener { + public void onStateChanged(Conference conference, int oldState, int newState) {} + public void onDisconnected(Conference conference, int cause, String message) {} + public void onConnectionAdded(Conference conference, Connection connection) {} + public void onConnectionRemoved(Conference conference, Connection connection) {} + public void onDestroyed(Conference conference) {} + public void onCapabilitiesChanged(Conference conference, int capabilities) {} + } + + private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); + private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>(); + private final List<Connection> mUnmodifiableChildConnection = + Collections.unmodifiableList(mChildConnections); + + private PhoneAccountHandle mPhoneAccount; + private int mState = Connection.STATE_NEW; + private int mDisconnectCause = DisconnectCause.NOT_VALID; + private int mCapabilities; + private String mDisconnectMessage; + + public Conference(PhoneAccountHandle phoneAccount) { + mPhoneAccount = phoneAccount; + } + + public final PhoneAccountHandle getPhoneAccount() { + return mPhoneAccount; + } + + public final List<Connection> getConnections() { + return mUnmodifiableChildConnection; + } + + public final int getState() { + return mState; + } + + public final int getCapabilities() { + return mCapabilities; + } + + /** + * Invoked when the Conference and all it's {@link Connection}s should be disconnected. + */ + public void onDisconnect() {} + + /** + * Invoked when the specified {@link Connection} should be separated from the conference call. + * + * @param connection The connection to separate. + */ + public void onSeparate(Connection connection) {} + + /** + * Invoked when the conference should be put on hold. + */ + public void onHold() {} + + /** + * Invoked when the conference should be moved from hold to active. + */ + public void onUnhold() {} + + /** + * Sets state to be on hold. + */ + public final void setOnHold() { + setState(Connection.STATE_HOLDING); + } + + /** + * Sets state to be active. + */ + public final void setActive() { + setState(Connection.STATE_ACTIVE); + } + + /** + * Sets state to disconnected. + * + * @param cause The reason for the disconnection, any of + * {@link android.telephony.DisconnectCause}. + * @param message Optional call-service-provided message about the disconnect. + */ + public final void setDisconnected(int cause, String message) { + mDisconnectCause = cause; + mDisconnectMessage = message; + setState(Connection.STATE_DISCONNECTED); + for (Listener l : mListeners) { + l.onDisconnected(this, mDisconnectCause, mDisconnectMessage); + } + } + + /** + * Sets the cabilities of a conference. + */ + public final void setCapabilities(int capabilities) { + if (capabilities != mCapabilities) { + mCapabilities = capabilities; + + for (Listener l : mListeners) { + l.onCapabilitiesChanged(this, mCapabilities); + } + } + } + + /** + * Adds the specified connection as a child of this conference. + * + * @param connection The connection to add. + * @return True if the connection was successfully added. + */ + public boolean addConnection(Connection connection) { + if (connection != null && !mChildConnections.contains(connection)) { + if (connection.setConference(this)) { + mChildConnections.add(connection); + for (Listener l : mListeners) { + l.onConnectionAdded(this, connection); + } + return true; + } + } + return false; + } + + /** + * Removes the specified connection as a child of this conference. + * + * @param connection The connection to remove. + * @return True if the connection was successfully removed. + */ + public void removeConnection(Connection connection) { + if (connection != null && mChildConnections.remove(connection)) { + connection.resetConference(); + for (Listener l : mListeners) { + l.onConnectionRemoved(this, connection); + } + } + } + + /** + * Tears down the conference object and any of it's current connections. + */ + public void destroy() { + Log.d(this, "destroying conference : %s", this); + // Tear down the children. + for (Connection connection : new ArrayList<>(mChildConnections)) { + Log.d(this, "removing connection %s", connection); + removeConnection(connection); + } + + // If not yet disconnected, set the conference call as disconnected first. + if (mState != Connection.STATE_DISCONNECTED) { + Log.d(this, "setting to disconnected"); + setDisconnected(DisconnectCause.LOCAL, null); + } + + // ...and notify. + for (Listener l : mListeners) { + l.onDestroyed(this); + } + } + + /** + * Add a listener to be notified of a state change. + * + * @param listener The new listener. + * @return This conference. + * @hide + */ + public final Conference addListener(Listener listener) { + mListeners.add(listener); + return this; + } + + /** + * Removes the specified listener. + * + * @param listener The listener to remove. + * @return This conference. + * @hide + */ + public final Conference removeListener(Listener listener) { + mListeners.remove(listener); + return this; + } + + private void setState(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) { + int oldState = mState; + mState = newState; + for (Listener l : mListeners) { + l.onStateChanged(this, oldState, newState); + } + } + } +} diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java index 78c34a1..27debde 100644 --- a/telecomm/java/android/telecomm/Connection.java +++ b/telecomm/java/android/telecomm/Connection.java @@ -74,7 +74,6 @@ public abstract class Connection { public void onRequestingRingback(Connection c, boolean ringback) {} public void onDestroyed(Connection c) {} public void onCallCapabilitiesChanged(Connection c, int callCapabilities) {} - public void onParentConnectionChanged(Connection c, Connection parent) {} public void onVideoProviderChanged( Connection c, VideoProvider videoProvider) {} public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {} @@ -82,6 +81,7 @@ public abstract class Connection { public void onStartActivityFromInCall(Connection c, PendingIntent intent) {} public void onConferenceableConnectionsChanged( Connection c, List<Connection> conferenceableConnections) {} + public void onConferenceChanged(Connection c, Conference conference) {} } public static abstract class VideoProvider { @@ -455,9 +455,6 @@ public abstract class Connection { */ private final Set<Listener> mListeners = Collections.newSetFromMap( new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1)); - private final List<Connection> mChildConnections = new ArrayList<>(); - private final List<Connection> mUnmodifiableChildConnections = - Collections.unmodifiableList(mChildConnections); private final List<Connection> mConferenceableConnections = new ArrayList<>(); private final List<Connection> mUnmodifiableConferenceableConnections = Collections.unmodifiableList(mConferenceableConnections); @@ -470,7 +467,6 @@ public abstract class Connection { private int mCallerDisplayNamePresentation; private boolean mRequestingRingback = false; private int mCallCapabilities; - private Connection mParentConnection; private VideoProvider mVideoProvider; private boolean mAudioModeIsVoip; private StatusHints mStatusHints; @@ -478,6 +474,8 @@ public abstract class Connection { private int mFailureCode; private String mFailureMessage; private boolean mIsCanceled; + private Conference mConference; + private ConnectionService mConnectionService; /** * Create a new Connection. @@ -543,18 +541,19 @@ public abstract class Connection { } /** - * Returns whether this connection is requesting that the system play a ringback tone - * on its behalf. + * @return The conference that this connection is a part of. Null if it is not part of any + * conference. */ - public final boolean isRequestingRingback() { - return mRequestingRingback; + public final Conference getConference() { + return mConference; } /** - * Returns whether this connection is a conference connection (has child connections). + * Returns whether this connection is requesting that the system play a ringback tone + * on its behalf. */ - public final boolean isConferenceConnection() { - return !mChildConnections.isEmpty(); + public final boolean isRequestingRingback() { + return mRequestingRingback; } /** @@ -656,34 +655,6 @@ public abstract class Connection { } /** - * TODO: Needs documentation. - */ - public final void setParentConnection(Connection parentConnection) { - Log.d(this, "parenting %s to %s", this, parentConnection); - if (mParentConnection != parentConnection) { - if (mParentConnection != null) { - mParentConnection.removeChild(this); - } - mParentConnection = parentConnection; - if (mParentConnection != null) { - mParentConnection.addChild(this); - // do something if the child connections goes down to ZERO. - } - for (Listener l : mListeners) { - l.onParentConnectionChanged(this, mParentConnection); - } - } - } - - public final Connection getParentConnection() { - return mParentConnection; - } - - public final List<Connection> getChildConnections() { - return mUnmodifiableChildConnections; - } - - /** * Returns the connection's {@link PhoneCapabilities} */ public final int getCallCapabilities() { @@ -936,6 +907,60 @@ public abstract class Connection { return mUnmodifiableConferenceableConnections; } + /* + * @hide + */ + public final void setConnectionService(ConnectionService connectionService) { + if (mConnectionService != null) { + Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " + + "which is already associated with another ConnectionService."); + } else { + mConnectionService = connectionService; + } + } + + /** + * @hide + */ + public final void unsetConnectionService(ConnectionService connectionService) { + if (mConnectionService != connectionService) { + Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " + + "that does not belong to the ConnectionService."); + } else { + mConnectionService = null; + } + } + + /** + * Sets the conference that this connection is a part of. This will fail if the connection is + * already part of a conference call. {@link #resetConference} to un-set the conference first. + * + * @param conference The conference. + * @return {@code true} if the conference was successfully set. + * @hide + */ + public final boolean setConference(Conference conference) { + // We check to see if it is already part of another conference. + if (mConference == null && mConnectionService != null && + mConnectionService.containsConference(conference)) { + mConference = conference; + fireConferenceChanged(); + return true; + } + return false; + } + + /** + * Resets the conference that this connection is a part of. + * @hide + */ + public final void resetConference() { + if (mConference != null) { + mConference = null; + fireConferenceChanged(); + } + } + /** * Launches an activity for this connection on top of the in-call UI. * @@ -1022,11 +1047,6 @@ public abstract class Connection { public void onPostDialContinue(boolean proceed) {} /** - * TODO: Needs documentation. - */ - public void onChildrenChanged(List<Connection> children) {} - - /** * Called when the phone account UI was clicked. */ public void onPhoneAccountClicked() {} @@ -1073,18 +1093,6 @@ public abstract class Connection { return sNullConnection; } - private void addChild(Connection connection) { - Log.d(this, "adding child %s", connection); - mChildConnections.add(connection); - onChildrenChanged(mChildConnections); - } - - private void removeChild(Connection connection) { - Log.d(this, "removing child %s", connection); - mChildConnections.remove(connection); - onChildrenChanged(mChildConnections); - } - private void setState(int state) { if (mState == STATE_FAILED || mState == STATE_CANCELED) { Log.d(this, "Connection already %s; cannot transition out of this state.", @@ -1139,6 +1147,12 @@ public abstract class Connection { } } + private final void fireConferenceChanged() { + for (Listener l : mListeners) { + l.onConferenceChanged(this, mConference); + } + } + private final void clearConferenceableList() { for (Connection c : mConferenceableConnections) { c.removeConnectionListener(mConnectionDeathListener); diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index d2d4828..97a3102 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -39,6 +39,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.UUID; /** * A {@link android.app.Service} that provides telephone connections to processes running on an @@ -73,12 +75,14 @@ public abstract class ConnectionService extends Service { private final Map<String, Connection> mConnectionById = new HashMap<>(); private final Map<Connection, String> mIdByConnection = new HashMap<>(); + private final Map<String, Conference> mConferenceById = new HashMap<>(); + private final Map<Conference, String> mIdByConference = new HashMap<>(); private final RemoteConnectionManager mRemoteConnectionManager = new RemoteConnectionManager(); - - private boolean mAreAccountsInitialized = false; private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>(); private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter(); + private boolean mAreAccountsInitialized = false; + private final IBinder mBinder = new IConnectionService.Stub() { @Override public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) { @@ -155,10 +159,10 @@ public abstract class ConnectionService extends Service { } @Override - public void conference(String conferenceCallId, String callId) { + public void conference(String callId1, String callId2) { SomeArgs args = SomeArgs.obtain(); - args.arg1 = conferenceCallId; - args.arg2 = callId; + args.arg1 = callId1; + args.arg2 = callId2; mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); } @@ -270,9 +274,9 @@ public abstract class ConnectionService extends Service { case MSG_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { - String conferenceCallId = (String) args.arg1; - String callId = (String) args.arg2; - conference(conferenceCallId, callId); + String callId1 = (String) args.arg1; + String callId2 = (String) args.arg2; + conference(callId1, callId2); } finally { args.recycle(); } @@ -301,6 +305,51 @@ public abstract class ConnectionService extends Service { } }; + private final Conference.Listener mConferenceListener = new Conference.Listener() { + @Override + public void onStateChanged(Conference conference, int oldState, int newState) { + String id = mIdByConference.get(conference); + switch (newState) { + case Connection.STATE_ACTIVE: + mAdapter.setActive(id); + break; + case Connection.STATE_HOLDING: + mAdapter.setOnHold(id); + break; + case Connection.STATE_DISCONNECTED: + // handled by onDisconnected + break; + } + } + + @Override + public void onDisconnected(Conference conference, int cause, String message) { + String id = mIdByConference.get(conference); + mAdapter.setDisconnected(id, cause, message); + } + + @Override + public void onConnectionAdded(Conference conference, Connection connection) { + } + + @Override + public void onConnectionRemoved(Conference conference, Connection connection) { + } + + @Override + public void onDestroyed(Conference conference) { + removeConference(conference); + } + + @Override + public void onCapabilitiesChanged(Conference conference, int capabilities) { + String id = mIdByConference.get(conference); + Log.d(this, "call capabilities: conference: %s", + PhoneCapabilities.toString(capabilities)); + mAdapter.setCallCapabilities(id, capabilities); + } + }; + private final Connection.Listener mConnectionListener = new Connection.Listener() { @Override public void onStateChanged(Connection c, int state) { @@ -383,13 +432,6 @@ public abstract class ConnectionService extends Service { } @Override - public void onParentConnectionChanged(Connection c, Connection parent) { - String id = mIdByConnection.get(c); - String parentId = parent == null ? null : mIdByConnection.get(parent); - mAdapter.setIsConferenced(id, parentId); - } - - @Override public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) { String id = mIdByConnection.get(c); mAdapter.setVideoProvider(id, videoProvider); @@ -426,6 +468,18 @@ public abstract class ConnectionService extends Service { Collections.sort(conferenceableCallIds); mAdapter.setConferenceableConnections(id, conferenceableCallIds); } + + @Override + public void onConferenceChanged(Connection connection, Conference conference) { + String id = mIdByConnection.get(connection); + if (id != null) { + String conferenceId = null; + if (conference != null) { + conferenceId = mIdByConference.get(conference); + } + mAdapter.setIsConferenced(id, conferenceId); + } + } }; /** {@inheritDoc} */ @@ -483,6 +537,13 @@ public abstract class ConnectionService extends Service { } c.removeConnectionListener(this); } + + @Override + public void onDestroyed(Connection c) { + // Listen to onDestroy in case the connection is destroyed before + // transitioning to another state. + c.removeConnectionListener(this); + } }); Log.d(this, "Connection created in state INITIALIZING"); connectionCreated(callId, request, createdConnection); @@ -586,38 +647,22 @@ public abstract class ConnectionService extends Service { findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone(); } - private void conference(final String conferenceCallId, String callId) { - Log.d(this, "conference %s, %s", conferenceCallId, callId); + private void conference(String callId1, String callId2) { + Log.d(this, "conference %s, %s", callId1, callId2); - Connection connection = findConnectionForAction(callId, "conference"); - if (connection == Connection.getNullConnection()) { - Log.w(this, "Connection missing in conference request %s.", callId); + Connection connection1 = findConnectionForAction(callId1, "conference"); + if (connection1 == Connection.getNullConnection()) { + Log.w(this, "Connection1 missing in conference request %s.", callId1); return; } - onCreateConferenceConnection(conferenceCallId, connection, - new Response<String, Connection>() { - /** ${inheritDoc} */ - @Override - public void onResult(String ignored, Connection... result) { - Log.d(this, "onCreateConference.Response %s", (Object[]) result); - if (result != null && result.length == 1) { - Connection conferenceConnection = result[0]; - if (!mIdByConnection.containsKey(conferenceConnection)) { - Log.v(this, "sending new conference call %s", conferenceCallId); - mAdapter.addConferenceCall(conferenceCallId); - addConnection(conferenceCallId, conferenceConnection); - } - } - } + Connection connection2 = findConnectionForAction(callId2, "conference"); + if (connection2 == Connection.getNullConnection()) { + Log.w(this, "Connection2 missing in conference request %s.", callId2); + return; + } - /** ${inheritDoc} */ - @Override - public void onError(String request, int code, String reason) { - // no-op - } - } - ); + onConference(connection1, connection2); } private void splitFromConference(String callId) { @@ -712,6 +757,40 @@ public abstract class ConnectionService extends Service { } /** + * Adds a new conference call. When a conference call is created either as a result of an + * explicit request via {@link #onConference} or otherwise, the connection service should supply + * an instance of {@link Conference} by invoking this method. A conference call provided by this + * method will persist until {@link Conference#destroy} is invoked on the conference instance. + * + * @param conference The new conference object. + */ + public final void addConference(Conference conference) { + String id = addConferenceInternal(conference); + if (id != null) { + List<String> connectionIds = new ArrayList<>(2); + for (Connection connection : conference.getConnections()) { + if (mIdByConnection.containsKey(connection)) { + connectionIds.add(mIdByConnection.get(connection)); + } + } + ParcelableConference parcelableConference = new ParcelableConference( + conference.getPhoneAccount(), + conference.getState(), + conference.getCapabilities(), + connectionIds); + mAdapter.addConferenceCall(id, parcelableConference); + + // Go through any child calls and set the parent. + for (Connection connection : conference.getConnections()) { + String connectionId = mIdByConnection.get(connection); + if (connectionId != null) { + mAdapter.setIsConferenced(connectionId, id); + } + } + } + } + + /** * Returns all the active {@code Connection}s for which this {@code ConnectionService} * has taken responsibility. * @@ -767,22 +846,14 @@ public abstract class ConnectionService extends Service { } /** - * Returns a new or existing conference connection when the the user elects to convert the - * specified connection into a conference call. The specified connection can be any connection - * which had previously specified itself as conference-capable including both simple connections - * and connections previously returned from this method. - * <p> - * TODO: To be refactored out with conference call re-engineering<br/> - * TODO: Also remove class {@link Response} once this method is removed + * Conference two specified connections. Invoked when the user has made a request to merge the + * specified connections into a conference call. In response, the connection service should + * create an instance of {@link Conference} and pass it into {@link #addConference}. * - * @param connection The connection from which the user opted to start a conference call. - * @param token The token to be passed into the response callback. - * @param callback The callback for providing the potentially-new conference connection. + * @param connection1 A connection to merge into a conference call. + * @param connection2 A connection to merge into a conference call. */ - public void onCreateConferenceConnection( - String token, - Connection connection, - Response<String, Connection> callback) {} + public void onConference(Connection connection1, Connection connection2) {} /** * Notifies that a connection has been added to this connection service and sent to Telecomm. @@ -798,6 +869,13 @@ public abstract class ConnectionService extends Service { */ public void onConnectionRemoved(Connection connection) {} + /** + * @hide + */ + public boolean containsConference(Conference conference) { + return mIdByConference.containsKey(conference); + } + private void onAccountsInitialized() { mAreAccountsInitialized = true; for (Runnable r : mPreInitializationConnectionRequests) { @@ -810,11 +888,13 @@ public abstract class ConnectionService extends Service { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); connection.addConnectionListener(mConnectionListener); + connection.setConnectionService(this); onConnectionAdded(connection); } private void removeConnection(Connection connection) { String id = mIdByConnection.get(connection); + connection.unsetConnectionService(this); connection.removeConnectionListener(mConnectionListener); mConnectionById.remove(mIdByConnection.get(connection)); mIdByConnection.remove(connection); @@ -822,6 +902,31 @@ public abstract class ConnectionService extends Service { mAdapter.removeCall(id); } + private String addConferenceInternal(Conference conference) { + if (mIdByConference.containsKey(conference)) { + Log.w(this, "Re-adding an existing conference: %s.", conference); + } else if (conference != null) { + String id = UUID.randomUUID().toString(); + mConferenceById.put(id, conference); + mIdByConference.put(conference, id); + conference.addListener(mConferenceListener); + return id; + } + + return null; + } + + private void removeConference(Conference conference) { + if (mIdByConference.containsKey(conference)) { + conference.removeListener(mConferenceListener); + + String id = mIdByConference.get(conference); + mConferenceById.remove(id); + mIdByConference.remove(conference); + mAdapter.removeCall(id); + } + } + private Connection findConnectionForAction(String callId, String action) { if (mConnectionById.containsKey(callId)) { return mConnectionById.get(callId); diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java index 4144b81..0188e62 100644 --- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java @@ -256,10 +256,10 @@ final class ConnectionServiceAdapter implements DeathRecipient { * * @param callId The unique ID of the conference call. */ - void addConferenceCall(String callId) { + void addConferenceCall(String callId, ParcelableConference parcelableConference) { for (IConnectionServiceAdapter adapter : mAdapters) { try { - adapter.addConferenceCall(callId); + adapter.addConferenceCall(callId, parcelableConference); } catch (RemoteException ignored) { } } diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java index 2632924..2654ace 100644 --- a/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java @@ -149,9 +149,16 @@ final class ConnectionServiceAdapterServant { } break; } - case MSG_ADD_CONFERENCE_CALL: - mDelegate.addConferenceCall((String) msg.obj); + case MSG_ADD_CONFERENCE_CALL: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.addConferenceCall( + (String) args.arg1, (ParcelableConference) args.arg2); + } finally { + args.recycle(); + } break; + } case MSG_REMOVE_CALL: mDelegate.removeCall((String) msg.obj); break; @@ -323,8 +330,11 @@ final class ConnectionServiceAdapterServant { } @Override - public void addConferenceCall(String callId) { - mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, callId).sendToTarget(); + public void addConferenceCall(String callId, ParcelableConference parcelableConference) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = parcelableConference; + mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, args).sendToTarget(); } @Override diff --git a/telecomm/java/android/telecomm/ParcelableConference.aidl b/telecomm/java/android/telecomm/ParcelableConference.aidl new file mode 100644 index 0000000..a260085 --- /dev/null +++ b/telecomm/java/android/telecomm/ParcelableConference.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 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.telecomm; + +parcelable ParcelableConference; diff --git a/telecomm/java/android/telecomm/ParcelableConference.java b/telecomm/java/android/telecomm/ParcelableConference.java new file mode 100644 index 0000000..b279861 --- /dev/null +++ b/telecomm/java/android/telecomm/ParcelableConference.java @@ -0,0 +1,111 @@ +/* + * Copyright 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.telecomm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * A parcelable representation of a conference connection. + * @hide + */ +public final class ParcelableConference implements Parcelable { + + private PhoneAccountHandle mPhoneAccount; + private int mState; + private int mCapabilities; + private List<String> mConnectionIds; + + public ParcelableConference( + PhoneAccountHandle phoneAccount, + int state, + int capabilities, + List<String> connectionIds) { + mPhoneAccount = phoneAccount; + mState = state; + mCapabilities = capabilities; + mConnectionIds = connectionIds; + } + + @Override + public String toString() { + return (new StringBuffer()) + .append("account: ") + .append(mPhoneAccount) + .append(", state: ") + .append(Connection.stateToString(mState)) + .append(", capabilities: ") + .append(PhoneCapabilities.toString(mCapabilities)) + .append(", children: ") + .append(mConnectionIds) + .toString(); + } + + public PhoneAccountHandle getPhoneAccount() { + return mPhoneAccount; + } + + public int getState() { + return mState; + } + + public int getCapabilities() { + return mCapabilities; + } + + public List<String> getConnectionIds() { + return mConnectionIds; + } + + public static final Parcelable.Creator<ParcelableConference> CREATOR = + new Parcelable.Creator<ParcelableConference> () { + @Override + public ParcelableConference createFromParcel(Parcel source) { + ClassLoader classLoader = ParcelableConference.class.getClassLoader(); + PhoneAccountHandle phoneAccount = source.readParcelable(classLoader); + int state = source.readInt(); + int capabilities = source.readInt(); + List<String> connectionIds = new ArrayList<>(2); + source.readList(connectionIds, classLoader); + + return new ParcelableConference(phoneAccount, state, capabilities, connectionIds); + } + + @Override + public ParcelableConference[] newArray(int size) { + return new ParcelableConference[size]; + } + }; + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** Writes ParcelableConference object into a Parcel. */ + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeParcelable(mPhoneAccount, 0); + destination.writeInt(mState); + destination.writeInt(mCapabilities); + destination.writeList(mConnectionIds); + } +} diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java index 9a1729f..dedb10e 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionService.java +++ b/telecomm/java/android/telecomm/RemoteConnectionService.java @@ -135,7 +135,7 @@ final class RemoteConnectionService { } @Override - public void addConferenceCall(String callId) { + public void addConferenceCall(String callId, ParcelableConference parcelableConference) { // not supported for remote connections. } diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl index fd4e931..e6ebae5 100644 --- a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.net.Uri; import android.telecomm.ConnectionRequest; import android.telecomm.ParcelableConnection; +import android.telecomm.ParcelableConference; import android.telecomm.StatusHints; import com.android.internal.telecomm.IVideoProvider; @@ -63,7 +64,7 @@ oneway interface IConnectionServiceAdapter { void setIsConferenced(String callId, String conferenceCallId); - void addConferenceCall(String callId); + void addConferenceCall(String callId, in ParcelableConference conference); void removeCall(String callId); |