blob: 03b38c2aaaad903a276e0546081d6f9a34b070d6 [file] [log] [blame]
Santos Cordon52d8a152014-06-17 19:08:45 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
Ihab Awadb19a0bc2014-08-07 19:46:01 -070014 * limitations under the License.
Santos Cordon52d8a152014-06-17 19:08:45 -070015 */
16
Tyler Gunnef9f6f92014-09-12 22:16:17 -070017package android.telecom;
Santos Cordon52d8a152014-06-17 19:08:45 -070018
Santos Cordon52d8a152014-06-17 19:08:45 -070019import android.net.Uri;
Ihab Awad5d0410f2014-07-30 10:07:40 -070020import android.os.IBinder;
Santos Cordone8dc4be2014-07-21 01:28:28 -070021import android.os.IBinder.DeathRecipient;
Santos Cordon52d8a152014-06-17 19:08:45 -070022import android.os.RemoteException;
Santos Cordon52d8a152014-06-17 19:08:45 -070023
Tyler Gunnef9f6f92014-09-12 22:16:17 -070024import com.android.internal.telecom.IConnectionService;
25import com.android.internal.telecom.IConnectionServiceAdapter;
26import com.android.internal.telecom.IVideoProvider;
27import com.android.internal.telecom.RemoteServiceCallback;
Santos Cordon52d8a152014-06-17 19:08:45 -070028
Ihab Awadb8e85c72014-08-23 20:34:57 -070029import java.util.ArrayList;
Ihab Awad5d0410f2014-07-30 10:07:40 -070030import java.util.HashMap;
31import java.util.HashSet;
32import java.util.Map;
33import java.util.Set;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -070034import java.util.List;
Santos Cordon52d8a152014-06-17 19:08:45 -070035import java.util.UUID;
36
37/**
38 * Remote connection service which other connection services can use to place calls on their behalf.
Sailesh Nepal091768c2014-06-30 15:15:23 -070039 *
40 * @hide
Santos Cordon52d8a152014-06-17 19:08:45 -070041 */
Ihab Awad5d0410f2014-07-30 10:07:40 -070042final class RemoteConnectionService {
Sailesh Nepal48031592014-07-18 14:21:23 -070043
Ihab Awadb8e85c72014-08-23 20:34:57 -070044 private static final RemoteConnection NULL_CONNECTION =
45 new RemoteConnection("NULL", null, null);
46
47 private static final RemoteConference NULL_CONFERENCE =
48 new RemoteConference("NULL", null);
Santos Cordon52d8a152014-06-17 19:08:45 -070049
Ihab Awad5d0410f2014-07-30 10:07:40 -070050 private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() {
Sailesh Nepal48031592014-07-18 14:21:23 -070051 @Override
Ihab Awad6107bab2014-08-18 09:23:25 -070052 public void handleCreateConnectionComplete(
Ihab Awadb19a0bc2014-08-07 19:46:01 -070053 String id,
54 ConnectionRequest request,
Ihab Awad5d0410f2014-07-30 10:07:40 -070055 ParcelableConnection parcel) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -070056 RemoteConnection connection =
57 findConnectionForAction(id, "handleCreateConnectionSuccessful");
Ihab Awad5d0410f2014-07-30 10:07:40 -070058 if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
59 mPendingConnections.remove(connection);
Ihab Awad6107bab2014-08-18 09:23:25 -070060 // Unconditionally initialize the connection ...
Ihab Awad5d0410f2014-07-30 10:07:40 -070061 connection.setCallCapabilities(parcel.getCapabilities());
Andrew Lee100e2932014-09-08 15:34:24 -070062 connection.setAddress(
Ihab Awad5d0410f2014-07-30 10:07:40 -070063 parcel.getHandle(), parcel.getHandlePresentation());
64 connection.setCallerDisplayName(
65 parcel.getCallerDisplayName(),
66 parcel.getCallerDisplayNamePresentation());
Sailesh Nepal70638f12014-09-09 21:49:14 -070067 // Set state after handle so that the client can identify the connection.
68 connection.setState(parcel.getState());
Ihab Awadb8e85c72014-08-23 20:34:57 -070069 List<RemoteConnection> conferenceable = new ArrayList<>();
70 for (String confId : parcel.getConferenceableConnectionIds()) {
71 if (mConnectionById.containsKey(confId)) {
72 conferenceable.add(mConnectionById.get(confId));
73 }
74 }
75 connection.setConferenceableConnections(conferenceable);
Ihab Awada64627c2014-08-20 09:36:40 -070076 connection.setVideoState(parcel.getVideoState());
Ihab Awad6107bab2014-08-18 09:23:25 -070077 if (connection.getState() == Connection.STATE_DISCONNECTED) {
78 // ... then, if it was created in a disconnected state, that indicates
79 // failure on the providing end, so immediately mark it destroyed
80 connection.setDestroyed();
81 }
Sailesh Nepal48031592014-07-18 14:21:23 -070082 }
83 }
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -070084
Sailesh Nepal2a46b902014-07-04 17:21:07 -070085 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -070086 public void setActive(String callId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -070087 if (mConnectionById.containsKey(callId)) {
88 findConnectionForAction(callId, "setActive")
89 .setState(Connection.STATE_ACTIVE);
90 } else {
91 findConferenceForAction(callId, "setActive")
92 .setState(Connection.STATE_ACTIVE);
93 }
Santos Cordon52d8a152014-06-17 19:08:45 -070094 }
95
Sailesh Nepal2a46b902014-07-04 17:21:07 -070096 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -070097 public void setRinging(String callId) {
98 findConnectionForAction(callId, "setRinging")
Ihab Awadb19a0bc2014-08-07 19:46:01 -070099 .setState(Connection.STATE_RINGING);
Andrew Lee5ffbe8b2014-06-20 16:29:33 -0700100 }
101
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700102 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700103 public void setDialing(String callId) {
104 findConnectionForAction(callId, "setDialing")
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700105 .setState(Connection.STATE_DIALING);
Santos Cordon52d8a152014-06-17 19:08:45 -0700106 }
107
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700108 @Override
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700109 public void setDisconnected(String callId, DisconnectCause disconnectCause) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700110 if (mConnectionById.containsKey(callId)) {
111 findConnectionForAction(callId, "setDisconnected")
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700112 .setDisconnected(disconnectCause);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700113 } else {
114 findConferenceForAction(callId, "setDisconnected")
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700115 .setDisconnected(disconnectCause);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700116 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700117 }
118
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700119 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700120 public void setOnHold(String callId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700121 if (mConnectionById.containsKey(callId)) {
122 findConnectionForAction(callId, "setOnHold")
123 .setState(Connection.STATE_HOLDING);
124 } else {
125 findConferenceForAction(callId, "setOnHold")
126 .setState(Connection.STATE_HOLDING);
127 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700128 }
129
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700130 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700131 public void setRingbackRequested(String callId, boolean ringing) {
132 findConnectionForAction(callId, "setRingbackRequested")
133 .setRingbackRequested(ringing);
Santos Cordon52d8a152014-06-17 19:08:45 -0700134 }
135
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700136 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700137 public void setCallCapabilities(String callId, int callCapabilities) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700138 if (mConnectionById.containsKey(callId)) {
139 findConnectionForAction(callId, "setCallCapabilities")
140 .setCallCapabilities(callCapabilities);
141 } else {
142 findConferenceForAction(callId, "setCallCapabilities")
143 .setCallCapabilities(callCapabilities);
144 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700145 }
146
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700147 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700148 public void setIsConferenced(String callId, String conferenceCallId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700149 // Note: callId should not be null; conferenceCallId may be null
150 RemoteConnection connection =
151 findConnectionForAction(callId, "setIsConferenced");
152 if (connection != NULL_CONNECTION) {
153 if (conferenceCallId == null) {
154 // 'connection' is being split from its conference
155 if (connection.getConference() != null) {
156 connection.getConference().removeConnection(connection);
157 }
158 } else {
159 RemoteConference conference =
160 findConferenceForAction(conferenceCallId, "setIsConferenced");
161 if (conference != NULL_CONFERENCE) {
162 conference.addConnection(connection);
163 }
164 }
165 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700166 }
167
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700168 @Override
Ihab Awadb8e85c72014-08-23 20:34:57 -0700169 public void addConferenceCall(
170 final String callId,
171 ParcelableConference parcel) {
172 RemoteConference conference = new RemoteConference(callId,
173 mOutgoingConnectionServiceRpc);
174
175 for (String id : parcel.getConnectionIds()) {
176 RemoteConnection c = mConnectionById.get(id);
177 if (c != null) {
178 conference.addConnection(c);
179 }
180 }
181
182 if (conference.getConnections().size() == 0) {
183 // A conference was created, but none of its connections are ones that have been
184 // created by, and therefore being tracked by, this remote connection service. It
185 // is of no interest to us.
186 return;
187 }
188
189 conference.setState(parcel.getState());
190 conference.setCallCapabilities(parcel.getCapabilities());
191 mConferenceById.put(callId, conference);
Andrew Lee100e2932014-09-08 15:34:24 -0700192 conference.registerCallback(new RemoteConference.Callback() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700193 @Override
194 public void onDestroyed(RemoteConference c) {
195 mConferenceById.remove(callId);
196 maybeDisconnectAdapter();
197 }
198 });
199
200 mOurConnectionServiceImpl.addRemoteConference(conference);
Santos Cordon52d8a152014-06-17 19:08:45 -0700201 }
202
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700203 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700204 public void removeCall(String callId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700205 if (mConnectionById.containsKey(callId)) {
206 findConnectionForAction(callId, "removeCall")
207 .setDestroyed();
208 } else {
209 findConferenceForAction(callId, "removeCall")
210 .setDestroyed();
211 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700212 }
213
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700214 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700215 public void onPostDialWait(String callId, String remaining) {
216 findConnectionForAction(callId, "onPostDialWait")
217 .setPostDialWait(remaining);
Santos Cordon52d8a152014-06-17 19:08:45 -0700218 }
219
Santos Cordon52d8a152014-06-17 19:08:45 -0700220 @Override
221 public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
Ihab Awad5d0410f2014-07-30 10:07:40 -0700222 // Not supported from remote connection service.
Santos Cordon52d8a152014-06-17 19:08:45 -0700223 }
Tyler Gunn8d83fa92014-07-01 11:31:21 -0700224
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700225 @Override
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700226 public void setVideoProvider(String callId, IVideoProvider videoProvider) {
Ihab Awada64627c2014-08-20 09:36:40 -0700227 findConnectionForAction(callId, "setVideoProvider")
228 .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider));
Tyler Gunnaa07df82014-07-17 07:50:22 -0700229 }
230
231 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700232 public void setVideoState(String callId, int videoState) {
233 findConnectionForAction(callId, "setVideoState")
234 .setVideoState(videoState);
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700235 }
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700236
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700237 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700238 public void setIsVoipAudioMode(String callId, boolean isVoip) {
239 findConnectionForAction(callId, "setIsVoipAudioMode")
240 .setIsVoipAudioMode(isVoip);
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700241 }
Sailesh Nepal61203862014-07-11 14:50:13 -0700242
243 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700244 public void setStatusHints(String callId, StatusHints statusHints) {
245 findConnectionForAction(callId, "setStatusHints")
246 .setStatusHints(statusHints);
Sailesh Nepal61203862014-07-11 14:50:13 -0700247 }
248
249 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700250 public void setAddress(String callId, Uri address, int presentation) {
251 findConnectionForAction(callId, "setAddress")
252 .setAddress(address, presentation);
Sailesh Nepal61203862014-07-11 14:50:13 -0700253 }
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -0700254
255 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700256 public void setCallerDisplayName(String callId, String callerDisplayName,
257 int presentation) {
258 findConnectionForAction(callId, "setCallerDisplayName")
259 .setCallerDisplayName(callerDisplayName, presentation);
260 }
261
262 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700263 public IBinder asBinder() {
264 throw new UnsupportedOperationException();
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -0700265 }
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700266
267 @Override
268 public final void setConferenceableConnections(
269 String callId, List<String> conferenceableConnectionIds) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700270 List<RemoteConnection> conferenceable = new ArrayList<>();
271 for (String id : conferenceableConnectionIds) {
272 if (mConnectionById.containsKey(id)) {
273 conferenceable.add(mConnectionById.get(id));
274 }
275 }
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700276
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700277 findConnectionForAction(callId, "setConferenceableConnections")
Ihab Awadb8e85c72014-08-23 20:34:57 -0700278 .setConferenceableConnections(conferenceable);
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700279 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700280 };
281
Ihab Awad5d0410f2014-07-30 10:07:40 -0700282 private final ConnectionServiceAdapterServant mServant =
283 new ConnectionServiceAdapterServant(mServantDelegate);
Santos Cordon52d8a152014-06-17 19:08:45 -0700284
Ihab Awad5d0410f2014-07-30 10:07:40 -0700285 private final DeathRecipient mDeathRecipient = new DeathRecipient() {
286 @Override
287 public void binderDied() {
288 for (RemoteConnection c : mConnectionById.values()) {
289 c.setDestroyed();
290 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700291 for (RemoteConference c : mConferenceById.values()) {
292 c.setDestroyed();
293 }
Ihab Awad5d0410f2014-07-30 10:07:40 -0700294 mConnectionById.clear();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700295 mConferenceById.clear();
Ihab Awad5d0410f2014-07-30 10:07:40 -0700296 mPendingConnections.clear();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700297 mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700298 }
299 };
300
Ihab Awadb8e85c72014-08-23 20:34:57 -0700301 private final IConnectionService mOutgoingConnectionServiceRpc;
302 private final ConnectionService mOurConnectionServiceImpl;
Ihab Awad5d0410f2014-07-30 10:07:40 -0700303 private final Map<String, RemoteConnection> mConnectionById = new HashMap<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700304 private final Map<String, RemoteConference> mConferenceById = new HashMap<>();
Ihab Awad5d0410f2014-07-30 10:07:40 -0700305 private final Set<RemoteConnection> mPendingConnections = new HashSet<>();
306
Ihab Awadb8e85c72014-08-23 20:34:57 -0700307 RemoteConnectionService(
308 IConnectionService outgoingConnectionServiceRpc,
309 ConnectionService ourConnectionServiceImpl) throws RemoteException {
310 mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc;
311 mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0);
312 mOurConnectionServiceImpl = ourConnectionServiceImpl;
Santos Cordon52d8a152014-06-17 19:08:45 -0700313 }
314
315 @Override
316 public String toString() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700317 return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]";
Santos Cordon52d8a152014-06-17 19:08:45 -0700318 }
319
Ihab Awadf8b69882014-07-25 15:14:01 -0700320 final RemoteConnection createRemoteConnection(
321 PhoneAccountHandle connectionManagerPhoneAccount,
322 ConnectionRequest request,
323 boolean isIncoming) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700324 final String id = UUID.randomUUID().toString();
Ihab Awad8aecfed2014-08-08 17:06:11 -0700325 final ConnectionRequest newRequest = new ConnectionRequest(
Ihab Awad5d0410f2014-07-30 10:07:40 -0700326 request.getAccountHandle(),
Nancy Chenea38cca2014-09-05 16:38:49 -0700327 request.getAddress(),
Ihab Awad5d0410f2014-07-30 10:07:40 -0700328 request.getExtras(),
329 request.getVideoState());
330 try {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700331 if (mConnectionById.isEmpty()) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700332 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub());
Ihab Awad8aecfed2014-08-08 17:06:11 -0700333 }
334 RemoteConnection connection =
Ihab Awadb8e85c72014-08-23 20:34:57 -0700335 new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest);
Ihab Awad8aecfed2014-08-08 17:06:11 -0700336 mPendingConnections.add(connection);
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700337 mConnectionById.put(id, connection);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700338 mOutgoingConnectionServiceRpc.createConnection(
Ihab Awad5d0410f2014-07-30 10:07:40 -0700339 connectionManagerPhoneAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700340 id,
Ihab Awad5d0410f2014-07-30 10:07:40 -0700341 newRequest,
342 isIncoming);
Andrew Lee100e2932014-09-08 15:34:24 -0700343 connection.registerCallback(new RemoteConnection.Callback() {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700344 @Override
345 public void onDestroyed(RemoteConnection connection) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700346 mConnectionById.remove(id);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700347 maybeDisconnectAdapter();
Ihab Awad8aecfed2014-08-08 17:06:11 -0700348 }
349 });
Ihab Awad5d0410f2014-07-30 10:07:40 -0700350 return connection;
351 } catch (RemoteException e) {
Andrew Lee7f3d41f2014-09-11 17:33:16 -0700352 return RemoteConnection.failure(
353 new DisconnectCause(DisconnectCause.ERROR, e.toString()));
Santos Cordon52d8a152014-06-17 19:08:45 -0700354 }
355 }
356
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700357 private RemoteConnection findConnectionForAction(
358 String callId, String action) {
Ihab Awad5d0410f2014-07-30 10:07:40 -0700359 if (mConnectionById.containsKey(callId)) {
360 return mConnectionById.get(callId);
361 }
362 Log.w(this, "%s - Cannot find Connection %s", action, callId);
363 return NULL_CONNECTION;
Santos Cordon52d8a152014-06-17 19:08:45 -0700364 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700365
366 private RemoteConference findConferenceForAction(
367 String callId, String action) {
368 if (mConferenceById.containsKey(callId)) {
369 return mConferenceById.get(callId);
370 }
371 Log.w(this, "%s - Cannot find Conference %s", action, callId);
372 return NULL_CONFERENCE;
373 }
374
375 private void maybeDisconnectAdapter() {
376 if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) {
377 try {
378 mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub());
379 } catch (RemoteException e) {
380 }
381 }
382 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700383}