RemoteConnectionService.java revision 7f3d41fd124dd7c4a8b72c1d48df08a8ee7209ec
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 connection.setState(parcel.getState()); 69 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); 76 connection.setVideoState(parcel.getVideoState()); 77 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 } 82 } 83 } 84 85 @Override 86 public void setActive(String callId) { 87 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 } 94 } 95 96 @Override 97 public void setRinging(String callId) { 98 findConnectionForAction(callId, "setRinging") 99 .setState(Connection.STATE_RINGING); 100 } 101 102 @Override 103 public void setDialing(String callId) { 104 findConnectionForAction(callId, "setDialing") 105 .setState(Connection.STATE_DIALING); 106 } 107 108 @Override 109 public void setDisconnected(String callId, DisconnectCause disconnectCause) { 110 if (mConnectionById.containsKey(callId)) { 111 findConnectionForAction(callId, "setDisconnected") 112 .setDisconnected(disconnectCause); 113 } else { 114 findConferenceForAction(callId, "setDisconnected") 115 .setDisconnected(disconnectCause); 116 } 117 } 118 119 @Override 120 public void setOnHold(String callId) { 121 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 } 128 } 129 130 @Override 131 public void setRingbackRequested(String callId, boolean ringing) { 132 findConnectionForAction(callId, "setRingbackRequested") 133 .setRingbackRequested(ringing); 134 } 135 136 @Override 137 public void setCallCapabilities(String callId, int callCapabilities) { 138 if (mConnectionById.containsKey(callId)) { 139 findConnectionForAction(callId, "setCallCapabilities") 140 .setCallCapabilities(callCapabilities); 141 } else { 142 findConferenceForAction(callId, "setCallCapabilities") 143 .setCallCapabilities(callCapabilities); 144 } 145 } 146 147 @Override 148 public void setIsConferenced(String callId, String conferenceCallId) { 149 // 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 } 166 } 167 168 @Override 169 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); 192 conference.registerCallback(new RemoteConference.Callback() { 193 @Override 194 public void onDestroyed(RemoteConference c) { 195 mConferenceById.remove(callId); 196 maybeDisconnectAdapter(); 197 } 198 }); 199 200 mOurConnectionServiceImpl.addRemoteConference(conference); 201 } 202 203 @Override 204 public void removeCall(String callId) { 205 if (mConnectionById.containsKey(callId)) { 206 findConnectionForAction(callId, "removeCall") 207 .setDestroyed(); 208 } else { 209 findConferenceForAction(callId, "removeCall") 210 .setDestroyed(); 211 } 212 } 213 214 @Override 215 public void onPostDialWait(String callId, String remaining) { 216 findConnectionForAction(callId, "onPostDialWait") 217 .setPostDialWait(remaining); 218 } 219 220 @Override 221 public void queryRemoteConnectionServices(RemoteServiceCallback callback) { 222 // Not supported from remote connection service. 223 } 224 225 @Override 226 public void setVideoProvider(String callId, IVideoProvider videoProvider) { 227 findConnectionForAction(callId, "setVideoProvider") 228 .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider)); 229 } 230 231 @Override 232 public void setVideoState(String callId, int videoState) { 233 findConnectionForAction(callId, "setVideoState") 234 .setVideoState(videoState); 235 } 236 237 @Override 238 public void setIsVoipAudioMode(String callId, boolean isVoip) { 239 findConnectionForAction(callId, "setIsVoipAudioMode") 240 .setIsVoipAudioMode(isVoip); 241 } 242 243 @Override 244 public void setStatusHints(String callId, StatusHints statusHints) { 245 findConnectionForAction(callId, "setStatusHints") 246 .setStatusHints(statusHints); 247 } 248 249 @Override 250 public void setAddress(String callId, Uri address, int presentation) { 251 findConnectionForAction(callId, "setAddress") 252 .setAddress(address, presentation); 253 } 254 255 @Override 256 public void setCallerDisplayName(String callId, String callerDisplayName, 257 int presentation) { 258 findConnectionForAction(callId, "setCallerDisplayName") 259 .setCallerDisplayName(callerDisplayName, presentation); 260 } 261 262 @Override 263 public IBinder asBinder() { 264 throw new UnsupportedOperationException(); 265 } 266 267 @Override 268 public final void setConferenceableConnections( 269 String callId, List<String> conferenceableConnectionIds) { 270 List<RemoteConnection> conferenceable = new ArrayList<>(); 271 for (String id : conferenceableConnectionIds) { 272 if (mConnectionById.containsKey(id)) { 273 conferenceable.add(mConnectionById.get(id)); 274 } 275 } 276 277 findConnectionForAction(callId, "setConferenceableConnections") 278 .setConferenceableConnections(conferenceable); 279 } 280 }; 281 282 private final ConnectionServiceAdapterServant mServant = 283 new ConnectionServiceAdapterServant(mServantDelegate); 284 285 private final DeathRecipient mDeathRecipient = new DeathRecipient() { 286 @Override 287 public void binderDied() { 288 for (RemoteConnection c : mConnectionById.values()) { 289 c.setDestroyed(); 290 } 291 for (RemoteConference c : mConferenceById.values()) { 292 c.setDestroyed(); 293 } 294 mConnectionById.clear(); 295 mConferenceById.clear(); 296 mPendingConnections.clear(); 297 mOutgoingConnectionServiceRpc.asBinder().unlinkToDeath(mDeathRecipient, 0); 298 } 299 }; 300 301 private final IConnectionService mOutgoingConnectionServiceRpc; 302 private final ConnectionService mOurConnectionServiceImpl; 303 private final Map<String, RemoteConnection> mConnectionById = new HashMap<>(); 304 private final Map<String, RemoteConference> mConferenceById = new HashMap<>(); 305 private final Set<RemoteConnection> mPendingConnections = new HashSet<>(); 306 307 RemoteConnectionService( 308 IConnectionService outgoingConnectionServiceRpc, 309 ConnectionService ourConnectionServiceImpl) throws RemoteException { 310 mOutgoingConnectionServiceRpc = outgoingConnectionServiceRpc; 311 mOutgoingConnectionServiceRpc.asBinder().linkToDeath(mDeathRecipient, 0); 312 mOurConnectionServiceImpl = ourConnectionServiceImpl; 313 } 314 315 @Override 316 public String toString() { 317 return "[RemoteCS - " + mOutgoingConnectionServiceRpc.asBinder().toString() + "]"; 318 } 319 320 final RemoteConnection createRemoteConnection( 321 PhoneAccountHandle connectionManagerPhoneAccount, 322 ConnectionRequest request, 323 boolean isIncoming) { 324 final String id = UUID.randomUUID().toString(); 325 final ConnectionRequest newRequest = new ConnectionRequest( 326 request.getAccountHandle(), 327 request.getAddress(), 328 request.getExtras(), 329 request.getVideoState()); 330 try { 331 if (mConnectionById.isEmpty()) { 332 mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub()); 333 } 334 RemoteConnection connection = 335 new RemoteConnection(id, mOutgoingConnectionServiceRpc, newRequest); 336 mPendingConnections.add(connection); 337 mConnectionById.put(id, connection); 338 mOutgoingConnectionServiceRpc.createConnection( 339 connectionManagerPhoneAccount, 340 id, 341 newRequest, 342 isIncoming); 343 connection.registerCallback(new RemoteConnection.Callback() { 344 @Override 345 public void onDestroyed(RemoteConnection connection) { 346 mConnectionById.remove(id); 347 maybeDisconnectAdapter(); 348 } 349 }); 350 return connection; 351 } catch (RemoteException e) { 352 return RemoteConnection.failure( 353 new DisconnectCause(DisconnectCause.ERROR, e.toString())); 354 } 355 } 356 357 private RemoteConnection findConnectionForAction( 358 String callId, String action) { 359 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; 364 } 365 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 } 383} 384