diff options
author | Ihab Awad <ihab@google.com> | 2014-07-30 10:07:40 -0700 |
---|---|---|
committer | Ihab Awad <ihab@google.com> | 2014-08-05 18:21:26 -0700 |
commit | 5d0410fa7c2dead7906780551ba4aa0305021cef (patch) | |
tree | aab4610704421337c6d1c343f6580d07dc7b2ed8 /telecomm | |
parent | 55a684a82db815785882e12945288c40f0b9eee7 (diff) | |
download | frameworks_base-5d0410fa7c2dead7906780551ba4aa0305021cef.zip frameworks_base-5d0410fa7c2dead7906780551ba4aa0305021cef.tar.gz frameworks_base-5d0410fa7c2dead7906780551ba4aa0305021cef.tar.bz2 |
Finalize implementation of Remote Connection Service (1/4)
Change-Id: I3788c9ab03a979f22ab628637adf6d520038cb6e
Diffstat (limited to 'telecomm')
-rw-r--r-- | telecomm/java/android/telecomm/Call.java | 44 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/ConnectionService.java | 32 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java | 377 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/InCallService.java | 25 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/Phone.java | 8 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/RemoteConnection.java | 316 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/RemoteConnectionManager.java | 10 | ||||
-rw-r--r-- | telecomm/java/android/telecomm/RemoteConnectionService.java | 483 |
8 files changed, 848 insertions, 447 deletions
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java index 838f221..3a04632 100644 --- a/telecomm/java/android/telecomm/Call.java +++ b/telecomm/java/android/telecomm/Call.java @@ -74,7 +74,7 @@ public final class Call { private final String mCallerDisplayName; private final int mCallerDisplayNamePresentation; private final PhoneAccountHandle mAccountHandle; - private final int mCapabilities; + private final int mCallCapabilities; private final int mDisconnectCauseCode; private final String mDisconnectCauseMsg; private final long mConnectTimeMillis; @@ -125,8 +125,8 @@ public final class Call { * @return A bitmask of the capabilities of the {@code Call}, as defined in * {@link CallCapabilities}. */ - public int getCapabilities() { - return mCapabilities; + public int getCallCapabilities() { + return mCallCapabilities; } /** @@ -162,14 +162,15 @@ public final class Call { } /** - * @return Returns the video state of the {@code Call}. + * @return The video state of the {@code Call}. */ public int getVideoState() { return mVideoState; } - /* - * @return The current {@link android.telecomm.StatusHints}, or null if none has been set. + /** + * @return The current {@link android.telecomm.StatusHints}, or {@code null} if none + * have been set. */ public StatusHints getStatusHints() { return mStatusHints; @@ -186,7 +187,7 @@ public final class Call { Objects.equals(mCallerDisplayNamePresentation, d.mCallerDisplayNamePresentation) && Objects.equals(mAccountHandle, d.mAccountHandle) && - Objects.equals(mCapabilities, d.mCapabilities) && + Objects.equals(mCallCapabilities, d.mCallCapabilities) && Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) && Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) && Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && @@ -205,7 +206,7 @@ public final class Call { Objects.hashCode(mCallerDisplayName) + Objects.hashCode(mCallerDisplayNamePresentation) + Objects.hashCode(mAccountHandle) + - Objects.hashCode(mCapabilities) + + Objects.hashCode(mCallCapabilities) + Objects.hashCode(mDisconnectCauseCode) + Objects.hashCode(mDisconnectCauseMsg) + Objects.hashCode(mConnectTimeMillis) + @@ -233,7 +234,7 @@ public final class Call { mCallerDisplayName = callerDisplayName; mCallerDisplayNamePresentation = callerDisplayNamePresentation; mAccountHandle = accountHandle; - mCapabilities = capabilities; + mCallCapabilities = capabilities; mDisconnectCauseCode = disconnectCauseCode; mDisconnectCauseMsg = disconnectCauseMsg; mConnectTimeMillis = connectTimeMillis; @@ -289,15 +290,6 @@ public final class Call { public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {} /** - * Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals - * that were embedded into the outgoing number. - * - * @param call The {@code Call} invoking this method. - * @param remainingPostDialSequence The post-dial characters that remain to be sent. - */ - public void onPostDial(Call call, String remainingPostDialSequence) {} - - /** * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause * character. This causes the post-dial signals to stop pending user confirmation. An * implementation should present this choice to the user and invoke @@ -314,7 +306,6 @@ public final class Call { * @param call The {@code Call} invoking this method. * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}. */ - public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {} /** @@ -427,8 +418,6 @@ public final class Call { * * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. - * While these tones are playing, this {@code Call} will notify listeners via - * {@link Listener#onPostDial(Call, String)}. * * If the DTMF string contains a {@link TelecommManager#DTMF_CHARACTER_PAUSE} symbol, this * {@code Call} will temporarily pause playing the tones for a pre-defined period of time. @@ -657,12 +646,6 @@ public final class Call { } /** {@hide} */ - final void internalSetPostDial(String remaining) { - mRemainingPostDialSequence = remaining; - firePostDial(mRemainingPostDialSequence); - } - - /** {@hide} */ final void internalSetPostDialWait(String remaining) { mRemainingPostDialSequence = remaining; firePostDialWait(mRemainingPostDialSequence); @@ -715,13 +698,6 @@ public final class Call { } } - private void firePostDial(String remainingPostDialSequence) { - Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); - for (int i = 0; i < listeners.length; i++) { - listeners[i].onPostDial(this, remainingPostDialSequence); - } - } - private void firePostDialWait(String remainingPostDialSequence) { Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); for (int i = 0; i < listeners.length; i++) { diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index 1484021..4c85f00 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -39,6 +39,7 @@ import com.android.internal.telecomm.IVideoCallCallback; import com.android.internal.telecomm.IVideoCallProvider; import com.android.internal.telecomm.RemoteServiceCallback; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -81,6 +82,7 @@ public abstract class ConnectionService extends Service { private final RemoteConnectionManager mRemoteConnectionManager = new RemoteConnectionManager(); private boolean mAreAccountsInitialized = false; + private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>(); private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter(); /** @@ -226,11 +228,23 @@ public abstract class ConnectionService extends Service { case MSG_CREATE_CONNECTION: { SomeArgs args = (SomeArgs) msg.obj; try { - PhoneAccountHandle connectionManagerPhoneAccount = + final PhoneAccountHandle connectionManagerPhoneAccount = (PhoneAccountHandle) args.arg1; - ConnectionRequest request = (ConnectionRequest) args.arg2; - boolean isIncoming = args.argi1 == 1; - createConnection(connectionManagerPhoneAccount, request, isIncoming); + final ConnectionRequest request = (ConnectionRequest) args.arg2; + final boolean isIncoming = args.argi1 == 1; + if (!mAreAccountsInitialized) { + mPreInitializationConnectionRequests.add(new Runnable() { + @Override + public void run() { + createConnection( + connectionManagerPhoneAccount, + request, + isIncoming); + } + }); + } else { + createConnection(connectionManagerPhoneAccount, request, isIncoming); + } } finally { args.recycle(); } @@ -643,7 +657,7 @@ public abstract class ConnectionService extends Service { componentNames.get(i), IConnectionService.Stub.asInterface(services.get(i))); } - mAreAccountsInitialized = true; + onAccountsInitialized(); Log.d(this, "remote connection services found: " + services); } }); @@ -803,6 +817,14 @@ public abstract class ConnectionService extends Service { return builder.toString(); } + private void onAccountsInitialized() { + mAreAccountsInitialized = true; + for (Runnable r : mPreInitializationConnectionRequests) { + r.run(); + } + mPreInitializationConnectionRequests.clear(); + } + private void addConnection(String callId, Connection connection) { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java new file mode 100644 index 0000000..3ef61ac --- /dev/null +++ b/telecomm/java/android/telecomm/ConnectionServiceAdapterServant.java @@ -0,0 +1,377 @@ +/* + * 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 + R* limitations under the License. + */ + +package android.telecomm; + +import com.android.internal.os.SomeArgs; +import com.android.internal.telecomm.IConnectionServiceAdapter; +import com.android.internal.telecomm.IVideoCallProvider; +import com.android.internal.telecomm.RemoteServiceCallback; + +import android.app.PendingIntent; +import android.net.Uri; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; + +/** + * A component that provides an RPC servant implementation of {@link IConnectionServiceAdapter}, + * posting incoming messages on the main thread on a client-supplied delegate object. + * + * TODO: Generate this and similar classes using a compiler starting from AIDL interfaces. + * + * @hide + */ +final class ConnectionServiceAdapterServant { + private static final int MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL = 1; + private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2; + private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 3; + private static final int MSG_SET_ACTIVE = 4; + private static final int MSG_SET_RINGING = 5; + private static final int MSG_SET_DIALING = 6; + private static final int MSG_SET_DISCONNECTED = 7; + private static final int MSG_SET_ON_HOLD = 8; + private static final int MSG_SET_REQUESTING_RINGBACK = 9; + private static final int MSG_SET_CALL_CAPABILITIES = 10; + private static final int MSG_SET_IS_CONFERENCED = 11; + private static final int MSG_ADD_CONFERENCE_CALL = 12; + private static final int MSG_REMOVE_CALL = 13; + private static final int MSG_ON_POST_DIAL_WAIT = 14; + private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15; + private static final int MSG_SET_VIDEO_STATE = 16; + private static final int MSG_SET_VIDEO_CALL_PROVIDER = 17; + private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18; + private static final int MSG_SET_STATUS_HINTS = 19; + private static final int MSG_SET_HANDLE = 20; + private static final int MSG_SET_CALLER_DISPLAY_NAME = 21; + private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 22; + + private final IConnectionServiceAdapter mDelegate; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + try { + internalHandleMessage(msg); + } catch (RemoteException e) { + } + } + + // Internal method defined to centralize handling of RemoteException + private void internalHandleMessage(Message msg) throws RemoteException { + switch (msg.what) { + case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.handleCreateConnectionSuccessful( + (ConnectionRequest) args.arg1, + (ParcelableConnection) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_HANDLE_CREATE_CONNECTION_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.handleCreateConnectionFailed( + (ConnectionRequest) args.arg1, + args.argi1, + (String) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: { + mDelegate.handleCreateConnectionCancelled((ConnectionRequest) msg.obj); + break; + } + case MSG_SET_ACTIVE: + mDelegate.setActive((String) msg.obj); + break; + case MSG_SET_RINGING: + mDelegate.setRinging((String) msg.obj); + break; + case MSG_SET_DIALING: + mDelegate.setDialing((String) msg.obj); + break; + case MSG_SET_DISCONNECTED: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setDisconnected( + (String) args.arg1, args.argi1, (String) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_SET_ON_HOLD: + mDelegate.setOnHold((String) msg.obj); + break; + case MSG_SET_REQUESTING_RINGBACK: + mDelegate.setRequestingRingback((String) msg.obj, msg.arg1 == 1); + break; + case MSG_SET_CALL_CAPABILITIES: + mDelegate.setCallCapabilities((String) msg.obj, msg.arg1); + break; + case MSG_SET_IS_CONFERENCED: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setIsConferenced((String) args.arg1, (String) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_ADD_CONFERENCE_CALL: + mDelegate.addConferenceCall((String) msg.obj); + break; + case MSG_REMOVE_CALL: + mDelegate.removeCall((String) msg.obj); + break; + case MSG_ON_POST_DIAL_WAIT: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.onPostDialWait((String) args.arg1, (String) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_QUERY_REMOTE_CALL_SERVICES: + mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj); + break; + case MSG_SET_VIDEO_STATE: + mDelegate.setVideoState((String) msg.obj, msg.arg1); + break; + case MSG_SET_VIDEO_CALL_PROVIDER: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setVideoCallProvider((String) args.arg1, + (IVideoCallProvider) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_SET_AUDIO_MODE_IS_VOIP: + mDelegate.setAudioModeIsVoip((String) msg.obj, msg.arg1 == 1); + break; + case MSG_SET_STATUS_HINTS: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setStatusHints((String) args.arg1, (StatusHints) args.arg2); + } finally { + args.recycle(); + } + break; + } + case MSG_SET_HANDLE: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setHandle((String) args.arg1, (Uri) args.arg2, args.argi1); + } finally { + args.recycle(); + } + break; + } + case MSG_SET_CALLER_DISPLAY_NAME: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.setCallerDisplayName( + (String) args.arg1, (String) args.arg2, args.argi1); + } finally { + args.recycle(); + } + break; + } + case MSG_START_ACTIVITY_FROM_IN_CALL: { + SomeArgs args = (SomeArgs) msg.obj; + try { + mDelegate.startActivityFromInCall( + (String) args.arg1, (PendingIntent) args.arg2); + } finally { + args.recycle(); + } + break; + } + } + } + }; + + private final IConnectionServiceAdapter mStub = new IConnectionServiceAdapter.Stub() { + @Override + public void handleCreateConnectionSuccessful( + ConnectionRequest request, ParcelableConnection connection) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = request; + args.arg2 = connection; + mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget(); + } + + @Override + public void handleCreateConnectionFailed( + ConnectionRequest request, int errorCode, String errorMessage) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = request; + args.argi1 = errorCode; + args.arg2 = errorMessage; + mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget(); + } + + @Override + public void handleCreateConnectionCancelled(ConnectionRequest request) { + mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget(); + } + + @Override + public void setActive(String connectionId) { + mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget(); + } + + @Override + public void setRinging(String connectionId) { + mHandler.obtainMessage(MSG_SET_RINGING, connectionId).sendToTarget(); + } + + @Override + public void setDialing(String connectionId) { + mHandler.obtainMessage(MSG_SET_DIALING, connectionId).sendToTarget(); + } + + @Override + public void setDisconnected( + String connectionId, int disconnectCause, String disconnectMessage) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = disconnectMessage; + args.argi1 = disconnectCause; + mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget(); + } + + @Override + public void setOnHold(String connectionId) { + mHandler.obtainMessage(MSG_SET_ON_HOLD, connectionId).sendToTarget(); + } + + @Override + public void setRequestingRingback(String connectionId, boolean ringback) { + mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, ringback ? 1 : 0, 0, connectionId) + .sendToTarget(); + } + + @Override + public void setCallCapabilities(String connectionId, int callCapabilities) { + mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, connectionId) + .sendToTarget(); + } + + @Override + public void setIsConferenced(String callId, String conferenceCallId) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = conferenceCallId; + mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget(); + } + + @Override + public void addConferenceCall(String callId) { + mHandler.obtainMessage(MSG_ADD_CONFERENCE_CALL, callId).sendToTarget(); + } + + @Override + public void removeCall(String connectionId) { + mHandler.obtainMessage(MSG_REMOVE_CALL, connectionId).sendToTarget(); + } + + @Override + public void onPostDialWait(String connectionId, String remainingDigits) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = remainingDigits; + mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget(); + } + + @Override + public void queryRemoteConnectionServices(RemoteServiceCallback callback) { + mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget(); + } + + @Override + public void setVideoState(String connectionId, int videoState) { + mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, connectionId).sendToTarget(); + } + + @Override + public void setVideoCallProvider( + String connectionId, IVideoCallProvider videoCallProvider) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = videoCallProvider; + mHandler.obtainMessage(MSG_SET_VIDEO_CALL_PROVIDER, args).sendToTarget(); + } + + @Override + public final void setAudioModeIsVoip(String connectionId, boolean isVoip) { + mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, isVoip ? 1 : 0, 0, + connectionId).sendToTarget(); + } + + @Override + public final void setStatusHints(String connectionId, StatusHints statusHints) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = statusHints; + mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget(); + } + + @Override + public final void setHandle(String connectionId, Uri handle, int presentation) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = handle; + args.argi1 = presentation; + mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget(); + } + + @Override + public final void setCallerDisplayName( + String connectionId, String callerDisplayName, int presentation) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = callerDisplayName; + args.argi1 = presentation; + mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget(); + } + + @Override + public final void startActivityFromInCall(String connectionId, PendingIntent intent) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = connectionId; + args.arg2 = intent; + mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, args).sendToTarget(); + } + }; + + public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) { + mDelegate = delegate; + } + + public IConnectionServiceAdapter getStub() { + return mStub; + } +} diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java index 14b25dc..83e2957 100644 --- a/telecomm/java/android/telecomm/InCallService.java +++ b/telecomm/java/android/telecomm/InCallService.java @@ -41,11 +41,10 @@ public abstract class InCallService extends Service { private static final int MSG_SET_IN_CALL_ADAPTER = 1; private static final int MSG_ADD_CALL = 2; private static final int MSG_UPDATE_CALL = 3; - private static final int MSG_SET_POST_DIAL = 4; - private static final int MSG_SET_POST_DIAL_WAIT = 5; - private static final int MSG_ON_AUDIO_STATE_CHANGED = 6; - private static final int MSG_BRING_TO_FOREGROUND = 7; - private static final int MSG_START_ACTIVITY = 8; + private static final int MSG_SET_POST_DIAL_WAIT = 4; + private static final int MSG_ON_AUDIO_STATE_CHANGED = 5; + private static final int MSG_BRING_TO_FOREGROUND = 6; + private static final int MSG_START_ACTIVITY = 7; /** Default Handler used to consolidate binder method calls onto a single thread. */ private final Handler mHandler = new Handler(Looper.getMainLooper()) { @@ -62,17 +61,6 @@ public abstract class InCallService extends Service { case MSG_UPDATE_CALL: mPhone.internalUpdateCall((ParcelableCall) msg.obj); break; - case MSG_SET_POST_DIAL: { - SomeArgs args = (SomeArgs) msg.obj; - try { - String callId = (String) args.arg1; - String remaining = (String) args.arg2; - mPhone.internalSetPostDial(callId, remaining); - } finally { - args.recycle(); - } - break; - } case MSG_SET_POST_DIAL_WAIT: { SomeArgs args = (SomeArgs) msg.obj; try { @@ -124,10 +112,7 @@ public abstract class InCallService extends Service { @Override public void setPostDial(String callId, String remaining) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = callId; - args.arg2 = remaining; - mHandler.obtainMessage(MSG_SET_POST_DIAL, args).sendToTarget(); + // TODO: Unused } @Override diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java index b4c8a80..c0454ee 100644 --- a/telecomm/java/android/telecomm/Phone.java +++ b/telecomm/java/android/telecomm/Phone.java @@ -121,14 +121,6 @@ public final class Phone { } /** {@hide} */ - final void internalSetPostDial(String telecommId, String remaining) { - Call call = mCallByTelecommCallId.get(telecommId); - if (call != null) { - call.internalSetPostDial(remaining); - } - } - - /** {@hide} */ final void internalSetPostDialWait(String telecommId, String remaining) { Call call = mCallByTelecommCallId.get(telecommId); if (call != null) { diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java index ab980e8..bb272fa 100644 --- a/telecomm/java/android/telecomm/RemoteConnection.java +++ b/telecomm/java/android/telecomm/RemoteConnection.java @@ -24,25 +24,152 @@ import android.telephony.DisconnectCause; import com.android.internal.telecomm.IConnectionService; import java.util.HashSet; +import java.util.List; import java.util.Set; /** * RemoteConnection object used by RemoteConnectionService. */ public final class RemoteConnection { + public static abstract class Listener { + /** + * Invoked when the state of this {@code RemoteConnection} has changed. See + * {@link #getState()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param state The new state of the {@code RemoteConnection}. + */ public void onStateChanged(RemoteConnection connection, int state) {} - public void onDisconnected(RemoteConnection connection, int cause, String message) {} + + /** + * Invoked when the parent of this {@code RemoteConnection} has changed. See + * {@link #getParent()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param parent The new parent of the {@code RemoteConnection}. + */ + public void onParentChanged(RemoteConnection connection, RemoteConnection parent) {} + + /** + * Invoked when the children of this {@code RemoteConnection} have changed. See + * {@link #getChildren()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param children The new children of the {@code RemoteConnection}. + */ + public void onChildrenChanged( + RemoteConnection connection, List<RemoteConnection> children) {} + + /** + * Invoked when this {@code RemoteConnection} is disconnected. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param disconnectCauseCode The failure code ({@see DisconnectCause}) associated with this + * failed connection. + * @param disconnectCauseMessage The reason for the connection failure. This will not be + * displayed to the user. + */ + public void onDisconnected( + RemoteConnection connection, + int disconnectCauseCode, + String disconnectCauseMessage) {} + + /** + * Invoked when this {@code RemoteConnection} is requesting ringback. See + * {@link #isRequestingRingback()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param ringback Whether the {@code RemoteConnection} is requesting ringback. + */ public void onRequestingRingback(RemoteConnection connection, boolean ringback) {} + + /** + * Indicates that the call capabilities of this {@code RemoteConnection} have changed. + * See {@link #getCallCapabilities()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param callCapabilities The new call capabilities of the {@code RemoteConnection}. + */ public void onCallCapabilitiesChanged(RemoteConnection connection, int callCapabilities) {} - public void onPostDialWait(RemoteConnection connection, String remainingDigits) {} + + /** + * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a + * pause character. This causes the post-dial signals to stop pending user confirmation. An + * implementation should present this choice to the user and invoke + * {@link #postDialContinue(boolean)} when the user makes the choice. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDialWait(RemoteConnection connection, String remainingPostDialSequence) {} + + /** + * Indicates that the VOIP audio status of this {@code RemoteConnection} has changed. + * See {@link #getAudioModeIsVoip()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP. + */ public void onAudioModeIsVoipChanged(RemoteConnection connection, boolean isVoip) {} + + /** + * Indicates that the status hints of this {@code RemoteConnection} have changed. See + * {@link #getStatusHints()} ()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param statusHints The new status hints of the {@code RemoteConnection}. + */ public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {} + + /** + * Indicates that the handle (e.g., phone number) of this {@code RemoteConnection} has + * changed. See {@link #getHandle()} and {@link #getHandlePresentation()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param handle The new handle of the {@code RemoteConnection}. + * @param presentation The {@link CallPropertyPresentation} which controls how the + * handle is shown. + */ public void onHandleChanged(RemoteConnection connection, Uri handle, int presentation) {} + + /** + * Indicates that the caller display name of this {@code RemoteConnection} has changed. + * See {@link #getCallerDisplayName()} and {@link #getCallerDisplayNamePresentation()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param callerDisplayName The new caller display name of the {@code RemoteConnection}. + * @param presentation The {@link CallPropertyPresentation} which controls how the + * caller display name is shown. + */ public void onCallerDisplayNameChanged( RemoteConnection connection, String callerDisplayName, int presentation) {} + + /** + * Indicates that the video state of this {@code RemoteConnection} has changed. + * See {@link #getVideoState()}. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param videoState The new video state of the {@code RemoteConnection}. + */ public void onVideoStateChanged(RemoteConnection connection, int videoState) {} + + /** + * Indicates that this {@code RemoteConnection} is requesting that the in-call UI + * launch the specified activity. + * + * @param connection The {@code RemoteConnection} invoking this method. + * @param intent A {@link PendingIntent} that the {@code RemoteConnection} wishes to + * have launched on its behalf. + */ public void onStartActivityFromInCall(RemoteConnection connection, PendingIntent intent) {} + + /** + * Indicates that this {@code RemoteConnection} has been destroyed. No further requests + * should be made to the {@code RemoteConnection}, and references to it should be cleared. + * + * @param connection The {@code RemoteConnection} invoking this method. + */ public void onDestroyed(RemoteConnection connection) {} } @@ -51,8 +178,8 @@ public final class RemoteConnection { private final Set<Listener> mListeners = new HashSet<>(); private int mState = Connection.State.NEW; - private int mDisconnectCause = DisconnectCause.NOT_VALID; - private String mDisconnectMessage; + private int mDisconnectCauseCode = DisconnectCause.NOT_VALID; + private String mDisconnectCauseMessage; private boolean mRequestingRingback; private boolean mConnected; private int mCallCapabilities; @@ -69,10 +196,9 @@ public final class RemoteConnection { /** * @hide */ - RemoteConnection(IConnectionService connectionService, ConnectionRequest request, - boolean isIncoming) { + RemoteConnection(IConnectionService connectionService, ConnectionRequest request) { mConnectionService = connectionService; - mConnectionId = request.getCallId(); + mConnectionId = request == null ? "NULL" : request.getCallId(); mConnected = true; mState = Connection.State.INITIALIZING; @@ -87,73 +213,161 @@ public final class RemoteConnection { * @param failureMessage */ private RemoteConnection(int failureCode, String failureMessage) { - this(null, null, true); + this(null, null); mConnected = false; mState = Connection.State.FAILED; mFailureCode = failureCode; mFailureMessage = failureMessage; } + /** + * Adds a listener to this {@code RemoteConnection}. + * + * @param listener A {@code Listener}. + */ public void addListener(Listener listener) { mListeners.add(listener); } + /** + * Removes a listener from this {@code RemoteConnection}. + * + * @param listener A {@code Listener}. + */ public void removeListener(Listener listener) { mListeners.remove(listener); } + /** + * Obtains the parent of this {@code RemoteConnection} in a conference, if any. + * + * @return The parent {@code RemoteConnection}, or {@code null} if this {@code RemoteConnection} + * is not a child of any conference {@code RemoteConnection}s. + */ + public RemoteConnection getParent() { return null; } + + /** + * Obtains the children of this conference {@code RemoteConnection}, if any. + * + * @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; } + + /** + * Obtains the state of this {@code RemoteConnection}. + * + * @return A state value, chosen from the {@code STATE_*} constants. + */ public int getState() { return mState; } - public int getDisconnectCause() { - return mDisconnectCause; + /** + * @return For a {@link Connection.State#DISCONNECTED} {@code RemoteConnection}, the + * disconnect cause expressed as a code chosen from among those declared in + * {@link DisconnectCause}. + */ + public int getDisconnectCauseCode() { + return mDisconnectCauseCode; } - public String getDisconnectMessage() { - return mDisconnectMessage; + /** + * @return For a {@link Connection.State#DISCONNECTED} {@code RemoteConnection}, an optional + * reason for disconnection expressed as a free text message. + */ + public String getDisconnectCauseMessage() { + return mDisconnectCauseMessage; } + /** + * @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in + * {@link CallCapabilities}. + */ public int getCallCapabilities() { return mCallCapabilities; } + /** + * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP. + */ public boolean getAudioModeIsVoip() { return mAudioModeIsVoip; } + /** + * @return The current {@link android.telecomm.StatusHints} of this {@code RemoteConnection}, + * or {@code null} if none have been set. + */ public StatusHints getStatusHints() { return mStatusHints; } + /** + * @return The handle (e.g., phone number) to which the {@code RemoteConnection} is currently + * connected. + */ public Uri getHandle() { return mHandle; } + /** + * @return The presentation requirements for the handle. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ public int getHandlePresentation() { return mHandlePresentation; } + /** + * @return The display name for the caller. + */ public String getCallerDisplayName() { return mCallerDisplayName; } + /** + * @return The presentation requirements for the caller display name. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ public int getCallerDisplayNamePresentation() { return mCallerDisplayNamePresentation; } + /** + * @return The video state of the {@code RemoteConnection}. See + * {@link VideoCallProfile.VideoState}. + */ public int getVideoState() { return mVideoState; } + /** + * @return The failure code ({@see DisconnectCause}) associated with this failed + * {@code RemoteConnection}. + */ public int getFailureCode() { return mFailureCode; } + /** + * @return The reason for the connection failure. This will not be displayed to the user. + */ public String getFailureMessage() { return mFailureMessage; } + /** + * @return Whether the {@code RemoteConnection} is requesting that the framework play a + * ringback tone on its behalf. + */ + public boolean isRequestingRingback() { + return false; + } + + /** + * Instructs this {@code RemoteConnection} to abort. + */ public void abort() { try { if (mConnected) { @@ -163,6 +377,10 @@ public final class RemoteConnection { } } + /** + * Instructs this {@link Connection.State#RINGING} {@code RemoteConnection} to answer. + * @param videoState The video state in which to answer the call. + */ public void answer(int videoState) { try { if (mConnected) { @@ -172,6 +390,9 @@ public final class RemoteConnection { } } + /** + * Instructs this {@link Connection.State#RINGING} {@code RemoteConnection} to reject. + */ public void reject() { try { if (mConnected) { @@ -181,6 +402,9 @@ public final class RemoteConnection { } } + /** + * Instructs this {@code RemoteConnection} to go on hold. + */ public void hold() { try { if (mConnected) { @@ -190,6 +414,9 @@ public final class RemoteConnection { } } + /** + * Instructs this {@link Connection.State#HOLDING} call to release from hold. + */ public void unhold() { try { if (mConnected) { @@ -199,6 +426,9 @@ public final class RemoteConnection { } } + /** + * Instructs this {@code RemoteConnection} to disconnect. + */ public void disconnect() { try { if (mConnected) { @@ -208,7 +438,16 @@ public final class RemoteConnection { } } - public void playDtmf(char digit) { + /** + * Instructs this {@code RemoteConnection} to play a dual-tone multi-frequency signaling + * (DTMF) tone. + * + * Any other currently playing DTMF tone in the specified call is immediately stopped. + * + * @param digit A character representing the DTMF digit for which to play the tone. This + * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. + */ + public void playDtmfTone(char digit) { try { if (mConnected) { mConnectionService.playDtmfTone(mConnectionId, digit); @@ -217,7 +456,14 @@ public final class RemoteConnection { } } - public void stopDtmf() { + /** + * Instructs this {@code RemoteConnection} to stop any dual-tone multi-frequency signaling + * (DTMF) tone currently playing. + * + * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is + * currently playing, this method will do nothing. + */ + public void stopDtmfTone() { try { if (mConnected) { mConnectionService.stopDtmfTone(mConnectionId); @@ -226,6 +472,27 @@ public final class RemoteConnection { } } + /** + * Instructs this {@code RemoteConnection} to continue playing a post-dial DTMF string. + * + * A post-dial DTMF string is a string of digits following the first instance of either + * {@link TelecommManager#DTMF_CHARACTER_WAIT} or {@link TelecommManager#DTMF_CHARACTER_PAUSE}. + * These digits are immediately sent as DTMF tones to the recipient as soon as the + * connection is made. + * + * If the DTMF string contains a {@link TelecommManager#DTMF_CHARACTER_PAUSE} symbol, this + * {@code RemoteConnection} will temporarily pause playing the tones for a pre-defined period + * of time. + * + * If the DTMF string contains a {@link TelecommManager#DTMF_CHARACTER_WAIT} symbol, this + * {@code RemoteConnection} will pause playing the tones and notify listeners via + * {@link Listener#onPostDialWait(RemoteConnection, String)}. At this point, the in-call app + * should display to the user an indication of this state and an affordance to continue + * the postdial sequence. When the user decides to continue the postdial sequence, the in-call + * app should invoke the {@link #postDialContinue(boolean)} method. + * + * @param proceed Whether or not to continue with the post-dial sequence. + */ public void postDialContinue(boolean proceed) { try { if (mConnected) { @@ -235,6 +502,10 @@ public final class RemoteConnection { } } + /** + * Instructs this {@code RemoteConnection} to swap itself with an existing background call, + * if one such call exists. + */ public void swapWithBackgroundCall() { try { if (mConnected) { @@ -244,6 +515,11 @@ public final class RemoteConnection { } } + /** + * Set the audio state of this {@code RemoteConnection}. + * + * @param state The audio state of this {@code RemoteConnection}. + */ public void setAudioState(CallAudioState state) { try { if (mConnected) { @@ -271,8 +547,8 @@ public final class RemoteConnection { void setDisconnected(int cause, String message) { if (mState != Connection.State.DISCONNECTED) { mState = Connection.State.DISCONNECTED; - mDisconnectCause = cause; - mDisconnectMessage = message; + mDisconnectCauseCode = cause; + mDisconnectCauseMessage = message; for (Listener l : mListeners) { l.onDisconnected(this, cause, message); @@ -382,13 +658,6 @@ public final class RemoteConnection { } } - /** @hide */ - void setConnectionService(IConnectionService connectionService) { - mConnectionService = connectionService; - mConnectionService = null; - setState(Connection.State.NEW); - } - /** * Create a RemoteConnection which is in the {@link Connection.State#FAILED} state. Attempting * to use it for anything will almost certainly result in bad things happening. Do not do this. @@ -396,7 +665,6 @@ public final class RemoteConnection { * @return a failed {@link RemoteConnection} * * @hide - * */ public static RemoteConnection failure(int failureCode, String failureMessage) { return new RemoteConnection(failureCode, failureMessage); diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java index ce8bfbd..365ed5b 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionManager.java +++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java @@ -17,14 +17,11 @@ package android.telecomm; import android.content.ComponentName; -import android.net.Uri; import android.os.RemoteException; import com.android.internal.telecomm.IConnectionService; import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Map; /** @@ -37,18 +34,13 @@ public class RemoteConnectionManager { if (!mRemoteConnectionServices.containsKey(componentName)) { try { RemoteConnectionService remoteConnectionService = - new RemoteConnectionService(componentName, connectionService); + new RemoteConnectionService(connectionService); mRemoteConnectionServices.put(componentName, remoteConnectionService); } catch (RemoteException ignored) { } } } - List<PhoneAccountHandle> getAccounts(Uri handle) { - List<PhoneAccountHandle> accountHandles = new LinkedList<>(); - return accountHandles; - } - public RemoteConnection createRemoteConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request, diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java index 501e843..14c7691 100644 --- a/telecomm/java/android/telecomm/RemoteConnectionService.java +++ b/telecomm/java/android/telecomm/RemoteConnectionService.java @@ -17,21 +17,21 @@ package android.telecomm; import android.app.PendingIntent; -import android.content.ComponentName; import android.net.Uri; -import android.os.Handler; +import android.os.IBinder; import android.os.IBinder.DeathRecipient; -import android.os.Message; import android.os.RemoteException; import android.telephony.DisconnectCause; -import android.text.TextUtils; -import com.android.internal.os.SomeArgs; import com.android.internal.telecomm.IConnectionService; import com.android.internal.telecomm.IConnectionServiceAdapter; import com.android.internal.telecomm.IVideoCallProvider; import com.android.internal.telecomm.RemoteServiceCallback; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.UUID; /** @@ -39,369 +39,187 @@ import java.util.UUID; * * @hide */ -final class RemoteConnectionService implements DeathRecipient { - private static final int MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL = 1; - private static final int MSG_HANDLE_CREATE_CONNECTION_FAILED = 2; - private static final int MSG_HANDLE_CREATE_CONNECTION_CANCELLED = 3; - private static final int MSG_SET_ACTIVE = 4; - private static final int MSG_SET_RINGING = 5; - private static final int MSG_SET_DIALING = 6; - private static final int MSG_SET_DISCONNECTED = 7; - private static final int MSG_SET_ON_HOLD = 8; - private static final int MSG_SET_REQUESTING_RINGBACK = 9; - private static final int MSG_SET_CALL_CAPABILITIES = 10; - private static final int MSG_SET_IS_CONFERENCED = 11; - private static final int MSG_ADD_CONFERENCE_CALL = 12; - private static final int MSG_REMOVE_CALL = 13; - private static final int MSG_ON_POST_DIAL_WAIT = 14; - private static final int MSG_QUERY_REMOTE_CALL_SERVICES = 15; - private static final int MSG_SET_VIDEO_STATE = 16; - private static final int MSG_SET_CALL_VIDEO_PROVIDER = 17; - private static final int MSG_SET_AUDIO_MODE_IS_VOIP = 18; - private static final int MSG_SET_STATUS_HINTS = 19; - private static final int MSG_SET_HANDLE = 20; - private static final int MSG_SET_CALLER_DISPLAY_NAME = 21; - private static final int MSG_START_ACTIVITY_FROM_IN_CALL = 22; +final class RemoteConnectionService { - private final IConnectionService mConnectionService; - private final ComponentName mComponentName; - - private String mConnectionId; - // Remote connection services only support a single connection. - private RemoteConnection mConnection; + private static final RemoteConnection NULL_CONNECTION = new RemoteConnection(null, null); - private final Handler mHandler = new Handler() { + private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() { @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL: { - SomeArgs args = (SomeArgs) msg.obj; - try { - ConnectionRequest request = (ConnectionRequest) args.arg1; - if (isPendingConnection(request.getCallId())) { - ParcelableConnection parcel = (ParcelableConnection) args.arg2; - mConnection.setState(parcel.getState()); - mConnection.setCallCapabilities(parcel.getCapabilities()); - mConnection.setHandle( - parcel.getHandle(), parcel.getHandlePresentation()); - mConnection.setCallerDisplayName( - parcel.getCallerDisplayName(), - parcel.getCallerDisplayNamePresentation()); - // TODO: Do we need to support video providers for remote connections? - } - } finally { - args.recycle(); - } - break; - } - case MSG_HANDLE_CREATE_CONNECTION_FAILED: { - SomeArgs args = (SomeArgs) msg.obj; - try { - ConnectionRequest request = (ConnectionRequest) args.arg1; - if (isPendingConnection(request.getCallId())) { - // TODO: How do we propogate the failure codes? - destroyConnection(); - } - } finally { - args.recycle(); - } - break; - } - case MSG_HANDLE_CREATE_CONNECTION_CANCELLED: { - ConnectionRequest request = (ConnectionRequest) msg.obj; - if (isPendingConnection(request.getCallId())) { - destroyConnection(); - } - break; - } - case MSG_SET_ACTIVE: - if (isCurrentConnection(msg.obj)) { - mConnection.setState(Connection.State.ACTIVE); - } - break; - case MSG_SET_RINGING: - if (isCurrentConnection(msg.obj)) { - mConnection.setState(Connection.State.RINGING); - } - break; - case MSG_SET_DIALING: - if (isCurrentConnection(msg.obj)) { - mConnection.setState(Connection.State.DIALING); - } - break; - case MSG_SET_DISCONNECTED: { - SomeArgs args = (SomeArgs) msg.obj; - try { - if (isCurrentConnection(args.arg1)) { - mConnection.setDisconnected(args.argi1, (String) args.arg2); - } - } finally { - args.recycle(); - } - break; - } - case MSG_SET_ON_HOLD: - if (isCurrentConnection(msg.obj)) { - mConnection.setState(Connection.State.HOLDING); - } - break; - case MSG_SET_REQUESTING_RINGBACK: - if (isCurrentConnection(msg.obj)) { - mConnection.setRequestingRingback(msg.arg1 == 1); - } - break; - case MSG_SET_CALL_CAPABILITIES: - if (isCurrentConnection(msg.obj)) { - mConnection.setCallCapabilities(msg.arg1); - } - break; - case MSG_SET_IS_CONFERENCED: - // not supported for remote connections. - break; - case MSG_ADD_CONFERENCE_CALL: - // not supported for remote connections. - break; - case MSG_REMOVE_CALL: - if (isCurrentConnection(msg.obj)) { - destroyConnection(); - } - break; - case MSG_ON_POST_DIAL_WAIT: { - SomeArgs args = (SomeArgs) msg.obj; - try { - if (isCurrentConnection(args.arg1)) { - mConnection.setPostDialWait((String) args.arg2); - } - } finally { - args.recycle(); - } - break; - } - case MSG_QUERY_REMOTE_CALL_SERVICES: - // Not supported from remote connection service. - break; - case MSG_SET_VIDEO_STATE: - if (isCurrentConnection(msg.obj)) { - mConnection.setVideoState(msg.arg1); - } - break; - case MSG_SET_CALL_VIDEO_PROVIDER: - // not supported for remote connections. - break; - case MSG_SET_AUDIO_MODE_IS_VOIP: - if (isCurrentConnection(msg.obj)) { - mConnection.setAudioModeIsVoip(msg.arg1 == 1); - } - break; - case MSG_SET_STATUS_HINTS: { - SomeArgs args = (SomeArgs) msg.obj; - try { - if (isCurrentConnection(args.arg1)) { - mConnection.setStatusHints((StatusHints) args.arg2); - } - } finally { - args.recycle(); - } - break; - } - case MSG_SET_HANDLE: { - SomeArgs args = (SomeArgs) msg.obj; - try { - if (isCurrentConnection(args.arg1)) { - mConnection.setHandle((Uri) args.arg2, args.argi1); - } - } finally { - args.recycle(); - } - break; - } - case MSG_SET_CALLER_DISPLAY_NAME: { - SomeArgs args = (SomeArgs) msg.obj; - try { - if (isCurrentConnection(msg.arg1)) { - mConnection.setCallerDisplayName((String) args.arg2, args.argi1); - } - } finally { - args.recycle(); - } - break; - } - case MSG_START_ACTIVITY_FROM_IN_CALL: { - SomeArgs args = (SomeArgs) msg.obj; - try { - if (isCurrentConnection(msg.arg1)) { - mConnection.startActivityFromInCall((PendingIntent) args.arg2); - } - } finally { - args.recycle(); - } - break; - } + public void handleCreateConnectionSuccessful(ConnectionRequest request, + ParcelableConnection parcel) { + RemoteConnection connection = findConnectionForAction( + request.getCallId(), "handleCreateConnectionSuccessful"); + if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) { + mPendingConnections.remove(connection); + connection.setState(parcel.getState()); + connection.setCallCapabilities(parcel.getCapabilities()); + connection.setHandle( + parcel.getHandle(), parcel.getHandlePresentation()); + connection.setCallerDisplayName( + parcel.getCallerDisplayName(), + parcel.getCallerDisplayNamePresentation()); + // TODO: Do we need to support video providers for remote connections? } } - - }; - - private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() { @Override - public void handleCreateConnectionSuccessful( - ConnectionRequest request, ParcelableConnection connection) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = request; - args.arg2 = connection; - mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_SUCCESSFUL, args).sendToTarget(); - } - - @Override - public void handleCreateConnectionFailed( - ConnectionRequest request, int errorCode, String errorMessage) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = request; - args.argi1 = errorCode; - args.arg2 = errorMessage; - mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_FAILED, args).sendToTarget(); + public void handleCreateConnectionFailed(ConnectionRequest request, int errorCode, + String errorMessage) { + // TODO: How do we propagate the failure codes? + findConnectionForAction( + request.getCallId(), "handleCreateConnectionFailed") + .setDestroyed(); } @Override public void handleCreateConnectionCancelled(ConnectionRequest request) { - mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_CANCELLED, request).sendToTarget(); + findConnectionForAction( + request.getCallId(), "handleCreateConnectionCancelled") + .setDestroyed(); } @Override - public void setActive(String connectionId) { - mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget(); + public void setActive(String callId) { + findConnectionForAction(callId, "setActive") + .setState(Connection.State.ACTIVE); } @Override - public void setRinging(String connectionId) { - mHandler.obtainMessage(MSG_SET_RINGING, connectionId).sendToTarget(); + public void setRinging(String callId) { + findConnectionForAction(callId, "setRinging") + .setState(Connection.State.RINGING); } @Override - public void setDialing(String connectionId) { - mHandler.obtainMessage(MSG_SET_DIALING, connectionId).sendToTarget(); + public void setDialing(String callId) { + findConnectionForAction(callId, "setDialing") + .setState(Connection.State.DIALING); } @Override - public void setDisconnected( - String connectionId, int disconnectCause, String disconnectMessage) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = connectionId; - args.arg2 = disconnectMessage; - args.argi1 = disconnectCause; - mHandler.obtainMessage(MSG_SET_DISCONNECTED, args).sendToTarget(); + public void setDisconnected(String callId, int disconnectCause, + String disconnectMessage) { + findConnectionForAction(callId, "setDisconnected") + .setDisconnected(disconnectCause, disconnectMessage); } @Override - public void setOnHold(String connectionId) { - mHandler.obtainMessage(MSG_SET_ON_HOLD, connectionId).sendToTarget(); + public void setOnHold(String callId) { + findConnectionForAction(callId, "setOnHold") + .setState(Connection.State.HOLDING); } @Override - public void setRequestingRingback(String connectionId, boolean ringback) { - mHandler.obtainMessage(MSG_SET_REQUESTING_RINGBACK, ringback ? 1 : 0, 0, connectionId) - .sendToTarget(); + public void setRequestingRingback(String callId, boolean ringing) { + findConnectionForAction(callId, "setRequestingRingback") + .setRequestingRingback(ringing); } @Override - public void setCallCapabilities(String connectionId, int callCapabilities) { - mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, connectionId) - .sendToTarget(); + public void setCallCapabilities(String callId, int callCapabilities) { + findConnectionForAction("callId", "setCallCapabilities") + .setCallCapabilities(callCapabilities); } @Override - public void setIsConferenced(String connectionId, String conferenceConnectionId) { + public void setIsConferenced(String callId, String conferenceCallId) { // not supported for remote connections. } @Override - public void addConferenceCall(String connectionId) { + public void addConferenceCall(String callId) { // not supported for remote connections. } @Override - public void removeCall(String connectionId) { - mHandler.obtainMessage(MSG_REMOVE_CALL, connectionId).sendToTarget(); + public void removeCall(String callId) { + findConnectionForAction(callId, "removeCall") + .setDestroyed(); } @Override - public void onPostDialWait(String connectionId, String remainingDigits) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = connectionId; - args.arg2 = remainingDigits; - mHandler.obtainMessage(MSG_ON_POST_DIAL_WAIT, args).sendToTarget(); + public void onPostDialWait(String callId, String remaining) { + findConnectionForAction(callId, "onPostDialWait") + .setPostDialWait(remaining); } @Override public void queryRemoteConnectionServices(RemoteServiceCallback callback) { - try { - // Not supported from remote connection service. - callback.onError(); - } catch (RemoteException e) { - } + // Not supported from remote connection service. } @Override - public void setVideoState(String connectionId, int videoState) { - mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, connectionId).sendToTarget(); + public void setVideoCallProvider(String callId, + IVideoCallProvider videoCallProvider) { + // not supported for remote connections. } @Override - public void setVideoCallProvider( - String connectionId, IVideoCallProvider videoCallProvider) { - // not supported for remote connections. + public void setVideoState(String callId, int videoState) { + findConnectionForAction(callId, "setVideoState") + .setVideoState(videoState); + } + + @Override + public void setAudioModeIsVoip(String callId, boolean isVoip) { + findConnectionForAction(callId, "setAudioModeIsVoip") + .setAudioModeIsVoip(isVoip); } @Override - public final void setAudioModeIsVoip(String connectionId, boolean isVoip) { - mHandler.obtainMessage(MSG_SET_AUDIO_MODE_IS_VOIP, isVoip ? 1 : 0, 0, - connectionId).sendToTarget(); + public void setStatusHints(String callId, StatusHints statusHints) { + findConnectionForAction(callId, "setStatusHints") + .setStatusHints(statusHints); } @Override - public final void setStatusHints(String connectionId, StatusHints statusHints) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = connectionId; - args.arg2 = statusHints; - mHandler.obtainMessage(MSG_SET_STATUS_HINTS, args).sendToTarget(); + public void setHandle(String callId, Uri handle, int presentation) { + findConnectionForAction(callId, "setHandle") + .setHandle(handle, presentation); } @Override - public final void setHandle(String connectionId, Uri handle, int presentation) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = connectionId; - args.arg2 = handle; - args.argi1 = presentation; - mHandler.obtainMessage(MSG_SET_HANDLE, args).sendToTarget(); + public void setCallerDisplayName(String callId, String callerDisplayName, + int presentation) { + findConnectionForAction(callId, "setCallerDisplayName") + .setCallerDisplayName(callerDisplayName, presentation); } @Override - public final void setCallerDisplayName( - String connectionId, String callerDisplayName, int presentation) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = connectionId; - args.arg2 = callerDisplayName; - args.argi1 = presentation; - mHandler.obtainMessage(MSG_SET_CALLER_DISPLAY_NAME, args).sendToTarget(); + public void startActivityFromInCall(String callId, PendingIntent intent) { + findConnectionForAction(callId, "startActivityFromInCall") + .startActivityFromInCall(intent); } @Override - public final void startActivityFromInCall(String connectionId, PendingIntent intent) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = connectionId; - args.arg2 = intent; - mHandler.obtainMessage(MSG_START_ACTIVITY_FROM_IN_CALL, args).sendToTarget(); + public IBinder asBinder() { + throw new UnsupportedOperationException(); } }; - RemoteConnectionService(ComponentName componentName, IConnectionService connectionService) - throws RemoteException { - mComponentName = componentName; - mConnectionService = connectionService; + private final ConnectionServiceAdapterServant mServant = + new ConnectionServiceAdapterServant(mServantDelegate); + + private final DeathRecipient mDeathRecipient = new DeathRecipient() { + @Override + public void binderDied() { + for (RemoteConnection c : mConnectionById.values()) { + c.setDestroyed(); + } + mConnectionById.clear(); + mPendingConnections.clear(); + mConnectionService.asBinder().unlinkToDeath(mDeathRecipient, 0); + } + }; - mConnectionService.addConnectionServiceAdapter(mAdapter); - mConnectionService.asBinder().linkToDeath(this, 0); + private final IConnectionService mConnectionService; + private final Map<String, RemoteConnection> mConnectionById = new HashMap<>(); + private final Set<RemoteConnection> mPendingConnections = new HashSet<>(); + + RemoteConnectionService(IConnectionService connectionService) throws RemoteException { + mConnectionService = connectionService; + mConnectionService.addConnectionServiceAdapter(mServant.getStub()); + mConnectionService.asBinder().linkToDeath(mDeathRecipient, 0); } @Override @@ -409,66 +227,37 @@ final class RemoteConnectionService implements DeathRecipient { return "[RemoteCS - " + mConnectionService.asBinder().toString() + "]"; } - /** ${inheritDoc} */ - @Override - public final void binderDied() { - if (mConnection != null) { - destroyConnection(); - } - - release(); - } - final RemoteConnection createRemoteConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request, boolean isIncoming) { - if (mConnectionId == null) { - String id = UUID.randomUUID().toString(); - ConnectionRequest newRequest = new ConnectionRequest( - request.getAccountHandle(), - id, - request.getHandle(), - request.getHandlePresentation(), - request.getExtras(), - request.getVideoState()); - mConnection = new RemoteConnection(mConnectionService, request, isIncoming); - try { - mConnectionService.createConnection( - connectionManagerPhoneAccount, - newRequest, - isIncoming); - mConnectionId = id; - } catch (RemoteException e) { - mConnection = RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED, - e.toString()); - } - return mConnection; - } else { - return RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED, null); + ConnectionRequest newRequest = new ConnectionRequest( + request.getAccountHandle(), + UUID.randomUUID().toString(), + request.getHandle(), + request.getHandlePresentation(), + request.getExtras(), + request.getVideoState()); + try { + mConnectionService.createConnection( + connectionManagerPhoneAccount, + newRequest, + isIncoming); + RemoteConnection connection = + new RemoteConnection(mConnectionService, request); + mPendingConnections.add(connection); + mConnectionById.put(newRequest.getCallId(), connection); + return connection; + } catch (RemoteException e) { + return RemoteConnection.failure(DisconnectCause.ERROR_UNSPECIFIED, e.toString()); } } - /** - * Releases the resources associated with this Remote connection service. Should be called when - * the remote service is no longer being used. - */ - void release() { - mConnectionService.asBinder().unlinkToDeath(this, 0); - } - - private boolean isPendingConnection(String id) { - return TextUtils.equals(mConnectionId, id); - } - - private boolean isCurrentConnection(Object obj) { - return obj instanceof String && mConnection != null && - TextUtils.equals(mConnectionId, (String) obj); - } - - private void destroyConnection() { - mConnection.setDestroyed(); - mConnection = null; - mConnectionId = null; + private RemoteConnection findConnectionForAction(String callId, String action) { + if (mConnectionById.containsKey(callId)) { + return mConnectionById.get(callId); + } + Log.w(this, "%s - Cannot find Connection %s", action, callId); + return NULL_CONNECTION; } } |