blob: bfd7c51658a55b86a2cd1915f8d8240b85de22a2 [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;
23import android.telephony.DisconnectCause;
Santos Cordon52d8a152014-06-17 19:08:45 -070024
Tyler Gunnef9f6f92014-09-12 22:16:17 -070025import com.android.internal.telecom.IConnectionService;
26import com.android.internal.telecom.IConnectionServiceAdapter;
27import com.android.internal.telecom.IVideoProvider;
28import com.android.internal.telecom.RemoteServiceCallback;
Santos Cordon52d8a152014-06-17 19:08:45 -070029
Ihab Awadb8e85c72014-08-23 20:34:57 -070030import java.util.ArrayList;
Ihab Awad5d0410f2014-07-30 10:07:40 -070031import java.util.HashMap;
32import java.util.HashSet;
33import java.util.Map;
34import java.util.Set;
Santos Cordon7c7bc7f2014-07-28 18:15:48 -070035import java.util.List;
Santos Cordon52d8a152014-06-17 19:08:45 -070036import java.util.UUID;
37
38/**
39 * Remote connection service which other connection services can use to place calls on their behalf.
Sailesh Nepal091768c2014-06-30 15:15:23 -070040 *
41 * @hide
Santos Cordon52d8a152014-06-17 19:08:45 -070042 */
Ihab Awad5d0410f2014-07-30 10:07:40 -070043final class RemoteConnectionService {
Sailesh Nepal48031592014-07-18 14:21:23 -070044
Ihab Awadb8e85c72014-08-23 20:34:57 -070045 private static final RemoteConnection NULL_CONNECTION =
46 new RemoteConnection("NULL", null, null);
47
48 private static final RemoteConference NULL_CONFERENCE =
49 new RemoteConference("NULL", null);
Santos Cordon52d8a152014-06-17 19:08:45 -070050
Ihab Awad5d0410f2014-07-30 10:07:40 -070051 private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() {
Sailesh Nepal48031592014-07-18 14:21:23 -070052 @Override
Ihab Awad6107bab2014-08-18 09:23:25 -070053 public void handleCreateConnectionComplete(
Ihab Awadb19a0bc2014-08-07 19:46:01 -070054 String id,
55 ConnectionRequest request,
Ihab Awad5d0410f2014-07-30 10:07:40 -070056 ParcelableConnection parcel) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -070057 RemoteConnection connection =
58 findConnectionForAction(id, "handleCreateConnectionSuccessful");
Ihab Awad5d0410f2014-07-30 10:07:40 -070059 if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
60 mPendingConnections.remove(connection);
Ihab Awad6107bab2014-08-18 09:23:25 -070061 // Unconditionally initialize the connection ...
Ihab Awad5d0410f2014-07-30 10:07:40 -070062 connection.setCallCapabilities(parcel.getCapabilities());
Andrew Lee100e2932014-09-08 15:34:24 -070063 connection.setAddress(
Ihab Awad5d0410f2014-07-30 10:07:40 -070064 parcel.getHandle(), parcel.getHandlePresentation());
65 connection.setCallerDisplayName(
66 parcel.getCallerDisplayName(),
67 parcel.getCallerDisplayNamePresentation());
Sailesh Nepal70638f12014-09-09 21:49:14 -070068 // Set state after handle so that the client can identify the connection.
69 connection.setState(parcel.getState());
Ihab Awadb8e85c72014-08-23 20:34:57 -070070 List<RemoteConnection> conferenceable = new ArrayList<>();
71 for (String confId : parcel.getConferenceableConnectionIds()) {
72 if (mConnectionById.containsKey(confId)) {
73 conferenceable.add(mConnectionById.get(confId));
74 }
75 }
76 connection.setConferenceableConnections(conferenceable);
Ihab Awada64627c2014-08-20 09:36:40 -070077 connection.setVideoState(parcel.getVideoState());
Ihab Awad6107bab2014-08-18 09:23:25 -070078 if (connection.getState() == Connection.STATE_DISCONNECTED) {
79 // ... then, if it was created in a disconnected state, that indicates
80 // failure on the providing end, so immediately mark it destroyed
81 connection.setDestroyed();
82 }
Sailesh Nepal48031592014-07-18 14:21:23 -070083 }
84 }
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -070085
Sailesh Nepal2a46b902014-07-04 17:21:07 -070086 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -070087 public void setActive(String callId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -070088 if (mConnectionById.containsKey(callId)) {
89 findConnectionForAction(callId, "setActive")
90 .setState(Connection.STATE_ACTIVE);
91 } else {
92 findConferenceForAction(callId, "setActive")
93 .setState(Connection.STATE_ACTIVE);
94 }
Santos Cordon52d8a152014-06-17 19:08:45 -070095 }
96
Sailesh Nepal2a46b902014-07-04 17:21:07 -070097 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -070098 public void setRinging(String callId) {
99 findConnectionForAction(callId, "setRinging")
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700100 .setState(Connection.STATE_RINGING);
Andrew Lee5ffbe8b2014-06-20 16:29:33 -0700101 }
102
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700103 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700104 public void setDialing(String callId) {
105 findConnectionForAction(callId, "setDialing")
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700106 .setState(Connection.STATE_DIALING);
Santos Cordon52d8a152014-06-17 19:08:45 -0700107 }
108
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700109 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700110 public void setDisconnected(String callId, int disconnectCause,
111 String disconnectMessage) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700112 if (mConnectionById.containsKey(callId)) {
113 findConnectionForAction(callId, "setDisconnected")
114 .setDisconnected(disconnectCause, disconnectMessage);
115 } else {
116 findConferenceForAction(callId, "setDisconnected")
117 .setDisconnected(disconnectCause, disconnectMessage);
118 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700119 }
120
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700121 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700122 public void setOnHold(String callId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700123 if (mConnectionById.containsKey(callId)) {
124 findConnectionForAction(callId, "setOnHold")
125 .setState(Connection.STATE_HOLDING);
126 } else {
127 findConferenceForAction(callId, "setOnHold")
128 .setState(Connection.STATE_HOLDING);
129 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700130 }
131
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700132 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700133 public void setRingbackRequested(String callId, boolean ringing) {
134 findConnectionForAction(callId, "setRingbackRequested")
135 .setRingbackRequested(ringing);
Santos Cordon52d8a152014-06-17 19:08:45 -0700136 }
137
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700138 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700139 public void setCallCapabilities(String callId, int callCapabilities) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700140 if (mConnectionById.containsKey(callId)) {
141 findConnectionForAction(callId, "setCallCapabilities")
142 .setCallCapabilities(callCapabilities);
143 } else {
144 findConferenceForAction(callId, "setCallCapabilities")
145 .setCallCapabilities(callCapabilities);
146 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700147 }
148
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700149 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700150 public void setIsConferenced(String callId, String conferenceCallId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700151 // Note: callId should not be null; conferenceCallId may be null
152 RemoteConnection connection =
153 findConnectionForAction(callId, "setIsConferenced");
154 if (connection != NULL_CONNECTION) {
155 if (conferenceCallId == null) {
156 // 'connection' is being split from its conference
157 if (connection.getConference() != null) {
158 connection.getConference().removeConnection(connection);
159 }
160 } else {
161 RemoteConference conference =
162 findConferenceForAction(conferenceCallId, "setIsConferenced");
163 if (conference != NULL_CONFERENCE) {
164 conference.addConnection(connection);
165 }
166 }
167 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700168 }
169
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700170 @Override
Ihab Awadb8e85c72014-08-23 20:34:57 -0700171 public void addConferenceCall(
172 final String callId,
173 ParcelableConference parcel) {
174 RemoteConference conference = new RemoteConference(callId,
175 mOutgoingConnectionServiceRpc);
176
177 for (String id : parcel.getConnectionIds()) {
178 RemoteConnection c = mConnectionById.get(id);
179 if (c != null) {
180 conference.addConnection(c);
181 }
182 }
183
184 if (conference.getConnections().size() == 0) {
185 // A conference was created, but none of its connections are ones that have been
186 // created by, and therefore being tracked by, this remote connection service. It
187 // is of no interest to us.
188 return;
189 }
190
191 conference.setState(parcel.getState());
192 conference.setCallCapabilities(parcel.getCapabilities());
193 mConferenceById.put(callId, conference);
Andrew Lee100e2932014-09-08 15:34:24 -0700194 conference.registerCallback(new RemoteConference.Callback() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700195 @Override
196 public void onDestroyed(RemoteConference c) {
197 mConferenceById.remove(callId);
198 maybeDisconnectAdapter();
199 }
200 });
201
202 mOurConnectionServiceImpl.addRemoteConference(conference);
Santos Cordon52d8a152014-06-17 19:08:45 -0700203 }
204
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700205 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700206 public void removeCall(String callId) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700207 if (mConnectionById.containsKey(callId)) {
208 findConnectionForAction(callId, "removeCall")
209 .setDestroyed();
210 } else {
211 findConferenceForAction(callId, "removeCall")
212 .setDestroyed();
213 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700214 }
215
Sailesh Nepal2a46b902014-07-04 17:21:07 -0700216 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700217 public void onPostDialWait(String callId, String remaining) {
218 findConnectionForAction(callId, "onPostDialWait")
219 .setPostDialWait(remaining);
Santos Cordon52d8a152014-06-17 19:08:45 -0700220 }
221
Santos Cordon52d8a152014-06-17 19:08:45 -0700222 @Override
223 public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
Ihab Awad5d0410f2014-07-30 10:07:40 -0700224 // Not supported from remote connection service.
Santos Cordon52d8a152014-06-17 19:08:45 -0700225 }
Tyler Gunn8d83fa92014-07-01 11:31:21 -0700226
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700227 @Override
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700228 public void setVideoProvider(String callId, IVideoProvider videoProvider) {
Ihab Awada64627c2014-08-20 09:36:40 -0700229 findConnectionForAction(callId, "setVideoProvider")
230 .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider));
Tyler Gunnaa07df82014-07-17 07:50:22 -0700231 }
232
233 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700234 public void setVideoState(String callId, int videoState) {
235 findConnectionForAction(callId, "setVideoState")
236 .setVideoState(videoState);
Sailesh Nepal33aaae42014-07-07 22:49:44 -0700237 }
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700238
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700239 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700240 public void setIsVoipAudioMode(String callId, boolean isVoip) {
241 findConnectionForAction(callId, "setIsVoipAudioMode")
242 .setIsVoipAudioMode(isVoip);
Sailesh Nepale7ef59a2014-07-08 21:48:22 -0700243 }
Sailesh Nepal61203862014-07-11 14:50:13 -0700244
245 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700246 public void setStatusHints(String callId, StatusHints statusHints) {
247 findConnectionForAction(callId, "setStatusHints")
248 .setStatusHints(statusHints);
Sailesh Nepal61203862014-07-11 14:50:13 -0700249 }
250
251 @Override
Andrew Lee100e2932014-09-08 15:34:24 -0700252 public void setAddress(String callId, Uri address, int presentation) {
253 findConnectionForAction(callId, "setAddress")
254 .setAddress(address, presentation);
Sailesh Nepal61203862014-07-11 14:50:13 -0700255 }
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -0700256
257 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700258 public void setCallerDisplayName(String callId, String callerDisplayName,
259 int presentation) {
260 findConnectionForAction(callId, "setCallerDisplayName")
261 .setCallerDisplayName(callerDisplayName, presentation);
262 }
263
264 @Override
Ihab Awad5d0410f2014-07-30 10:07:40 -0700265 public IBinder asBinder() {
266 throw new UnsupportedOperationException();
Sailesh Nepal2ab88cc2014-07-18 14:49:18 -0700267 }
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700268
269 @Override
270 public final void setConferenceableConnections(
271 String callId, List<String> conferenceableConnectionIds) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700272 List<RemoteConnection> conferenceable = new ArrayList<>();
273 for (String id : conferenceableConnectionIds) {
274 if (mConnectionById.containsKey(id)) {
275 conferenceable.add(mConnectionById.get(id));
276 }
277 }
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700278
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700279 findConnectionForAction(callId, "setConferenceableConnections")
Ihab Awadb8e85c72014-08-23 20:34:57 -0700280 .setConferenceableConnections(conferenceable);
Santos Cordon7c7bc7f2014-07-28 18:15:48 -0700281 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700282 };
283
Ihab Awad5d0410f2014-07-30 10:07:40 -0700284 private final ConnectionServiceAdapterServant mServant =
285 new ConnectionServiceAdapterServant(mServantDelegate);
Santos Cordon52d8a152014-06-17 19:08:45 -0700286
Ihab Awad5d0410f2014-07-30 10:07:40 -0700287 private final DeathRecipient mDeathRecipient = new DeathRecipient() {
288 @Override
289 public void binderDied() {
290 for (RemoteConnection c : mConnectionById.values()) {
291 c.setDestroyed();
292 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700293 for (RemoteConference c : mConferenceById.values()) {
294 c.setDestroyed();
295 }
Ihab Awad5d0410f2014-07-30 10:07:40 -0700296 mConnectionById.clear();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700297 mConferenceById.clear();
Ihab Awad5d0410f2014-07-30 10:07:40 -0700298 mPendingConnections.clear();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700299 mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0);
Ihab Awad5d0410f2014-07-30 10:07:40 -0700300 }
301 };
302
Ihab Awadb8e85c72014-08-23 20:34:57 -0700303 private final IConnectionService mOutgoingConnectionServiceRpc;
304 private final ConnectionService mOurConnectionServiceImpl;
Ihab Awad5d0410f2014-07-30 10:07:40 -0700305 private final Map<String, RemoteConnection> mConnectionById = new HashMap<>();
Ihab Awadb8e85c72014-08-23 20:34:57 -0700306 private final Map<String, RemoteConference> mConferenceById = new HashMap<>();
Ihab Awad5d0410f2014-07-30 10:07:40 -0700307 private final Set<RemoteConnection> mPendingConnections = new HashSet<>();
308
Ihab Awadb8e85c72014-08-23 20:34:57 -0700309 RemoteConnectionService(
310 IConnectionService outgoingConnectionServiceRpc,
311 ConnectionService ourConnectionServiceImpl) throws RemoteException {
312 mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc;
313 mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0);
314 mOurConnectionServiceImpl = ourConnectionServiceImpl;
Santos Cordon52d8a152014-06-17 19:08:45 -0700315 }
316
317 @Override
318 public String toString() {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700319 return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]";
Santos Cordon52d8a152014-06-17 19:08:45 -0700320 }
321
Ihab Awadf8b69882014-07-25 15:14:01 -0700322 final RemoteConnection createRemoteConnection(
323 PhoneAccountHandle connectionManagerPhoneAccount,
324 ConnectionRequest request,
325 boolean isIncoming) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700326 final String id = UUID.randomUUID().toString();
Ihab Awad8aecfed2014-08-08 17:06:11 -0700327 final ConnectionRequest newRequest = new ConnectionRequest(
Ihab Awad5d0410f2014-07-30 10:07:40 -0700328 request.getAccountHandle(),
Nancy Chenea38cca2014-09-05 16:38:49 -0700329 request.getAddress(),
Ihab Awad5d0410f2014-07-30 10:07:40 -0700330 request.getExtras(),
331 request.getVideoState());
332 try {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700333 if (mConnectionById.isEmpty()) {
Ihab Awadb8e85c72014-08-23 20:34:57 -0700334 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub());
Ihab Awad8aecfed2014-08-08 17:06:11 -0700335 }
336 RemoteConnection connection =
Ihab Awadb8e85c72014-08-23 20:34:57 -0700337 new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest);
Ihab Awad8aecfed2014-08-08 17:06:11 -0700338 mPendingConnections.add(connection);
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700339 mConnectionById.put(id, connection);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700340 mOutgoingConnectionServiceRpc.createConnection(
Ihab Awad5d0410f2014-07-30 10:07:40 -0700341 connectionManagerPhoneAccount,
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700342 id,
Ihab Awad5d0410f2014-07-30 10:07:40 -0700343 newRequest,
344 isIncoming);
Andrew Lee100e2932014-09-08 15:34:24 -0700345 connection.registerCallback(new RemoteConnection.Callback() {
Ihab Awad8aecfed2014-08-08 17:06:11 -0700346 @Override
347 public void onDestroyed(RemoteConnection connection) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700348 mConnectionById.remove(id);
Ihab Awadb8e85c72014-08-23 20:34:57 -0700349 maybeDisconnectAdapter();
Ihab Awad8aecfed2014-08-08 17:06:11 -0700350 }
351 });
Ihab Awad5d0410f2014-07-30 10:07:40 -0700352 return connection;
353 } catch (RemoteException e) {
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700354 return RemoteConnection
355 .failure(DisconnectCause.ERROR_UNSPECIFIED, e.toString());
Santos Cordon52d8a152014-06-17 19:08:45 -0700356 }
357 }
358
Ihab Awadb19a0bc2014-08-07 19:46:01 -0700359 private RemoteConnection findConnectionForAction(
360 String callId, String action) {
Ihab Awad5d0410f2014-07-30 10:07:40 -0700361 if (mConnectionById.containsKey(callId)) {
362 return mConnectionById.get(callId);
363 }
364 Log.w(this, "%s - Cannot find Connection %s", action, callId);
365 return NULL_CONNECTION;
Santos Cordon52d8a152014-06-17 19:08:45 -0700366 }
Ihab Awadb8e85c72014-08-23 20:34:57 -0700367
368 private RemoteConference findConferenceForAction(
369 String callId, String action) {
370 if (mConferenceById.containsKey(callId)) {
371 return mConferenceById.get(callId);
372 }
373 Log.w(this, "%s - Cannot find Conference %s", action, callId);
374 return NULL_CONFERENCE;
375 }
376
377 private void maybeDisconnectAdapter() {
378 if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) {
379 try {
380 mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub());
381 } catch (RemoteException e) {
382 }
383 }
384 }
Santos Cordon52d8a152014-06-17 19:08:45 -0700385}