diff options
Diffstat (limited to 'telecomm/java/android/telecom/RemoteConnectionService.java')
-rw-r--r-- | telecomm/java/android/telecom/RemoteConnectionService.java | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java new file mode 100644 index 0000000..af4ee22 --- /dev/null +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecom; + +import android.net.Uri; +import android.os.IBinder; +import android.os.IBinder.DeathRecipient; +import android.os.RemoteException; + +import com.android.internal.telecom.IConnectionService; +import com.android.internal.telecom.IConnectionServiceAdapter; +import com.android.internal.telecom.IVideoProvider; +import com.android.internal.telecom.RemoteServiceCallback; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.UUID; + +/** + * Remote connection service which other connection services can use to place calls on their behalf. + * + * @hide + */ +final class RemoteConnectionService { + + private static final RemoteConnection NULL_CONNECTION = + new RemoteConnection("NULL", null, null); + + private static final RemoteConference NULL_CONFERENCE = + new RemoteConference("NULL", null); + + private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() { + @Override + public void handleCreateConnectionComplete( + String id, + ConnectionRequest request, + ParcelableConnection parcel) { + RemoteConnection connection = + findConnectionForAction(id, "handleCreateConnectionSuccessful"); + if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) { + mPendingConnections.remove(connection); + // Unconditionally initialize the connection ... + connection.setCallCapabilities(parcel.getCapabilities()); + connection.setAddress( + parcel.getHandle(), parcel.getHandlePresentation()); + connection.setCallerDisplayName( + parcel.getCallerDisplayName(), + parcel.getCallerDisplayNamePresentation()); + // Set state after handle so that the client can identify the connection. + if (parcel.getState() == Connection.STATE_DISCONNECTED) { + connection.setDisconnected(parcel.getDisconnectCause()); + } else { + connection.setState(parcel.getState()); + } + List<RemoteConnection> conferenceable = new ArrayList<>(); + for (String confId : parcel.getConferenceableConnectionIds()) { + if (mConnectionById.containsKey(confId)) { + conferenceable.add(mConnectionById.get(confId)); + } + } + connection.setConferenceableConnections(conferenceable); + connection.setVideoState(parcel.getVideoState()); + if (connection.getState() == Connection.STATE_DISCONNECTED) { + // ... then, if it was created in a disconnected state, that indicates + // failure on the providing end, so immediately mark it destroyed + connection.setDestroyed(); + } + } + } + + @Override + public void setActive(String callId) { + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setActive") + .setState(Connection.STATE_ACTIVE); + } else { + findConferenceForAction(callId, "setActive") + .setState(Connection.STATE_ACTIVE); + } + } + + @Override + public void setRinging(String callId) { + findConnectionForAction(callId, "setRinging") + .setState(Connection.STATE_RINGING); + } + + @Override + public void setDialing(String callId) { + findConnectionForAction(callId, "setDialing") + .setState(Connection.STATE_DIALING); + } + + @Override + public void setDisconnected(String callId, DisconnectCause disconnectCause) { + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setDisconnected") + .setDisconnected(disconnectCause); + } else { + findConferenceForAction(callId, "setDisconnected") + .setDisconnected(disconnectCause); + } + } + + @Override + public void setOnHold(String callId) { + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setOnHold") + .setState(Connection.STATE_HOLDING); + } else { + findConferenceForAction(callId, "setOnHold") + .setState(Connection.STATE_HOLDING); + } + } + + @Override + public void setRingbackRequested(String callId, boolean ringing) { + findConnectionForAction(callId, "setRingbackRequested") + .setRingbackRequested(ringing); + } + + @Override + public void setCallCapabilities(String callId, int callCapabilities) { + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "setCallCapabilities") + .setCallCapabilities(callCapabilities); + } else { + findConferenceForAction(callId, "setCallCapabilities") + .setCallCapabilities(callCapabilities); + } + } + + @Override + public void setIsConferenced(String callId, String conferenceCallId) { + // Note: callId should not be null; conferenceCallId may be null + RemoteConnection connection = + findConnectionForAction(callId, "setIsConferenced"); + if (connection != NULL_CONNECTION) { + if (conferenceCallId == null) { + // 'connection' is being split from its conference + if (connection.getConference() != null) { + connection.getConference().removeConnection(connection); + } + } else { + RemoteConference conference = + findConferenceForAction(conferenceCallId, "setIsConferenced"); + if (conference != NULL_CONFERENCE) { + conference.addConnection(connection); + } + } + } + } + + @Override + public void addConferenceCall( + final String callId, + ParcelableConference parcel) { + RemoteConference conference = new RemoteConference(callId, + mOutgoingConnectionServiceRpc); + + for (String id : parcel.getConnectionIds()) { + RemoteConnection c = mConnectionById.get(id); + if (c != null) { + conference.addConnection(c); + } + } + + if (conference.getConnections().size() == 0) { + // A conference was created, but none of its connections are ones that have been + // created by, and therefore being tracked by, this remote connection service. It + // is of no interest to us. + return; + } + + conference.setState(parcel.getState()); + conference.setCallCapabilities(parcel.getCapabilities()); + mConferenceById.put(callId, conference); + conference.registerCallback(new RemoteConference.Callback() { + @Override + public void onDestroyed(RemoteConference c) { + mConferenceById.remove(callId); + maybeDisconnectAdapter(); + } + }); + + mOurConnectionServiceImpl.addRemoteConference(conference); + } + + @Override + public void removeCall(String callId) { + if (mConnectionById.containsKey(callId)) { + findConnectionForAction(callId, "removeCall") + .setDestroyed(); + } else { + findConferenceForAction(callId, "removeCall") + .setDestroyed(); + } + } + + @Override + public void onPostDialWait(String callId, String remaining) { + findConnectionForAction(callId, "onPostDialWait") + .setPostDialWait(remaining); + } + + @Override + public void queryRemoteConnectionServices(RemoteServiceCallback callback) { + // Not supported from remote connection service. + } + + @Override + public void setVideoProvider(String callId, IVideoProvider videoProvider) { + findConnectionForAction(callId, "setVideoProvider") + .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider)); + } + + @Override + public void setVideoState(String callId, int videoState) { + findConnectionForAction(callId, "setVideoState") + .setVideoState(videoState); + } + + @Override + public void setIsVoipAudioMode(String callId, boolean isVoip) { + findConnectionForAction(callId, "setIsVoipAudioMode") + .setIsVoipAudioMode(isVoip); + } + + @Override + public void setStatusHints(String callId, StatusHints statusHints) { + findConnectionForAction(callId, "setStatusHints") + .setStatusHints(statusHints); + } + + @Override + public void setAddress(String callId, Uri address, int presentation) { + findConnectionForAction(callId, "setAddress") + .setAddress(address, presentation); + } + + @Override + public void setCallerDisplayName(String callId, String callerDisplayName, + int presentation) { + findConnectionForAction(callId, "setCallerDisplayName") + .setCallerDisplayName(callerDisplayName, presentation); + } + + @Override + public IBinder asBinder() { + throw new UnsupportedOperationException(); + } + + @Override + public final void setConferenceableConnections( + String callId, List<String> conferenceableConnectionIds) { + List<RemoteConnection> conferenceable = new ArrayList<>(); + for (String id : conferenceableConnectionIds) { + if (mConnectionById.containsKey(id)) { + conferenceable.add(mConnectionById.get(id)); + } + } + + if (hasConnection(callId)) { + findConnectionForAction(callId, "setConferenceableConnections") + .setConferenceableConnections(conferenceable); + } else { + findConferenceForAction(callId, "setConferenceableConnections") + .setConferenceableConnections(conferenceable); + } + } + }; + + private final ConnectionServiceAdapterServant mServant = + new ConnectionServiceAdapterServant(mServantDelegate); + + private final DeathRecipient mDeathRecipient = new DeathRecipient() { + @Override + public void binderDied() { + for (RemoteConnection c : mConnectionById.values()) { + c.setDestroyed(); + } + for (RemoteConference c : mConferenceById.values()) { + c.setDestroyed(); + } + mConnectionById.clear(); + mConferenceById.clear(); + mPendingConnections.clear(); + mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0); + } + }; + + private final IConnectionService mOutgoingConnectionServiceRpc; + private final ConnectionService mOurConnectionServiceImpl; + private final Map<String, RemoteConnection> mConnectionById = new HashMap<>(); + private final Map<String, RemoteConference> mConferenceById = new HashMap<>(); + private final Set<RemoteConnection> mPendingConnections = new HashSet<>(); + + RemoteConnectionService( + IConnectionService outgoingConnectionServiceRpc, + ConnectionService ourConnectionServiceImpl) throws RemoteException { + mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc; + mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0); + mOurConnectionServiceImpl = ourConnectionServiceImpl; + } + + @Override + public String toString() { + return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]"; + } + + final RemoteConnection createRemoteConnection( + PhoneAccountHandle connectionManagerPhoneAccount, + ConnectionRequest request, + boolean isIncoming) { + final String id = UUID.randomUUID().toString(); + final ConnectionRequest newRequest = new ConnectionRequest( + request.getAccountHandle(), + request.getAddress(), + request.getExtras(), + request.getVideoState()); + try { + if (mConnectionById.isEmpty()) { + mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub()); + } + RemoteConnection connection = + new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest); + mPendingConnections.add(connection); + mConnectionById.put(id, connection); + mOutgoingConnectionServiceRpc.createConnection( + connectionManagerPhoneAccount, + id, + newRequest, + isIncoming, + false /* isUnknownCall */); + connection.registerCallback(new RemoteConnection.Callback() { + @Override + public void onDestroyed(RemoteConnection connection) { + mConnectionById.remove(id); + maybeDisconnectAdapter(); + } + }); + return connection; + } catch (RemoteException e) { + return RemoteConnection.failure( + new DisconnectCause(DisconnectCause.ERROR, e.toString())); + } + } + + private boolean hasConnection(String callId) { + return mConnectionById.containsKey(callId); + } + + 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; + } + + private RemoteConference findConferenceForAction( + String callId, String action) { + if (mConferenceById.containsKey(callId)) { + return mConferenceById.get(callId); + } + Log.w(this, "%s - Cannot find Conference %s", action, callId); + return NULL_CONFERENCE; + } + + private void maybeDisconnectAdapter() { + if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) { + try { + mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub()); + } catch (RemoteException e) { + } + } + } +} |