Loading telecomm/java/android/telecomm/CallCapabilities.java +43 −7 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package android.telecomm; /** Defines actions a call currently supports. */ public class CallCapabilities { public final class CallCapabilities { /** Call can currently be put on hold or unheld. */ public static final int HOLD = 0x00000001; Loading @@ -27,24 +27,60 @@ public class CallCapabilities { /** Call can currently be merged. */ public static final int MERGE_CALLS = 0x00000004; /* Call can currently be swapped with another call. */ /** Call can currently be swapped with another call. */ public static final int SWAP_CALLS = 0x00000008; /* Call currently supports adding another call to this one. */ /** Call currently supports adding another call to this one. */ public static final int ADD_CALL = 0x00000010; /* Call supports responding via text option. */ /** Call supports responding via text option. */ public static final int RESPOND_VIA_TEXT = 0x00000020; /* Call can be muted. */ /** Call can be muted. */ public static final int MUTE = 0x00000040; /* Call supports generic conference mode. */ /** Call supports generic conference mode. */ public static final int GENERIC_CONFERENCE = 0x00000080; /* Call currently supports switch between connections. */ /** Call currently supports switch between connections. */ public static final int CONNECTION_HANDOFF = 0x00000100; public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF; public static String toString(int capabilities) { StringBuilder builder = new StringBuilder(); builder.append("[Capabilities:"); if ((capabilities & HOLD) != 0) { builder.append(" HOLD"); } if ((capabilities & SUPPORT_HOLD) != 0) { builder.append(" SUPPORT_HOLD"); } if ((capabilities & MERGE_CALLS) != 0) { builder.append(" MERGE_CALLS"); } if ((capabilities & SWAP_CALLS) != 0) { builder.append(" SWAP_CALLS"); } if ((capabilities & ADD_CALL) != 0) { builder.append(" ADD_CALL"); } if ((capabilities & RESPOND_VIA_TEXT) != 0) { builder.append(" RESPOND_VIA_TEXT"); } if ((capabilities & MUTE) != 0) { builder.append(" MUTE"); } if ((capabilities & GENERIC_CONFERENCE) != 0) { builder.append(" GENERIC_CONFERENCE"); } if ((capabilities & CONNECTION_HANDOFF) != 0) { builder.append(" HANDOFF"); } builder.append("]"); return builder.toString(); } private CallCapabilities() {} } telecomm/java/android/telecomm/CallService.java +16 −30 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ public abstract class CallService extends Service { private static final int MSG_ON_AUDIO_STATE_CHANGED = 11; private static final int MSG_PLAY_DTMF_TONE = 12; private static final int MSG_STOP_DTMF_TONE = 13; private static final int MSG_ADD_TO_CONFERENCE = 14; private static final int MSG_CONFERENCE = 14; private static final int MSG_SPLIT_FROM_CONFERENCE = 15; private static final int MSG_ON_POST_DIAL_CONTINUE = 16; Loading Loading @@ -128,24 +128,12 @@ public abstract class CallService extends Service { case MSG_STOP_DTMF_TONE: stopDtmfTone((String) msg.obj); break; case MSG_ADD_TO_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { @SuppressWarnings("unchecked") List<String> callIds = (List<String>) args.arg2; String conferenceCallId = (String) args.arg1; addToConference(conferenceCallId, callIds); } finally { args.recycle(); } break; } case MSG_SPLIT_FROM_CONFERENCE: { case MSG_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { String conferenceCallId = (String) args.arg1; String callId = (String) args.arg2; splitFromConference(conferenceCallId, callId); conference(conferenceCallId, callId); } finally { args.recycle(); } Loading @@ -162,6 +150,9 @@ public abstract class CallService extends Service { } break; } case MSG_SPLIT_FROM_CONFERENCE: splitFromConference((String) msg.obj); break; default: break; } Loading Loading @@ -245,19 +236,16 @@ public abstract class CallService extends Service { } @Override public void addToConference(String conferenceCallId, List<String> callsToConference) { public void conference(String conferenceCallId, String callId) { SomeArgs args = SomeArgs.obtain(); args.arg1 = conferenceCallId; args.arg2 = callsToConference; mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget(); args.arg2 = callId; mMessageHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); } @Override public void splitFromConference(String conferenceCallId, String callId) { SomeArgs args = SomeArgs.obtain(); args.arg1 = conferenceCallId; args.arg2 = callId; mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget(); public void splitFromConference(String callId) { mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget(); } @Override Loading Loading @@ -424,24 +412,22 @@ public abstract class CallService extends Service { public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); /** * Adds the specified calls to the specified conference call. * Conferences the specified call. * * @param conferenceCallId The unique ID of the conference call onto which the specified calls * should be added. * @param callIds The calls to add to the conference call. * @param callId The call to conference. * @hide */ public abstract void addToConference(String conferenceCallId, List<String> callIds); public abstract void conference(String conferenceCallId, String callId); /** * Removes the specified call from the specified conference call. This is a no-op if the call * is not already part of the conference call. * Removes the specified call from a conference call. * * @param conferenceCallId The conference call. * @param callId The call to remove from the conference call * @hide */ public abstract void splitFromConference(String conferenceCallId, String callId); public abstract void splitFromConference(String callId); public void onPostDialContinue(String callId, boolean proceed) {} public void onPostDialWait(Connection conn, String remaining) {} Loading telecomm/java/android/telecomm/CallServiceAdapter.java +19 −6 Original line number Diff line number Diff line Loading @@ -179,12 +179,12 @@ public final class CallServiceAdapter { * Indicates that the specified call can conference with any of the specified list of calls. * * @param callId The unique ID of the call. * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced. * @param canConference Specified whether or not the call can be conferenced. * @hide */ public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) { public void setCanConference(String callId, boolean canConference) { try { mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds); mAdapter.setCanConference(callId, canConference); } catch (RemoteException ignored) { } } Loading @@ -193,13 +193,14 @@ public final class CallServiceAdapter { * Indicates whether or not the specified call is currently conferenced into the specified * conference call. * * @param conferenceCallId The unique ID of the conference call. * @param callId The unique ID of the call being conferenced. * @param conferenceCallId The unique ID of the conference call. Null if call is not * conferenced. * @hide */ public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) { public void setIsConferenced(String callId, String conferenceCallId) { try { mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced); mAdapter.setIsConferenced(callId, conferenceCallId); } catch (RemoteException ignored) { } } Loading @@ -224,4 +225,16 @@ public final class CallServiceAdapter { } catch (RemoteException ignored) { } } /** * Indicates that a new conference call has been created. * * @param callId The unique ID of the conference call. */ public void addConferenceCall(String callId) { try { mAdapter.addConferenceCall(callId, null); } catch (RemoteException ignored) { } } } telecomm/java/android/telecomm/Connection.java +121 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,10 @@ package android.telecomm; import android.net.Uri; import android.os.Bundle; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** Loading @@ -35,6 +38,8 @@ public abstract class Connection { void onDisconnected(Connection c, int cause, String message); void onRequestingRingback(Connection c, boolean ringback); void onDestroyed(Connection c); void onConferenceCapableChanged(Connection c, boolean isConferenceCapable); void onParentConnectionChanged(Connection c, Connection parent); } public static class ListenerBase implements Listener { Loading Loading @@ -65,6 +70,14 @@ public abstract class Connection { /** {@inheritDoc} */ @Override public void onRequestingRingback(Connection c, boolean ringback) {} /** ${inheritDoc} */ @Override public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {} /** ${inheritDoc} */ @Override public void onParentConnectionChanged(Connection c, Connection parent) {} } public final class State { Loading @@ -79,10 +92,14 @@ public abstract class Connection { } private final Set<Listener> mListeners = new HashSet<>(); private final List<Connection> mChildConnections = new ArrayList<>(); private int mState = State.NEW; private CallAudioState mCallAudioState; private Uri mHandle; private boolean mRequestingRingback = false; private boolean mIsConferenceCapable = false; private Connection mParentConnection; /** * Create a new Connection. Loading Loading @@ -175,6 +192,16 @@ public abstract class Connection { onDisconnect(); } /** * Separates this Connection from a parent connection. * * @hide */ public final void separate() { Log.d(this, "separate"); onSeparate(); } /** * Abort this Connection. The Connection will immediately transition to * the {@link State#DISCONNECTED} state, and send no notifications of this Loading Loading @@ -239,6 +266,14 @@ public abstract class Connection { } } /** * TODO(santoscordon): Needs updated documentation. */ public final void conference() { Log.d(this, "conference"); onConference(); } /** * Inform this Connection that the state of its audio output has been changed externally. * Loading Loading @@ -274,13 +309,45 @@ public abstract class Connection { } /** * @return Whether this connection is requesting that the system play a ringback tone * Returns whether this connection is requesting that the system play a ringback tone * on its behalf. */ public boolean isRequestingRingback() { return mRequestingRingback; } /** * Returns whether this connection is a conference connection (has child connections). */ public boolean isConferenceConnection() { return !mChildConnections.isEmpty(); } public 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 Connection getParentConnection() { return mParentConnection; } public List<Connection> getChildConnections() { return mChildConnections; } /** * Sets the value of the {@link #getHandle()} property and notifies listeners. * Loading Loading @@ -358,6 +425,32 @@ public abstract class Connection { } } /** * TODO(santoscordon): Needs documentation. */ protected void setIsConferenceCapable(boolean isConferenceCapable) { if (mIsConferenceCapable != isConferenceCapable) { mIsConferenceCapable = isConferenceCapable; for (Listener l : mListeners) { l.onConferenceCapableChanged(this, mIsConferenceCapable); } } } /** * TODO(santoscordon): Needs documentation. */ protected void setDestroyed() { // It is possible that onDestroy() will trigger the listener to remove itself which will // result in a concurrent modification exception. To counteract this we make a copy of the // listeners and iterate on that. for (Listener l : new ArrayList<>(mListeners)) { if (mListeners.contains(l)) { l.onDestroyed(this); } } } /** * Notifies this Connection and listeners that the {@link #getCallAudioState()} property * has a new value. Loading Loading @@ -417,6 +510,11 @@ public abstract class Connection { */ protected void onDisconnect() {} /** * Notifies this Connection of a request to disconnect. */ protected void onSeparate() {} /** * Notifies this Connection of a request to abort. */ Loading Loading @@ -449,6 +547,28 @@ public abstract class Connection { */ protected void onPostDialContinue(boolean proceed) {} /** * TODO(santoscordon): Needs documentation. */ protected void onConference() {} /** * TODO(santoscordon): Needs documentation. */ protected void onChildrenChanged(List<Connection> children) {} 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) { Log.d(this, "setState: %s", stateToString(state)); onSetState(state); Loading telecomm/java/android/telecomm/ConnectionService.java +92 −21 Original line number Diff line number Diff line Loading @@ -20,10 +20,15 @@ import android.net.Uri; import android.os.Bundle; import android.telephony.DisconnectCause; import android.os.SystemClock; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * A {@link android.app.Service} that provides telephone connections to Loading @@ -32,7 +37,6 @@ import java.util.Map; public abstract class ConnectionService extends CallService { // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); private static final Connection NULL_CONNECTION = new Connection() {}; // Mappings from Connections to IDs as understood by the current CallService implementation Loading Loading @@ -99,6 +103,20 @@ public abstract class ConnectionService extends CallService { Log.d(this, "Adapter onRingback %b", ringback); getAdapter().setRequestingRingback(id, ringback); } @Override public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) { String id = mIdByConnection.get(c); getAdapter().setCanConference(id, isConferenceCapable); } /** ${inheritDoc} */ @Override public void onParentConnectionChanged(Connection c, Connection parent) { String id = mIdByConnection.get(c); String parentId = parent == null ? null : mIdByConnection.get(parent); getAdapter().setIsConferenced(id, parentId); } }; @Override Loading @@ -110,8 +128,7 @@ public abstract class ConnectionService extends CallService { @Override public void onResult(Uri handle, Subscription... result) { boolean isCompatible = result.length > 0; Log.d(this, "adapter setIsCompatibleWith " + callInfo.getId() + " " + isCompatible); Log.d(this, "adapter setIsCompatibleWith "); getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible); } Loading @@ -135,7 +152,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { if (result.length != 1) { if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo); getAdapter().handleFailedOutgoingCall( request, Loading @@ -145,10 +162,10 @@ public abstract class ConnectionService extends CallService { c.abort(); } } else { addConnection(callInfo.getId(), result[0]); Log.d(this, "adapter handleSuccessfulOutgoingCall %s", callInfo.getId()); getAdapter().handleSuccessfulOutgoingCall(callInfo.getId()); addConnection(callInfo.getId(), result[0]); } } Loading Loading @@ -177,7 +194,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { if (result.length != 1) { if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callId); getAdapter().handleFailedOutgoingCall( request, Loading Loading @@ -258,27 +275,43 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override public final void addToConference(String conferenceCallId, List<String> callIds) { Log.d(this, "addToConference %s, %s", conferenceCallId, callIds); public final void conference(final String conferenceCallId, String callId) { Log.d(this, "conference %s, %s", conferenceCallId, callId); List<Connection> connections = new LinkedList<>(); for (String id : callIds) { Connection connection = findConnectionForAction(id, "addToConference"); Connection connection = findConnectionForAction(callId, "conference"); if (connection == NULL_CONNECTION) { Log.w(this, "Connection missing in conference request %s.", id); Log.w(this, "Connection missing in conference request %s.", callId); return; } connections.add(connection); 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); getAdapter().addConferenceCall(conferenceCallId); addConnection(conferenceCallId, conferenceConnection); } } } // TODO(santoscordon): Find an existing conference call or create a new one. Then call // conferenceWith on it. /** ${inheritDoc} */ @Override public void onError(String request, int code, String reason) { // no-op } }); } /** @hide */ @Override public final void splitFromConference(String conferenceCallId, String callId) { Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId); public final void splitFromConference(String callId) { Log.d(this, "splitFromConference(%s)", callId); Connection connection = findConnectionForAction(callId, "splitFromConference"); if (connection == NULL_CONNECTION) { Loading Loading @@ -308,6 +341,13 @@ public abstract class ConnectionService extends CallService { getAdapter().onPostDialWait(mIdByConnection.get(conn), remaining); } /** * Returns all connections currently associated with this connection service. */ public Collection<Connection> getAllConnections() { return mConnectionById.values(); } /** * Find a set of Subscriptions matching a given handle (e.g. phone number). * Loading @@ -328,6 +368,21 @@ public abstract class ConnectionService extends CallService { ConnectionRequest request, Response<ConnectionRequest, Connection> callback) {} /** * 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. * * @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. */ public void onCreateConferenceConnection( String token, Connection connection, Response<String, Connection> callback) {} /** * Create a Connection to match an incoming connection notification. * Loading @@ -338,6 +393,20 @@ public abstract class ConnectionService extends CallService { ConnectionRequest request, Response<ConnectionRequest, Connection> callback) {} /** * Notifies that a connection has been added to this connection service and sent to Telecomm. * * @param connection The connection which was added. */ public void onConnectionAdded(Connection connection) {} /** * Notified that a connection has been removed from this connection service. * * @param connection The connection which was removed. */ public void onConnectionRemoved(Connection connection) {} static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { Loading Loading @@ -387,12 +456,14 @@ public abstract class ConnectionService extends CallService { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); connection.addConnectionListener(mConnectionListener); onConnectionAdded(connection); } private void removeConnection(Connection connection) { connection.removeConnectionListener(mConnectionListener); mConnectionById.remove(mIdByConnection.get(connection)); mIdByConnection.remove(connection); onConnectionRemoved(connection); } private Connection findConnectionForAction(String callId, String action) { Loading Loading
telecomm/java/android/telecomm/CallCapabilities.java +43 −7 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ package android.telecomm; /** Defines actions a call currently supports. */ public class CallCapabilities { public final class CallCapabilities { /** Call can currently be put on hold or unheld. */ public static final int HOLD = 0x00000001; Loading @@ -27,24 +27,60 @@ public class CallCapabilities { /** Call can currently be merged. */ public static final int MERGE_CALLS = 0x00000004; /* Call can currently be swapped with another call. */ /** Call can currently be swapped with another call. */ public static final int SWAP_CALLS = 0x00000008; /* Call currently supports adding another call to this one. */ /** Call currently supports adding another call to this one. */ public static final int ADD_CALL = 0x00000010; /* Call supports responding via text option. */ /** Call supports responding via text option. */ public static final int RESPOND_VIA_TEXT = 0x00000020; /* Call can be muted. */ /** Call can be muted. */ public static final int MUTE = 0x00000040; /* Call supports generic conference mode. */ /** Call supports generic conference mode. */ public static final int GENERIC_CONFERENCE = 0x00000080; /* Call currently supports switch between connections. */ /** Call currently supports switch between connections. */ public static final int CONNECTION_HANDOFF = 0x00000100; public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF; public static String toString(int capabilities) { StringBuilder builder = new StringBuilder(); builder.append("[Capabilities:"); if ((capabilities & HOLD) != 0) { builder.append(" HOLD"); } if ((capabilities & SUPPORT_HOLD) != 0) { builder.append(" SUPPORT_HOLD"); } if ((capabilities & MERGE_CALLS) != 0) { builder.append(" MERGE_CALLS"); } if ((capabilities & SWAP_CALLS) != 0) { builder.append(" SWAP_CALLS"); } if ((capabilities & ADD_CALL) != 0) { builder.append(" ADD_CALL"); } if ((capabilities & RESPOND_VIA_TEXT) != 0) { builder.append(" RESPOND_VIA_TEXT"); } if ((capabilities & MUTE) != 0) { builder.append(" MUTE"); } if ((capabilities & GENERIC_CONFERENCE) != 0) { builder.append(" GENERIC_CONFERENCE"); } if ((capabilities & CONNECTION_HANDOFF) != 0) { builder.append(" HANDOFF"); } builder.append("]"); return builder.toString(); } private CallCapabilities() {} }
telecomm/java/android/telecomm/CallService.java +16 −30 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ public abstract class CallService extends Service { private static final int MSG_ON_AUDIO_STATE_CHANGED = 11; private static final int MSG_PLAY_DTMF_TONE = 12; private static final int MSG_STOP_DTMF_TONE = 13; private static final int MSG_ADD_TO_CONFERENCE = 14; private static final int MSG_CONFERENCE = 14; private static final int MSG_SPLIT_FROM_CONFERENCE = 15; private static final int MSG_ON_POST_DIAL_CONTINUE = 16; Loading Loading @@ -128,24 +128,12 @@ public abstract class CallService extends Service { case MSG_STOP_DTMF_TONE: stopDtmfTone((String) msg.obj); break; case MSG_ADD_TO_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { @SuppressWarnings("unchecked") List<String> callIds = (List<String>) args.arg2; String conferenceCallId = (String) args.arg1; addToConference(conferenceCallId, callIds); } finally { args.recycle(); } break; } case MSG_SPLIT_FROM_CONFERENCE: { case MSG_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { String conferenceCallId = (String) args.arg1; String callId = (String) args.arg2; splitFromConference(conferenceCallId, callId); conference(conferenceCallId, callId); } finally { args.recycle(); } Loading @@ -162,6 +150,9 @@ public abstract class CallService extends Service { } break; } case MSG_SPLIT_FROM_CONFERENCE: splitFromConference((String) msg.obj); break; default: break; } Loading Loading @@ -245,19 +236,16 @@ public abstract class CallService extends Service { } @Override public void addToConference(String conferenceCallId, List<String> callsToConference) { public void conference(String conferenceCallId, String callId) { SomeArgs args = SomeArgs.obtain(); args.arg1 = conferenceCallId; args.arg2 = callsToConference; mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget(); args.arg2 = callId; mMessageHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); } @Override public void splitFromConference(String conferenceCallId, String callId) { SomeArgs args = SomeArgs.obtain(); args.arg1 = conferenceCallId; args.arg2 = callId; mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget(); public void splitFromConference(String callId) { mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget(); } @Override Loading Loading @@ -424,24 +412,22 @@ public abstract class CallService extends Service { public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); /** * Adds the specified calls to the specified conference call. * Conferences the specified call. * * @param conferenceCallId The unique ID of the conference call onto which the specified calls * should be added. * @param callIds The calls to add to the conference call. * @param callId The call to conference. * @hide */ public abstract void addToConference(String conferenceCallId, List<String> callIds); public abstract void conference(String conferenceCallId, String callId); /** * Removes the specified call from the specified conference call. This is a no-op if the call * is not already part of the conference call. * Removes the specified call from a conference call. * * @param conferenceCallId The conference call. * @param callId The call to remove from the conference call * @hide */ public abstract void splitFromConference(String conferenceCallId, String callId); public abstract void splitFromConference(String callId); public void onPostDialContinue(String callId, boolean proceed) {} public void onPostDialWait(Connection conn, String remaining) {} Loading
telecomm/java/android/telecomm/CallServiceAdapter.java +19 −6 Original line number Diff line number Diff line Loading @@ -179,12 +179,12 @@ public final class CallServiceAdapter { * Indicates that the specified call can conference with any of the specified list of calls. * * @param callId The unique ID of the call. * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced. * @param canConference Specified whether or not the call can be conferenced. * @hide */ public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) { public void setCanConference(String callId, boolean canConference) { try { mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds); mAdapter.setCanConference(callId, canConference); } catch (RemoteException ignored) { } } Loading @@ -193,13 +193,14 @@ public final class CallServiceAdapter { * Indicates whether or not the specified call is currently conferenced into the specified * conference call. * * @param conferenceCallId The unique ID of the conference call. * @param callId The unique ID of the call being conferenced. * @param conferenceCallId The unique ID of the conference call. Null if call is not * conferenced. * @hide */ public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) { public void setIsConferenced(String callId, String conferenceCallId) { try { mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced); mAdapter.setIsConferenced(callId, conferenceCallId); } catch (RemoteException ignored) { } } Loading @@ -224,4 +225,16 @@ public final class CallServiceAdapter { } catch (RemoteException ignored) { } } /** * Indicates that a new conference call has been created. * * @param callId The unique ID of the conference call. */ public void addConferenceCall(String callId) { try { mAdapter.addConferenceCall(callId, null); } catch (RemoteException ignored) { } } }
telecomm/java/android/telecomm/Connection.java +121 −1 Original line number Diff line number Diff line Loading @@ -19,7 +19,10 @@ package android.telecomm; import android.net.Uri; import android.os.Bundle; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** Loading @@ -35,6 +38,8 @@ public abstract class Connection { void onDisconnected(Connection c, int cause, String message); void onRequestingRingback(Connection c, boolean ringback); void onDestroyed(Connection c); void onConferenceCapableChanged(Connection c, boolean isConferenceCapable); void onParentConnectionChanged(Connection c, Connection parent); } public static class ListenerBase implements Listener { Loading Loading @@ -65,6 +70,14 @@ public abstract class Connection { /** {@inheritDoc} */ @Override public void onRequestingRingback(Connection c, boolean ringback) {} /** ${inheritDoc} */ @Override public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {} /** ${inheritDoc} */ @Override public void onParentConnectionChanged(Connection c, Connection parent) {} } public final class State { Loading @@ -79,10 +92,14 @@ public abstract class Connection { } private final Set<Listener> mListeners = new HashSet<>(); private final List<Connection> mChildConnections = new ArrayList<>(); private int mState = State.NEW; private CallAudioState mCallAudioState; private Uri mHandle; private boolean mRequestingRingback = false; private boolean mIsConferenceCapable = false; private Connection mParentConnection; /** * Create a new Connection. Loading Loading @@ -175,6 +192,16 @@ public abstract class Connection { onDisconnect(); } /** * Separates this Connection from a parent connection. * * @hide */ public final void separate() { Log.d(this, "separate"); onSeparate(); } /** * Abort this Connection. The Connection will immediately transition to * the {@link State#DISCONNECTED} state, and send no notifications of this Loading Loading @@ -239,6 +266,14 @@ public abstract class Connection { } } /** * TODO(santoscordon): Needs updated documentation. */ public final void conference() { Log.d(this, "conference"); onConference(); } /** * Inform this Connection that the state of its audio output has been changed externally. * Loading Loading @@ -274,13 +309,45 @@ public abstract class Connection { } /** * @return Whether this connection is requesting that the system play a ringback tone * Returns whether this connection is requesting that the system play a ringback tone * on its behalf. */ public boolean isRequestingRingback() { return mRequestingRingback; } /** * Returns whether this connection is a conference connection (has child connections). */ public boolean isConferenceConnection() { return !mChildConnections.isEmpty(); } public 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 Connection getParentConnection() { return mParentConnection; } public List<Connection> getChildConnections() { return mChildConnections; } /** * Sets the value of the {@link #getHandle()} property and notifies listeners. * Loading Loading @@ -358,6 +425,32 @@ public abstract class Connection { } } /** * TODO(santoscordon): Needs documentation. */ protected void setIsConferenceCapable(boolean isConferenceCapable) { if (mIsConferenceCapable != isConferenceCapable) { mIsConferenceCapable = isConferenceCapable; for (Listener l : mListeners) { l.onConferenceCapableChanged(this, mIsConferenceCapable); } } } /** * TODO(santoscordon): Needs documentation. */ protected void setDestroyed() { // It is possible that onDestroy() will trigger the listener to remove itself which will // result in a concurrent modification exception. To counteract this we make a copy of the // listeners and iterate on that. for (Listener l : new ArrayList<>(mListeners)) { if (mListeners.contains(l)) { l.onDestroyed(this); } } } /** * Notifies this Connection and listeners that the {@link #getCallAudioState()} property * has a new value. Loading Loading @@ -417,6 +510,11 @@ public abstract class Connection { */ protected void onDisconnect() {} /** * Notifies this Connection of a request to disconnect. */ protected void onSeparate() {} /** * Notifies this Connection of a request to abort. */ Loading Loading @@ -449,6 +547,28 @@ public abstract class Connection { */ protected void onPostDialContinue(boolean proceed) {} /** * TODO(santoscordon): Needs documentation. */ protected void onConference() {} /** * TODO(santoscordon): Needs documentation. */ protected void onChildrenChanged(List<Connection> children) {} 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) { Log.d(this, "setState: %s", stateToString(state)); onSetState(state); Loading
telecomm/java/android/telecomm/ConnectionService.java +92 −21 Original line number Diff line number Diff line Loading @@ -20,10 +20,15 @@ import android.net.Uri; import android.os.Bundle; import android.telephony.DisconnectCause; import android.os.SystemClock; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * A {@link android.app.Service} that provides telephone connections to Loading @@ -32,7 +37,6 @@ import java.util.Map; public abstract class ConnectionService extends CallService { // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); private static final Connection NULL_CONNECTION = new Connection() {}; // Mappings from Connections to IDs as understood by the current CallService implementation Loading Loading @@ -99,6 +103,20 @@ public abstract class ConnectionService extends CallService { Log.d(this, "Adapter onRingback %b", ringback); getAdapter().setRequestingRingback(id, ringback); } @Override public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) { String id = mIdByConnection.get(c); getAdapter().setCanConference(id, isConferenceCapable); } /** ${inheritDoc} */ @Override public void onParentConnectionChanged(Connection c, Connection parent) { String id = mIdByConnection.get(c); String parentId = parent == null ? null : mIdByConnection.get(parent); getAdapter().setIsConferenced(id, parentId); } }; @Override Loading @@ -110,8 +128,7 @@ public abstract class ConnectionService extends CallService { @Override public void onResult(Uri handle, Subscription... result) { boolean isCompatible = result.length > 0; Log.d(this, "adapter setIsCompatibleWith " + callInfo.getId() + " " + isCompatible); Log.d(this, "adapter setIsCompatibleWith "); getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible); } Loading @@ -135,7 +152,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { if (result.length != 1) { if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo); getAdapter().handleFailedOutgoingCall( request, Loading @@ -145,10 +162,10 @@ public abstract class ConnectionService extends CallService { c.abort(); } } else { addConnection(callInfo.getId(), result[0]); Log.d(this, "adapter handleSuccessfulOutgoingCall %s", callInfo.getId()); getAdapter().handleSuccessfulOutgoingCall(callInfo.getId()); addConnection(callInfo.getId(), result[0]); } } Loading Loading @@ -177,7 +194,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { if (result.length != 1) { if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callId); getAdapter().handleFailedOutgoingCall( request, Loading Loading @@ -258,27 +275,43 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override public final void addToConference(String conferenceCallId, List<String> callIds) { Log.d(this, "addToConference %s, %s", conferenceCallId, callIds); public final void conference(final String conferenceCallId, String callId) { Log.d(this, "conference %s, %s", conferenceCallId, callId); List<Connection> connections = new LinkedList<>(); for (String id : callIds) { Connection connection = findConnectionForAction(id, "addToConference"); Connection connection = findConnectionForAction(callId, "conference"); if (connection == NULL_CONNECTION) { Log.w(this, "Connection missing in conference request %s.", id); Log.w(this, "Connection missing in conference request %s.", callId); return; } connections.add(connection); 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); getAdapter().addConferenceCall(conferenceCallId); addConnection(conferenceCallId, conferenceConnection); } } } // TODO(santoscordon): Find an existing conference call or create a new one. Then call // conferenceWith on it. /** ${inheritDoc} */ @Override public void onError(String request, int code, String reason) { // no-op } }); } /** @hide */ @Override public final void splitFromConference(String conferenceCallId, String callId) { Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId); public final void splitFromConference(String callId) { Log.d(this, "splitFromConference(%s)", callId); Connection connection = findConnectionForAction(callId, "splitFromConference"); if (connection == NULL_CONNECTION) { Loading Loading @@ -308,6 +341,13 @@ public abstract class ConnectionService extends CallService { getAdapter().onPostDialWait(mIdByConnection.get(conn), remaining); } /** * Returns all connections currently associated with this connection service. */ public Collection<Connection> getAllConnections() { return mConnectionById.values(); } /** * Find a set of Subscriptions matching a given handle (e.g. phone number). * Loading @@ -328,6 +368,21 @@ public abstract class ConnectionService extends CallService { ConnectionRequest request, Response<ConnectionRequest, Connection> callback) {} /** * 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. * * @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. */ public void onCreateConferenceConnection( String token, Connection connection, Response<String, Connection> callback) {} /** * Create a Connection to match an incoming connection notification. * Loading @@ -338,6 +393,20 @@ public abstract class ConnectionService extends CallService { ConnectionRequest request, Response<ConnectionRequest, Connection> callback) {} /** * Notifies that a connection has been added to this connection service and sent to Telecomm. * * @param connection The connection which was added. */ public void onConnectionAdded(Connection connection) {} /** * Notified that a connection has been removed from this connection service. * * @param connection The connection which was removed. */ public void onConnectionRemoved(Connection connection) {} static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { Loading Loading @@ -387,12 +456,14 @@ public abstract class ConnectionService extends CallService { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); connection.addConnectionListener(mConnectionListener); onConnectionAdded(connection); } private void removeConnection(Connection connection) { connection.removeConnectionListener(mConnectionListener); mConnectionById.remove(mIdByConnection.get(connection)); mIdByConnection.remove(connection); onConnectionRemoved(connection); } private Connection findConnectionForAction(String callId, String action) { Loading