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