RemoteConnectionService.java revision 17455a3d39350a39eb995897929977d793358365
1/* 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 14 * limitations under the License. 15 */ 16 17package android.telecom; 18 19import android.net.Uri; 20import android.os.IBinder; 21import android.os.IBinder.DeathRecipient; 22import android.os.RemoteException; 23 24import com.android.internal.telecom.IConnectionService; 25import com.android.internal.telecom.IConnectionServiceAdapter; 26import com.android.internal.telecom.IVideoProvider; 27import com.android.internal.telecom.RemoteServiceCallback; 28 29import java.util.ArrayList; 30import java.util.HashMap; 31import java.util.HashSet; 32import java.util.Map; 33import java.util.Set; 34import java.util.List; 35import java.util.UUID; 36 37/** 38 * Remote connection service which other connection services can use to place calls on their behalf. 39 * 40 * @hide 41 */ 42final class RemoteConnectionService { 43 44 // Note: Casting null to avoid ambiguous constructor reference. 45 private static final RemoteConnection NULL_CONNECTION = 46 new RemoteConnection("NULL", null, (ConnectionRequest) null); 47 48 private static final RemoteConference NULL_CONFERENCE = 49 new RemoteConference("NULL", null); 50 51 private final IConnectionServiceAdapter mServantDelegate = new IConnectionServiceAdapter() { 52 @Override 53 public void handleCreateConnectionComplete( 54 String id, 55 ConnectionRequest request, 56 ParcelableConnection parcel) { 57 RemoteConnection connection = 58 findConnectionForAction(id, "handleCreateConnectionSuccessful"); 59 if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) { 60 mPendingConnections.remove(connection); 61 // Unconditionally initialize the connection ... 62 connection.setConnectionCapabilities(parcel.getConnectionCapabilities()); 63 connection.setAddress( 64 parcel.getHandle(), parcel.getHandlePresentation()); 65 connection.setCallerDisplayName( 66 parcel.getCallerDisplayName(), 67 parcel.getCallerDisplayNamePresentation()); 68 // Set state after handle so that the client can identify the connection. 69 if (parcel.getState() == Connection.STATE_DISCONNECTED) { 70 connection.setDisconnected(parcel.getDisconnectCause()); 71 } else { 72 connection.setState(parcel.getState()); 73 } 74 List<RemoteConnection> conferenceable = new ArrayList<>(); 75 for (String confId : parcel.getConferenceableConnectionIds()) { 76 if (mConnectionById.containsKey(confId)) { 77 conferenceable.add(mConnectionById.get(confId)); 78 } 79 } 80 connection.setConferenceableConnections(conferenceable); 81 connection.setVideoState(parcel.getVideoState()); 82 if (connection.getState() == Connection.STATE_DISCONNECTED) { 83 // ... then, if it was created in a disconnected state, that indicates 84 // failure on the providing end, so immediately mark it destroyed 85 connection.setDestroyed(); 86 } 87 } 88 } 89 90 @Override 91 public void setActive(String callId) { 92 if (mConnectionById.containsKey(callId)) { 93 findConnectionForAction(callId, "setActive") 94 .setState(Connection.STATE_ACTIVE); 95 } else { 96 findConferenceForAction(callId, "setActive") 97 .setState(Connection.STATE_ACTIVE); 98 } 99 } 100 101 @Override 102 public void setRinging(String callId) { 103 findConnectionForAction(callId, "setRinging") 104 .setState(Connection.STATE_RINGING); 105 } 106 107 @Override 108 public void setDialing(String callId) { 109 findConnectionForAction(callId, "setDialing") 110 .setState(Connection.STATE_DIALING); 111 } 112 113 @Override 114 public void setDisconnected(String callId, DisconnectCause disconnectCause) { 115 if (mConnectionById.containsKey(callId)) { 116 findConnectionForAction(callId, "setDisconnected") 117 .setDisconnected(disconnectCause); 118 } else { 119 findConferenceForAction(callId, "setDisconnected") 120 .setDisconnected(disconnectCause); 121 } 122 } 123 124 @Override 125 public void setOnHold(String callId) { 126 if (mConnectionById.containsKey(callId)) { 127 findConnectionForAction(callId, "setOnHold") 128 .setState(Connection.STATE_HOLDING); 129 } else { 130 findConferenceForAction(callId, "setOnHold") 131 .setState(Connection.STATE_HOLDING); 132 } 133 } 134 135 @Override 136 public void setRingbackRequested(String callId, boolean ringing) { 137 findConnectionForAction(callId, "setRingbackRequested") 138 .setRingbackRequested(ringing); 139 } 140 141 @Override 142 public void setConnectionCapabilities(String callId, int connectionCapabilities) { 143 if (mConnectionById.containsKey(callId)) { 144 findConnectionForAction(callId, "setConnectionCapabilities") 145 .setConnectionCapabilities(connectionCapabilities); 146 } else { 147 findConferenceForAction(callId, "setConnectionCapabilities") 148 .setConnectionCapabilities(connectionCapabilities); 149 } 150 } 151 152 @Override 153 public void setIsConferenced(String callId, String conferenceCallId) { 154 // Note: callId should not be null; conferenceCallId may be null 155 RemoteConnection connection = 156 findConnectionForAction(callId, "setIsConferenced"); 157 if (connection != NULL_CONNECTION) { 158 if (conferenceCallId == null) { 159 // 'connection' is being split from its conference 160 if (connection.getConference() != null) { 161 connection.getConference().removeConnection(connection); 162 } 163 } else { 164 RemoteConference conference = 165 findConferenceForAction(conferenceCallId, "setIsConferenced"); 166 if (conference != NULL_CONFERENCE) { 167 conference.addConnection(connection); 168 } 169 } 170 } 171 } 172 173 @Override 174 public void setConferenceMergeFailed(String callId) { 175 // Nothing to do here. 176 // The event has already been handled and there is no state to update 177 // in the underlying connection or conference objects 178 } 179 180 @Override 181 public void addConferenceCall( 182 final String callId, 183 ParcelableConference parcel) { 184 RemoteConference conference = new RemoteConference(callId, 185 mOutgoingConnectionServiceRpc); 186 187 for (String id : parcel.getConnectionIds()) { 188 RemoteConnection c = mConnectionById.get(id); 189 if (c != null) { 190 conference.addConnection(c); 191 } 192 } 193 194 if (conference.getConnections().size() == 0) { 195 // A conference was created, but none of its connections are ones that have been 196 // created by, and therefore being tracked by, this remote connection service. It 197 // is of no interest to us. 198 return; 199 } 200 201 conference.setState(parcel.getState()); 202 conference.setConnectionCapabilities(parcel.getConnectionCapabilities()); 203 mConferenceById.put(callId, conference); 204 conference.registerCallback(new RemoteConference.Callback() { 205 @Override 206 public void onDestroyed(RemoteConference c) { 207 mConferenceById.remove(callId); 208 maybeDisconnectAdapter(); 209 } 210 }); 211 212 mOurConnectionServiceImpl.addRemoteConference(conference); 213 } 214 215 @Override 216 public void removeCall(String callId) { 217 if (mConnectionById.containsKey(callId)) { 218 findConnectionForAction(callId, "removeCall") 219 .setDestroyed(); 220 } else { 221 findConferenceForAction(callId, "removeCall") 222 .setDestroyed(); 223 } 224 } 225 226 @Override 227 public void onPostDialWait(String callId, String remaining) { 228 findConnectionForAction(callId, "onPostDialWait") 229 .setPostDialWait(remaining); 230 } 231 232 @Override 233 public void onPostDialChar(String callId, char nextChar) { 234 findConnectionForAction(callId, "onPostDialChar") 235 .onPostDialChar(nextChar); 236 } 237 238 @Override 239 public void queryRemoteConnectionServices(RemoteServiceCallback callback) { 240 // Not supported from remote connection service. 241 } 242 243 @Override 244 public void setVideoProvider(String callId, IVideoProvider videoProvider) { 245 RemoteConnection.VideoProvider remoteVideoProvider = null; 246 if (videoProvider != null) { 247 remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider); 248 } 249 findConnectionForAction(callId, "setVideoProvider") 250 .setVideoProvider(remoteVideoProvider); 251 } 252 253 @Override 254 public void setVideoState(String callId, int videoState) { 255 findConnectionForAction(callId, "setVideoState") 256 .setVideoState(videoState); 257 } 258 259 @Override 260 public void setIsVoipAudioMode(String callId, boolean isVoip) { 261 findConnectionForAction(callId, "setIsVoipAudioMode") 262 .setIsVoipAudioMode(isVoip); 263 } 264 265 @Override 266 public void setStatusHints(String callId, StatusHints statusHints) { 267 findConnectionForAction(callId, "setStatusHints") 268 .setStatusHints(statusHints); 269 } 270 271 @Override 272 public void setAddress(String callId, Uri address, int presentation) { 273 findConnectionForAction(callId, "setAddress") 274 .setAddress(address, presentation); 275 } 276 277 @Override 278 public void setCallerDisplayName(String callId, String callerDisplayName, 279 int presentation) { 280 findConnectionForAction(callId, "setCallerDisplayName") 281 .setCallerDisplayName(callerDisplayName, presentation); 282 } 283 284 @Override 285 public IBinder asBinder() { 286 throw new UnsupportedOperationException(); 287 } 288 289 @Override 290 public final void setConferenceableConnections( 291 String callId, List<String> conferenceableConnectionIds) { 292 List<RemoteConnection> conferenceable = new ArrayList<>(); 293 for (String id : conferenceableConnectionIds) { 294 if (mConnectionById.containsKey(id)) { 295 conferenceable.add(mConnectionById.get(id)); 296 } 297 } 298 299 if (hasConnection(callId)) { 300 findConnectionForAction(callId, "setConferenceableConnections") 301 .setConferenceableConnections(conferenceable); 302 } else { 303 findConferenceForAction(callId, "setConferenceableConnections") 304 .setConferenceableConnections(conferenceable); 305 } 306 } 307 308 @Override 309 public void addExistingConnection(String callId, ParcelableConnection connection) { 310 // TODO: add contents of this method 311 RemoteConnection remoteConnction = new RemoteConnection(callId, 312 mOutgoingConnectionServiceRpc, connection); 313 314 mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction); 315 } 316 }; 317 318 private final ConnectionServiceAdapterServant mServant = 319 new ConnectionServiceAdapterServant(mServantDelegate); 320 321 private final DeathRecipient mDeathRecipient = new DeathRecipient() { 322 @Override 323 public void binderDied() { 324 for (RemoteConnection c : mConnectionById.values()) { 325 c.setDestroyed(); 326 } 327 for (RemoteConference c : mConferenceById.values()) { 328 c.setDestroyed(); 329 } 330 mConnectionById.clear(); 331 mConferenceById.clear(); 332 mPendingConnections.clear(); 333 mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0); 334 } 335 }; 336 337 private final IConnectionService mOutgoingConnectionServiceRpc; 338 private final ConnectionService mOurConnectionServiceImpl; 339 private final Map<String, RemoteConnection> mConnectionById = new HashMap<>(); 340 private final Map<String, RemoteConference> mConferenceById = new HashMap<>(); 341 private final Set<RemoteConnection> mPendingConnections = new HashSet<>(); 342 343 RemoteConnectionService( 344 IConnectionService outgoingConnectionServiceRpc, 345 ConnectionService ourConnectionServiceImpl) throws RemoteException { 346 mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc; 347 mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0); 348 mOurConnectionServiceImpl = ourConnectionServiceImpl; 349 } 350 351 @Override 352 public String toString() { 353 return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]"; 354 } 355 356 final RemoteConnection createRemoteConnection( 357 PhoneAccountHandle connectionManagerPhoneAccount, 358 ConnectionRequest request, 359 boolean isIncoming) { 360 final String id = UUID.randomUUID().toString(); 361 final ConnectionRequest newRequest = new ConnectionRequest( 362 request.getAccountHandle(), 363 request.getAddress(), 364 request.getExtras(), 365 request.getVideoState()); 366 try { 367 if (mConnectionById.isEmpty()) { 368 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub()); 369 } 370 RemoteConnection connection = 371 new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest); 372 mPendingConnections.add(connection); 373 mConnectionById.put(id, connection); 374 mOutgoingConnectionServiceRpc.createConnection( 375 connectionManagerPhoneAccount, 376 id, 377 newRequest, 378 isIncoming, 379 false /* isUnknownCall */); 380 connection.registerCallback(new RemoteConnection.Callback() { 381 @Override 382 public void onDestroyed(RemoteConnection connection) { 383 mConnectionById.remove(id); 384 maybeDisconnectAdapter(); 385 } 386 }); 387 return connection; 388 } catch (RemoteException e) { 389 return RemoteConnection.failure( 390 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 391 } 392 } 393 394 private boolean hasConnection(String callId) { 395 return mConnectionById.containsKey(callId); 396 } 397 398 private RemoteConnection findConnectionForAction( 399 String callId, String action) { 400 if (mConnectionById.containsKey(callId)) { 401 return mConnectionById.get(callId); 402 } 403 Log.w(this, "%s - Cannot find Connection %s", action, callId); 404 return NULL_CONNECTION; 405 } 406 407 private RemoteConference findConferenceForAction( 408 String callId, String action) { 409 if (mConferenceById.containsKey(callId)) { 410 return mConferenceById.get(callId); 411 } 412 Log.w(this, "%s - Cannot find Conference %s", action, callId); 413 return NULL_CONFERENCE; 414 } 415 416 private void maybeDisconnectAdapter() { 417 if (mConnectionById.isEmpty() && mConferenceById.isEmpty()) { 418 try { 419 mOutgoingConnectionServiceRpc.removeConnectionServiceAdapter(mServant.getStub()); 420 } catch (RemoteException e) { 421 } 422 } 423 } 424} 425