diff options
Diffstat (limited to 'telecomm/java')
| -rw-r--r-- | telecomm/java/android/telecomm/Call.java | 24 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/CallProperties.java | 26 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/Conference.java | 8 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/ConnectionService.java | 50 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/InCallService.java | 10 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/ParcelableCall.java | 16 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/ParcelableConnection.java | 46 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/Phone.java | 15 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/PhoneCapabilities.java | 1 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/RemoteConference.java | 198 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/RemoteConnection.java | 75 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/RemoteConnectionManager.java | 30 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/RemoteConnectionService.java | 177 | ||||
| -rw-r--r-- | telecomm/java/android/telecomm/TelecommManager.java | 74 | ||||
| -rw-r--r-- | telecomm/java/com/android/internal/telecomm/ITelecommService.aidl | 10 |
15 files changed, 664 insertions, 96 deletions
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java index a71f739..7c596c1 100644 --- a/telecomm/java/android/telecomm/Call.java +++ b/telecomm/java/android/telecomm/Call.java @@ -89,6 +89,7 @@ public final class Call { private final int mCallerDisplayNamePresentation; private final PhoneAccountHandle mAccountHandle; private final int mCallCapabilities; + private final int mCallProperties; private final int mDisconnectCauseCode; private final String mDisconnectCauseMessage; private final long mConnectTimeMillis; @@ -145,6 +146,14 @@ public final class Call { } /** + * @return A bitmask of the properties of the {@code Call}, as defined in + * {@link CallProperties}. + */ + public int getCallProperties() { + return mCallProperties; + } + + /** * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed * as a code chosen from among those declared in {@link DisconnectCause}. */ @@ -210,6 +219,7 @@ public final class Call { d.mCallerDisplayNamePresentation) && Objects.equals(mAccountHandle, d.mAccountHandle) && Objects.equals(mCallCapabilities, d.mCallCapabilities) && + Objects.equals(mCallProperties, d.mCallProperties) && Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) && Objects.equals(mDisconnectCauseMessage, d.mDisconnectCauseMessage) && Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && @@ -230,6 +240,7 @@ public final class Call { Objects.hashCode(mCallerDisplayNamePresentation) + Objects.hashCode(mAccountHandle) + Objects.hashCode(mCallCapabilities) + + Objects.hashCode(mCallProperties) + Objects.hashCode(mDisconnectCauseCode) + Objects.hashCode(mDisconnectCauseMessage) + Objects.hashCode(mConnectTimeMillis) + @@ -247,6 +258,7 @@ public final class Call { int callerDisplayNamePresentation, PhoneAccountHandle accountHandle, int capabilities, + int properties, int disconnectCauseCode, String disconnectCauseMessage, long connectTimeMillis, @@ -260,6 +272,7 @@ public final class Call { mCallerDisplayNamePresentation = callerDisplayNamePresentation; mAccountHandle = accountHandle; mCallCapabilities = capabilities; + mCallProperties = properties; mDisconnectCauseCode = disconnectCauseCode; mDisconnectCauseMessage = disconnectCauseMessage; mConnectTimeMillis = connectTimeMillis; @@ -642,6 +655,7 @@ public final class Call { parcelableCall.getCallerDisplayNamePresentation(), parcelableCall.getAccountHandle(), parcelableCall.getCapabilities(), + parcelableCall.getProperties(), parcelableCall.getDisconnectCauseCode(), parcelableCall.getDisconnectCauseMsg(), parcelableCall.getConnectTimeMillis(), @@ -743,6 +757,16 @@ public final class Call { fireStartActivity(intent); } + /** {@hide} */ + final void internalSetDisconnected() { + if (mState != Call.STATE_DISCONNECTED) { + mState = Call.STATE_DISCONNECTED; + fireStateChanged(mState); + fireCallDestroyed(); + mPhone.internalRemoveCall(this); + } + } + private void fireStateChanged(int newState) { for (Listener listener : mListeners) { listener.onStateChanged(this, newState); diff --git a/telecomm/java/android/telecomm/CallProperties.java b/telecomm/java/android/telecomm/CallProperties.java new file mode 100644 index 0000000..90eb0cb --- /dev/null +++ b/telecomm/java/android/telecomm/CallProperties.java @@ -0,0 +1,26 @@ +/* + * 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; + +/** + * Defines properties of a phone call which may be affected by changes to the call. + * @hide + */ +public class CallProperties { + /** Call is currently in a conference call. */ + public static final int CONFERENCE = 0x00000001; +} diff --git a/telecomm/java/android/telecomm/Conference.java b/telecomm/java/android/telecomm/Conference.java index 44accb7..879ff66 100644 --- a/telecomm/java/android/telecomm/Conference.java +++ b/telecomm/java/android/telecomm/Conference.java @@ -18,9 +18,7 @@ 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; @@ -43,7 +41,7 @@ public abstract class Conference { private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); private final List<Connection> mChildConnections = new CopyOnWriteArrayList<>(); - private final List<Connection> mUnmodifiableChildConnection = + private final List<Connection> mUnmodifiableChildConnections = Collections.unmodifiableList(mChildConnections); private PhoneAccountHandle mPhoneAccount; @@ -61,7 +59,7 @@ public abstract class Conference { } public final List<Connection> getConnections() { - return mUnmodifiableChildConnection; + return mUnmodifiableChildConnections; } public final int getState() { @@ -125,7 +123,7 @@ public abstract class Conference { } /** - * Sets the cabilities of a conference. + * Sets the capabilities of a conference. */ public final void setCapabilities(int capabilities) { if (capabilities != mCapabilities) { diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index b77bb18..4696815 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -39,7 +39,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; /** @@ -79,7 +78,8 @@ public abstract class ConnectionService extends Service { 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 final RemoteConnectionManager mRemoteConnectionManager = + new RemoteConnectionManager(this); private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>(); private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter(); @@ -461,15 +461,9 @@ public abstract class ConnectionService extends Service { @Override public void onConferenceableConnectionsChanged( Connection connection, List<Connection> conferenceableConnections) { - String id = mIdByConnection.get(connection); - List<String> conferenceableCallIds = new ArrayList<>(conferenceableConnections.size()); - for (Connection c : conferenceableConnections) { - if (mIdByConnection.containsKey(c)) { - conferenceableCallIds.add(mIdByConnection.get(c)); - } - } - Collections.sort(conferenceableCallIds); - mAdapter.setConferenceableConnections(id, conferenceableCallIds); + mAdapter.setConferenceableConnections( + mIdByConnection.get(connection), + createConnectionIdList(conferenceableConnections)); } @Override @@ -542,7 +536,8 @@ public abstract class ConnectionService extends Service { connection.getAudioModeIsVoip(), connection.getStatusHints(), connection.getDisconnectCause(), - connection.getDisconnectMessage())); + connection.getDisconnectMessage(), + createConnectionIdList(connection.getConferenceableConnections()))); } private void abort(String callId) { @@ -717,6 +712,15 @@ public abstract class ConnectionService extends Service { } /** + * Adds two {@code RemoteConnection}s to some {@code RemoteConference}. + */ + public final void conferenceRemoteConnections( + RemoteConnection a, + RemoteConnection b) { + mRemoteConnectionManager.conferenceRemoteConnections(a, b); + } + + /** * 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 @@ -824,11 +828,17 @@ public abstract class ConnectionService extends Service { /** * Notified that a connection has been removed from this connection service. + * <p> + * TODO: Deprecate this since we can listen to the Connection onDestroyed() to determine when + * it is destroyed. This then percolates down to the RemoteConference stuff, where we can also + * have a callback for one being added, but we don't need one for being destroyed. * * @param connection The connection which was removed. */ public void onConnectionRemoved(Connection connection) {} + public void onRemoteConferenceAdded(RemoteConference conference) {} + /** * @hide */ @@ -836,6 +846,11 @@ public abstract class ConnectionService extends Service { return mIdByConference.containsKey(conference); } + /** {@hide} */ + void addRemoteConference(RemoteConference remoteConference) { + onRemoteConferenceAdded(remoteConference); + } + private void onAccountsInitialized() { mAreAccountsInitialized = true; for (Runnable r : mPreInitializationConnectionRequests) { @@ -910,6 +925,17 @@ public abstract class ConnectionService extends Service { return getNullConference(); } + private List<String> createConnectionIdList(List<Connection> connections) { + List<String> ids = new ArrayList<>(); + for (Connection c : connections) { + if (mIdByConnection.containsKey(c)) { + ids.add(mIdByConnection.get(c)); + } + } + Collections.sort(ids); + return ids; + } + private Conference getNullConference() { if (sNullConference == null) { sNullConference = new Conference(null) {}; diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java index a062632..cbcee75 100644 --- a/telecomm/java/android/telecomm/InCallService.java +++ b/telecomm/java/android/telecomm/InCallService.java @@ -163,6 +163,16 @@ public abstract class InCallService extends Service { return new InCallServiceBinder(); } + @Override + public boolean onUnbind(Intent intent) { + Phone oldPhone = mPhone; + mPhone = null; + + oldPhone.destroy(); + onPhoneDestroyed(oldPhone); + return false; + } + /** * Obtain the {@code Phone} associated with this {@code InCallService}. * diff --git a/telecomm/java/android/telecomm/ParcelableCall.java b/telecomm/java/android/telecomm/ParcelableCall.java index 8098b94..a2aa192 100644 --- a/telecomm/java/android/telecomm/ParcelableCall.java +++ b/telecomm/java/android/telecomm/ParcelableCall.java @@ -40,6 +40,7 @@ public final class ParcelableCall implements Parcelable { private final String mDisconnectCauseMsg; private final List<String> mCannedSmsResponses; private final int mCapabilities; + private final int mProperties; private final long mConnectTimeMillis; private final Uri mHandle; private final int mHandlePresentation; @@ -63,6 +64,7 @@ public final class ParcelableCall implements Parcelable { String disconnectCauseMsg, List<String> cannedSmsResponses, int capabilities, + int properties, long connectTimeMillis, Uri handle, int handlePresentation, @@ -83,6 +85,7 @@ public final class ParcelableCall implements Parcelable { mDisconnectCauseMsg = disconnectCauseMsg; mCannedSmsResponses = cannedSmsResponses; mCapabilities = capabilities; + mProperties = properties; mConnectTimeMillis = connectTimeMillis; mHandle = handle; mHandlePresentation = handlePresentation; @@ -137,6 +140,9 @@ public final class ParcelableCall implements Parcelable { return mCapabilities; } + /** Bitmask of properties of the call. */ + public int getProperties() { return mProperties; } + /** The time that the call switched to the active state. */ public long getConnectTimeMillis() { return mConnectTimeMillis; @@ -246,6 +252,7 @@ public final class ParcelableCall implements Parcelable { List<String> cannedSmsResponses = new ArrayList<>(); source.readList(cannedSmsResponses, classLoader); int capabilities = source.readInt(); + int properties = source.readInt(); long connectTimeMillis = source.readLong(); Uri handle = source.readParcelable(classLoader); int handlePresentation = source.readInt(); @@ -264,10 +271,10 @@ public final class ParcelableCall implements Parcelable { source.readList(conferenceableCallIds, classLoader); Bundle extras = source.readParcelable(classLoader); return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg, - cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation, - callerDisplayName, callerDisplayNamePresentation, gatewayInfo, - accountHandle, videoCallProvider, parentCallId, childCallIds, statusHints, - videoState, conferenceableCallIds, extras); + cannedSmsResponses, capabilities, properties, connectTimeMillis, handle, + handlePresentation, callerDisplayName, callerDisplayNamePresentation, + gatewayInfo, accountHandle, videoCallProvider, parentCallId, childCallIds, + statusHints, videoState, conferenceableCallIds, extras); } @Override @@ -291,6 +298,7 @@ public final class ParcelableCall implements Parcelable { destination.writeString(mDisconnectCauseMsg); destination.writeList(mCannedSmsResponses); destination.writeInt(mCapabilities); + destination.writeInt(mProperties); destination.writeLong(mConnectTimeMillis); destination.writeParcelable(mHandle, 0); destination.writeInt(mHandlePresentation); diff --git a/telecomm/java/android/telecomm/ParcelableConnection.java b/telecomm/java/android/telecomm/ParcelableConnection.java index 812ee55..2e21d37 100644 --- a/telecomm/java/android/telecomm/ParcelableConnection.java +++ b/telecomm/java/android/telecomm/ParcelableConnection.java @@ -22,6 +22,9 @@ import android.os.Parcelable; import com.android.internal.telecomm.IVideoProvider; +import java.util.ArrayList; +import java.util.List; + /** * Information about a connection that is used between Telecomm and the ConnectionService. * This is used to send initial Connection information to Telecomm when the connection is @@ -29,20 +32,21 @@ import com.android.internal.telecomm.IVideoProvider; * @hide */ public final class ParcelableConnection implements Parcelable { - private PhoneAccountHandle mPhoneAccount; - private int mState; - private int mCapabilities; - private Uri mHandle; - private int mHandlePresentation; - private String mCallerDisplayName; - private int mCallerDisplayNamePresentation; - private IVideoProvider mVideoProvider; - private int mVideoState; - private boolean mRequestingRingback; - private boolean mAudioModeIsVoip; - private StatusHints mStatusHints; - private int mDisconnectCause; - private String mDisconnectMessage; + private final PhoneAccountHandle mPhoneAccount; + private final int mState; + private final int mCapabilities; + private final Uri mHandle; + private final int mHandlePresentation; + private final String mCallerDisplayName; + private final int mCallerDisplayNamePresentation; + private final IVideoProvider mVideoProvider; + private final int mVideoState; + private final boolean mRequestingRingback; + private final boolean mAudioModeIsVoip; + private final StatusHints mStatusHints; + private final int mDisconnectCause; + private final String mDisconnectMessage; + private final List<String> mConferenceableConnectionIds; /** @hide */ public ParcelableConnection( @@ -59,7 +63,8 @@ public final class ParcelableConnection implements Parcelable { boolean audioModeIsVoip, StatusHints statusHints, int disconnectCause, - String disconnectMessage) { + String disconnectMessage, + List<String> conferenceableConnectionIds) { mPhoneAccount = phoneAccount; mState = state; mCapabilities = capabilities; @@ -74,6 +79,7 @@ public final class ParcelableConnection implements Parcelable { mStatusHints = statusHints; mDisconnectCause = disconnectCause; mDisconnectMessage = disconnectMessage; + this.mConferenceableConnectionIds = conferenceableConnectionIds; } public PhoneAccountHandle getPhoneAccount() { @@ -133,6 +139,10 @@ public final class ParcelableConnection implements Parcelable { return mDisconnectMessage; } + public final List<String> getConferenceableConnectionIds() { + return mConferenceableConnectionIds; + } + @Override public String toString() { return new StringBuilder() @@ -166,6 +176,8 @@ public final class ParcelableConnection implements Parcelable { StatusHints statusHints = source.readParcelable(classLoader); int disconnectCauseCode = source.readInt(); String disconnectCauseMessage = source.readString(); + List<String> conferenceableConnectionIds = new ArrayList<>(); + source.readStringList(conferenceableConnectionIds); return new ParcelableConnection( phoneAccount, @@ -181,7 +193,8 @@ public final class ParcelableConnection implements Parcelable { audioModeIsVoip, statusHints, disconnectCauseCode, - disconnectCauseMessage); + disconnectCauseMessage, + conferenceableConnectionIds); } @Override @@ -214,5 +227,6 @@ public final class ParcelableConnection implements Parcelable { destination.writeParcelable(mStatusHints, 0); destination.writeInt(mDisconnectCause); destination.writeString(mDisconnectMessage); + destination.writeStringList(mConferenceableConnectionIds); } } diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java index e125342..d90d954 100644 --- a/telecomm/java/android/telecomm/Phone.java +++ b/telecomm/java/android/telecomm/Phone.java @@ -20,7 +20,6 @@ import android.annotation.SystemApi; import android.app.PendingIntent; import android.util.ArrayMap; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -83,7 +82,7 @@ public final class Phone { // A List allows us to keep the Calls in a stable iteration order so that casually developed // user interface components do not incur any spurious jank - private final List<Call> mCalls = new ArrayList<>(); + private final List<Call> mCalls = new CopyOnWriteArrayList<>(); // An unmodifiable view of the above List can be safely shared with subclass implementations private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls); @@ -160,6 +159,18 @@ public final class Phone { } /** + * Called to destroy the phone and cleanup any lingering calls. + * @hide + */ + final void destroy() { + for (Call call : mCalls) { + if (call.getState() != Call.STATE_DISCONNECTED) { + call.internalSetDisconnected(); + } + } + } + + /** * Adds a listener to this {@code Phone}. * * @param listener A {@code Listener} object. diff --git a/telecomm/java/android/telecomm/PhoneCapabilities.java b/telecomm/java/android/telecomm/PhoneCapabilities.java index 45168d5..0c6a1ef 100644 --- a/telecomm/java/android/telecomm/PhoneCapabilities.java +++ b/telecomm/java/android/telecomm/PhoneCapabilities.java @@ -19,7 +19,6 @@ package android.telecomm; /** * Defines capabilities a phone call can support, such as conference calling and video telephony. * Also defines properties of a phone call, such as whether it is using VoLTE technology. - */ public final class PhoneCapabilities { /** Call can currently be put on hold or unheld. */ diff --git a/telecomm/java/android/telecomm/RemoteConference.java b/telecomm/java/android/telecomm/RemoteConference.java new file mode 100644 index 0000000..02f6de6 --- /dev/null +++ b/telecomm/java/android/telecomm/RemoteConference.java @@ -0,0 +1,198 @@ +/* + * 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 com.android.internal.telecomm.IConnectionService; + +import android.os.RemoteException; +import android.telephony.DisconnectCause; + +import java.util.Collections; +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 final class RemoteConference { + + public abstract static class Listener { + public void onStateChanged(RemoteConference conference, int oldState, int newState) {} + public void onDisconnected(RemoteConference conference, int cause, String message) {} + public void onConnectionAdded(RemoteConference conference, RemoteConnection connection) {} + public void onConnectionRemoved(RemoteConference conference, RemoteConnection connection) {} + public void onCapabilitiesChanged(RemoteConference conference, int capabilities) {} + public void onDestroyed(RemoteConference conference) {} + } + + private final String mId; + private final IConnectionService mConnectionService; + + private final Set<Listener> mListeners = new CopyOnWriteArraySet<>(); + private final List<RemoteConnection> mChildConnections = new CopyOnWriteArrayList<>(); + private final List<RemoteConnection> mUnmodifiableChildConnections = + Collections.unmodifiableList(mChildConnections); + + private int mState = Connection.STATE_NEW; + private int mDisconnectCause = DisconnectCause.NOT_VALID; + private int mCallCapabilities; + private String mDisconnectMessage; + + /** {@hide} */ + RemoteConference(String id, IConnectionService connectionService) { + mId = id; + mConnectionService = connectionService; + } + + /** {@hide} */ + String getId() { + return mId; + } + + /** {@hide} */ + void setDestroyed() { + for (RemoteConnection connection : mChildConnections) { + connection.setConference(null); + } + for (Listener l : mListeners) { + l.onDestroyed(this); + } + } + + /** {@hide} */ + 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); + } + } + } + + /** {@hide} */ + void addConnection(RemoteConnection connection) { + if (!mChildConnections.contains(connection)) { + mChildConnections.add(connection); + connection.setConference(this); + for (Listener l : mListeners) { + l.onConnectionAdded(this, connection); + } + } + } + + /** {@hide} */ + void removeConnection(RemoteConnection connection) { + if (mChildConnections.contains(connection)) { + mChildConnections.remove(connection); + connection.setConference(null); + for (Listener l : mListeners) { + l.onConnectionRemoved(this, connection); + } + } + } + + /** {@hide} */ + void setCallCapabilities(int capabilities) { + if (mCallCapabilities != capabilities) { + mCallCapabilities = capabilities; + for (Listener l : mListeners) { + l.onCapabilitiesChanged(this, mCallCapabilities); + } + } + } + + /** {@hide} */ + void setDisconnected(int cause, String message) { + if (mState != Connection.STATE_DISCONNECTED) { + mDisconnectCause = cause; + mDisconnectMessage = message; + setState(Connection.STATE_DISCONNECTED); + for (Listener l : mListeners) { + l.onDisconnected(this, cause, message); + } + } + } + + public final List<RemoteConnection> getConnections() { + return mUnmodifiableChildConnections; + } + + public final int getState() { + return mState; + } + + public final int getCallCapabilities() { + return mCallCapabilities; + } + + public void disconnect() { + try { + mConnectionService.disconnect(mId); + } catch (RemoteException e) { + } + } + + public void separate(RemoteConnection connection) { + if (mChildConnections.contains(connection)) { + try { + mConnectionService.splitFromConference(connection.getId()); + } catch (RemoteException e) { + } + } + } + + public void hold() { + try { + mConnectionService.hold(mId); + } catch (RemoteException e) { + } + } + + public void unhold() { + try { + mConnectionService.unhold(mId); + } catch (RemoteException e) { + } + } + + public int getDisconnectCause() { + return mDisconnectCause; + } + + public String getDisconnectMessage() { + return mDisconnectMessage; + } + + public final void addListener(Listener listener) { + mListeners.add(listener); + } + + public final void removeListener(Listener listener) { + mListeners.remove(listener); + } +} diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java index 70db6f5..8c86b15 100644 --- a/telecomm/java/android/telecomm/RemoteConnection.java +++ b/telecomm/java/android/telecomm/RemoteConnection.java @@ -179,8 +179,30 @@ public final class RemoteConnection { * @param connection The {@code RemoteConnection} invoking this method. */ public void onDestroyed(RemoteConnection connection) {} + + /** + * Indicates that the {@code RemoteConnection}s with which this {@code RemoteConnection} + * may be asked to create a conference has changed. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param conferenceableConnections The {@code RemoteConnection}s with which this + * {@code RemoteConnection} may be asked to create a conference. + */ public void onConferenceableConnectionsChanged( - RemoteConnection connection, List<RemoteConnection> conferenceableConnections) {} + RemoteConnection connection, + List<RemoteConnection> conferenceableConnections) {} + + /** + * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part + * of has changed. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param conference The {@code RemoteConference} of which this {@code RemoteConnection} is + * a part, which may be {@code null}. + */ + public void onConferenceChanged( + RemoteConnection connection, + RemoteConference conference) {} } private IConnectionService mConnectionService; @@ -192,7 +214,9 @@ public final class RemoteConnection { */ private final Set<Listener> mListeners = Collections.newSetFromMap( new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1)); - private final Set<RemoteConnection> mConferenceableConnections = new HashSet<>(); + private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>(); + private final List<RemoteConnection> mUnmodifiableconferenceableConnections = + Collections.unmodifiableList(mConferenceableConnections); private int mState = Connection.STATE_NEW; private int mDisconnectCauseCode = DisconnectCause.NOT_VALID; @@ -209,6 +233,7 @@ public final class RemoteConnection { private int mCallerDisplayNamePresentation; private int mFailureCode; private String mFailureMessage; + private RemoteConference mConference; /** * @hide @@ -273,7 +298,7 @@ public final class RemoteConnection { * @return The children of this {@code RemoteConnection} if this {@code RemoteConnection} is * a conference, or an empty {@code List} otherwise. */ - public List<RemoteConnection> getChildren() { return null; } + public List<RemoteConnection> getChildren() { return new ArrayList<>(); } /** * Obtains the state of this {@code RemoteConnection}. @@ -539,6 +564,37 @@ public final class RemoteConnection { } /** + * Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be + * successfully asked to create a conference with. + * + * @return The {@code RemoteConnection}s with which this {@code RemoteConnection} may be + * merged into a {@link RemoteConference}. + */ + public List<RemoteConnection> getConferenceableConnections() { + return mUnmodifiableconferenceableConnections; + } + + /** + * Obtain the {@code RemoteConference} that this {@code RemoteConnection} may be a part + * of, or {@code null} if there is no such {@code RemoteConference}. + * + * @return A {@code RemoteConference} or {@code null}; + */ + public RemoteConference getConference() { + return mConference; + } + + /** {@hide} */ + String getId() { + return mConnectionId; + } + + /** {@hide} */ + IConnectionService getConnectionService() { + return mConnectionService; + } + + /** * @hide */ void setState(int state) { @@ -671,8 +727,17 @@ public final class RemoteConnection { mConferenceableConnections.clear(); mConferenceableConnections.addAll(conferenceableConnections); for (Listener l : mListeners) { - l.onConferenceableConnectionsChanged( - this, new ArrayList<RemoteConnection>(mConferenceableConnections)); + l.onConferenceableConnectionsChanged(this, mUnmodifiableconferenceableConnections); + } + } + + /** @hide */ + void setConference(RemoteConference conference) { + if (mConference != conference) { + mConference = conference; + for (Listener l : mListeners) { + l.onConferenceChanged(this, conference); + } } } diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java index 365ed5b..83502c5 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionManager.java +++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java @@ -28,13 +28,22 @@ import java.util.Map; * @hide */ public class RemoteConnectionManager { - private Map<ComponentName, RemoteConnectionService> mRemoteConnectionServices = new HashMap<>(); + private final Map<ComponentName, RemoteConnectionService> mRemoteConnectionServices = + new HashMap<>(); + private final ConnectionService mOurConnectionServiceImpl; - void addConnectionService(ComponentName componentName, IConnectionService connectionService) { + public RemoteConnectionManager(ConnectionService ourConnectionServiceImpl) { + mOurConnectionServiceImpl = ourConnectionServiceImpl; + } + + void addConnectionService( + ComponentName componentName, + IConnectionService outgoingConnectionServiceRpc) { if (!mRemoteConnectionServices.containsKey(componentName)) { try { - RemoteConnectionService remoteConnectionService = - new RemoteConnectionService(connectionService); + RemoteConnectionService remoteConnectionService = new RemoteConnectionService( + outgoingConnectionServiceRpc, + mOurConnectionServiceImpl); mRemoteConnectionServices.put(componentName, remoteConnectionService); } catch (RemoteException ignored) { } @@ -63,4 +72,17 @@ public class RemoteConnectionManager { } return null; } + + public void conferenceRemoteConnections(RemoteConnection a, RemoteConnection b) { + if (a.getConnectionService() == b.getConnectionService()) { + try { + a.getConnectionService().conference(a.getId(), b.getId()); + } catch (RemoteException e) { + } + } else { + Log.w(this, "Request to conference incompatible remote connections (%s,%s) (%s,%s)", + a.getConnectionService(), a.getId(), + b.getConnectionService(), b.getId()); + } + } } diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java index 541df4e..51722fe 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionService.java +++ b/telecomm/java/android/telecomm/RemoteConnectionService.java @@ -28,11 +28,11 @@ import com.android.internal.telecomm.IConnectionServiceAdapter; import com.android.internal.telecomm.IVideoProvider; import com.android.internal.telecomm.RemoteServiceCallback; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.Collections; import java.util.List; import java.util.UUID; @@ -43,8 +43,11 @@ import java.util.UUID; */ final class RemoteConnectionService { - private static final RemoteConnection - NULL_CONNECTION = new RemoteConnection("NULL", null, null); + private static final RemoteConnection NULL_CONNECTION = + new RemoteConnection("NULL", null, null); + + private static final RemoteConference NULL_CONFERENCE = + new RemoteConference("NULL", null); private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() { @Override @@ -64,6 +67,13 @@ final class RemoteConnectionService { connection.setCallerDisplayName( parcel.getCallerDisplayName(), parcel.getCallerDisplayNamePresentation()); + List<RemoteConnection> conferenceable = new ArrayList<>(); + for (String confId : parcel.getConferenceableConnectionIds()) { + if (mConnectionById.containsKey(confId)) { + conferenceable.add(mConnectionById.get(confId)); + } + } + connection.setConferenceableConnections(conferenceable); // TODO: Do we need to support video providers for remote connections? if (connection.getState() == Connection.STATE_DISCONNECTED) { // ... then, if it was created in a disconnected state, that indicates @@ -75,8 +85,13 @@ final class RemoteConnectionService { @Override public void setActive(String callId) { - findConnectionForAction(callId, "setActive") - .setState(Connection.STATE_ACTIVE); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setActive") + .setState(Connection.STATE_ACTIVE); + } else { + findConferenceForAction(callId, "setActive") + .setState(Connection.STATE_ACTIVE); + } } @Override @@ -94,14 +109,24 @@ final class RemoteConnectionService { @Override public void setDisconnected(String callId, int disconnectCause, String disconnectMessage) { - findConnectionForAction(callId, "setDisconnected") - .setDisconnected(disconnectCause, disconnectMessage); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setDisconnected") + .setDisconnected(disconnectCause, disconnectMessage); + } else { + findConferenceForAction(callId, "setDisconnected") + .setDisconnected(disconnectCause, disconnectMessage); + } } @Override public void setOnHold(String callId) { - findConnectionForAction(callId, "setOnHold") - .setState(Connection.STATE_HOLDING); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setOnHold") + .setState(Connection.STATE_HOLDING); + } else { + findConferenceForAction(callId, "setOnHold") + .setState(Connection.STATE_HOLDING); + } } @Override @@ -112,24 +137,80 @@ final class RemoteConnectionService { @Override public void setCallCapabilities(String callId, int callCapabilities) { - findConnectionForAction("callId", "setCallCapabilities") - .setCallCapabilities(callCapabilities); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setCallCapabilities") + .setCallCapabilities(callCapabilities); + } else { + findConferenceForAction(callId, "setCallCapabilities") + .setCallCapabilities(callCapabilities); + } } @Override public void setIsConferenced(String callId, String conferenceCallId) { - // not supported for remote connections. + // Note: callId should not be null; conferenceCallId may be null + RemoteConnection connection = + findConnectionForAction(callId, "setIsConferenced"); + if (connection != NULL_CONNECTION) { + if (conferenceCallId == null) { + // 'connection' is being split from its conference + if (connection.getConference() != null) { + connection.getConference().removeConnection(connection); + } + } else { + RemoteConference conference = + findConferenceForAction(conferenceCallId, "setIsConferenced"); + if (conference != NULL_CONFERENCE) { + conference.addConnection(connection); + } + } + } } @Override - public void addConferenceCall(String callId, ParcelableConference parcelableConference) { - // not supported for remote connections. + public void addConferenceCall( + final String callId, + ParcelableConference parcel) { + RemoteConference conference = new RemoteConference(callId, + mOutgoingConnectionServiceRpc); + + for (String id : parcel.getConnectionIds()) { + RemoteConnection c = mConnectionById.get(id); + if (c != null) { + conference.addConnection(c); + } + } + + if (conference.getConnections().size() == 0) { + // A conference was created, but none of its connections are ones that have been + // created by, and therefore being tracked by, this remote connection service. It + // is of no interest to us. + return; + } + + conference.setState(parcel.getState()); + conference.setCallCapabilities(parcel.getCapabilities()); + mConferenceById.put(callId, conference); + conference.addListener(new RemoteConference.Listener() { + @Override + public void onDestroyed(RemoteConference c) { + mConferenceById.remove(callId); + maybeDisconnectAdapter(); + } + }); + + mOurConnectionServiceImpl.addRemoteConference(conference); } @Override public void removeCall(String callId) { - findConnectionForAction(callId, "removeCall") - .setDestroyed(); + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "removeCall") + .setDestroyed(); + } else { + findConferenceForAction(callId, "removeCall") + .setDestroyed(); + } } @Override @@ -193,13 +274,15 @@ final class RemoteConnectionService { @Override public final void setConferenceableConnections( String callId, List<String> conferenceableConnectionIds) { + List<RemoteConnection> conferenceable = new ArrayList<>(); + for (String id : conferenceableConnectionIds) { + if (mConnectionById.containsKey(id)) { + conferenceable.add(mConnectionById.get(id)); + } + } - // TODO: When we support more than 1 remote connection, this should - // loop through the incoming list of connection IDs and acquire the list - // of remote connections which correspond to the IDs. That list should - // be set onto the remote connections. findConnectionForAction(callId, "setConferenceableConnections") - .setConferenceableConnections(Collections.<RemoteConnection>emptyList()); + .setConferenceableConnections(conferenceable); } }; @@ -212,24 +295,33 @@ final class RemoteConnectionService { for (RemoteConnection c : mConnectionById.values()) { c.setDestroyed(); } + for (RemoteConference c : mConferenceById.values()) { + c.setDestroyed(); + } mConnectionById.clear(); + mConferenceById.clear(); mPendingConnections.clear(); - mConnectionService.asBinder().unlinkToDeath(mDeathRecipient, 0); + mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0); } }; - private final IConnectionService mConnectionService; + private final IConnectionService mOutgoingConnectionServiceRpc; + private final ConnectionService mOurConnectionServiceImpl; private final Map<String, RemoteConnection> mConnectionById = new HashMap<>(); + private final Map<String, RemoteConference> mConferenceById = new HashMap<>(); private final Set<RemoteConnection> mPendingConnections = new HashSet<>(); - RemoteConnectionService(IConnectionService connectionService) throws RemoteException { - mConnectionService = connectionService; - mConnectionService.asBinder().linkToDeath(mDeathRecipient, 0); + RemoteConnectionService( + IConnectionService outgoingConnectionServiceRpc, + ConnectionService ourConnectionServiceImpl) throws RemoteException { + mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc; + mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0); + mOurConnectionServiceImpl = ourConnectionServiceImpl; } @Override public String toString() { - return "[RemoteCS - " + mConnectionService.asBinder().toString() + "]"; + return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]"; } final RemoteConnection createRemoteConnection( @@ -245,13 +337,13 @@ final class RemoteConnectionService { request.getVideoState()); try { if (mConnectionById.isEmpty()) { - mConnectionService.addConnectionServiceAdapter(mServant.getStub()); + mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub()); } RemoteConnection connection = - new RemoteConnection(id, mConnectionService, newRequest); + new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest); mPendingConnections.add(connection); mConnectionById.put(id, connection); - mConnectionService.createConnection( + mOutgoingConnectionServiceRpc.createConnection( connectionManagerPhoneAccount, id, newRequest, @@ -260,12 +352,7 @@ final class RemoteConnectionService { @Override public void onDestroyed(RemoteConnection connection) { mConnectionById.remove(id); - if (mConnectionById.isEmpty()) { - try { - mConnectionService.removeConnectionServiceAdapter(mServant.getStub()); - } catch (RemoteException e) { - } - } + maybeDisconnectAdapter(); } }); return connection; @@ -283,4 +370,22 @@ final class RemoteConnectionService { Log.w(this, "%s - Cannot find Connection %s", action, callId); return NULL_CONNECTION; } + + private RemoteConference findConferenceForAction( + String callId, String action) { + if (mConferenceById.containsKey(callId)) { + return mConferenceById.get(callId); + } + Log.w(this, "%s - Cannot find Conference %s", action, callId); + return NULL_CONFERENCE; + } + + private void maybeDisconnectAdapter() { + if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) { + try { + mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub()); + } catch (RemoteException e) { + } + } + } } diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java index e59fea1..868282f 100644 --- a/telecomm/java/android/telecomm/TelecommManager.java +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -17,7 +17,6 @@ package android.telecomm; import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; - import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -25,6 +24,7 @@ import android.util.Log; import com.android.internal.telecomm.ITelecommService; +import java.util.ArrayList; import java.util.List; /** @@ -299,7 +299,7 @@ public class TelecommManager { } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecommService#getOutgoingPhoneAccounts", e); } - return null; + return new ArrayList<>(); } /** @@ -394,14 +394,15 @@ public class TelecommManager { /** * Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding * states). - * - * @hide + * <p> + * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE} + * </p> */ @SystemApi - public boolean isInAPhoneCall() { + public boolean isInCall() { try { if (isServiceConnected()) { - return getTelecommService().isInAPhoneCall(); + return getTelecommService().isInCall(); } } catch (RemoteException e) { Log.e(TAG, "RemoteException attempting to get default phone app.", e); @@ -541,6 +542,67 @@ public class TelecommManager { } } + /** + * Processes the specified dial string as an MMI code. + * MMI codes are any sequence of characters entered into the dialpad that contain a "*" or "#". + * Some of these sequences launch special behavior through handled by Telephony. + * <p> + * Requires that the method-caller be set as the system dialer app. + * </p> + * + * @param dialString The digits to dial. + * @return True if the digits were processed as an MMI code, false otherwise. + */ + public boolean handleMmi(String dialString) { + ITelecommService service = getTelecommService(); + if (service != null) { + try { + return service.handlePinMmi(dialString); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#handlePinMmi", e); + } + } + return false; + } + + /** + * Removes the missed-call notification if one is present. + * <p> + * Requires that the method-caller be set as the system dialer app. + * </p> + */ + public void cancelMissedCallsNotification() { + ITelecommService service = getTelecommService(); + if (service != null) { + try { + service.cancelMissedCallsNotification(); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#cancelMissedCallsNotification", e); + } + } + } + + /** + * Brings the in-call screen to the foreground if there is an ongoing call. If there is + * currently no ongoing call, then this method does nothing. + * <p> + * Requires that the method-caller be set as the system dialer app or have the + * {@link android.Manifest.permission#READ_PHONE_STATE} permission. + * </p> + * + * @param showDialpad Brings up the in-call dialpad as part of showing the in-call screen. + */ + public void showInCallScreen(boolean showDialpad) { + ITelecommService service = getTelecommService(); + if (service != null) { + try { + service.showInCallScreen(showDialpad); + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecommService#showCallScreen", e); + } + } + } + private ITelecommService getTelecommService() { return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME)); } diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl index 3c1dea6..0ac5078 100644 --- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl @@ -32,7 +32,7 @@ interface ITelecommService { * * @param showDialpad if true, make the dialpad visible initially. */ - void showCallScreen(boolean showDialpad); + void showInCallScreen(boolean showDialpad); /** * @see TelecommManager#getDefaultOutgoingPhoneAccount @@ -79,9 +79,9 @@ interface ITelecommService { void silenceRinger(); /** - * @see TelecommManager#isInAPhoneCall + * @see TelecommManager#isInCall */ - boolean isInAPhoneCall(); + boolean isInCall(); /** * @see TelecomManager#isRinging @@ -99,12 +99,12 @@ interface ITelecommService { void acceptRingingCall(); /** - * @see PhoneManager#cancelMissedCallsNotification + * @see TelecommManager#cancelMissedCallsNotification */ void cancelMissedCallsNotification(); /** - * @see PhoneManager#handlePinMmi + * @see TelecommManager#handleMmi */ boolean handlePinMmi(String dialString); |
