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 17 package android.telecom; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SdkConstant; 24 import android.annotation.SystemApi; 25 import android.annotation.TestApi; 26 import android.app.Service; 27 import android.content.ComponentName; 28 import android.content.Intent; 29 import android.location.Location; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.IBinder; 34 import android.os.Looper; 35 import android.os.Message; 36 import android.os.OutcomeReceiver; 37 import android.os.ParcelFileDescriptor; 38 import android.os.RemoteException; 39 import android.telecom.Logging.Session; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.os.SomeArgs; 43 import com.android.internal.telecom.IConnectionService; 44 import com.android.internal.telecom.IConnectionServiceAdapter; 45 import com.android.internal.telecom.RemoteServiceCallback; 46 47 import java.util.ArrayList; 48 import java.util.Collection; 49 import java.util.Collections; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.UUID; 53 import java.util.concurrent.ConcurrentHashMap; 54 import java.util.concurrent.Executor; 55 56 /** 57 * An abstract service that should be implemented by any apps which either: 58 * <ol> 59 * <li>Can make phone calls (VoIP or otherwise) and want those calls to be integrated into the 60 * built-in phone app. Referred to as a <b>system managed</b> {@link ConnectionService}.</li> 61 * <li>Are a standalone calling app and don't want their calls to be integrated into the 62 * built-in phone app. Referred to as a <b>self managed</b> {@link ConnectionService}.</li> 63 * </ol> 64 * Once implemented, the {@link ConnectionService} needs to take the following steps so that Telecom 65 * will bind to it: 66 * <p> 67 * 1. <i>Registration in AndroidManifest.xml</i> 68 * <br/> 69 * <pre> 70 * <service android:name="com.example.package.MyConnectionService" 71 * android:label="@string/some_label_for_my_connection_service" 72 * android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"> 73 * <intent-filter> 74 * <action android:name="android.telecom.ConnectionService" /> 75 * </intent-filter> 76 * </service> 77 * </pre> 78 * <p> 79 * 2. <i> Registration of {@link PhoneAccount} with {@link TelecomManager}.</i> 80 * <br/> 81 * See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information. 82 * <p> 83 * System managed {@link ConnectionService}s must be enabled by the user in the phone app settings 84 * before Telecom will bind to them. Self-managed {@link ConnectionService}s must declare the 85 * {@link android.Manifest.permission#MANAGE_OWN_CALLS} permission in their manifest before Telecom 86 * will bind to them. 87 * <p> 88 * Once registered and enabled by the user in the phone app settings or granted permission, telecom 89 * will bind to a {@link ConnectionService} implementation when it wants that 90 * {@link ConnectionService} to place a call or the service has indicated that is has an incoming 91 * call through {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}. The 92 * {@link ConnectionService} can then expect a call to 93 * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} or 94 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} 95 * wherein it should provide a new instance of a {@link Connection} object. It is through this 96 * {@link Connection} object that telecom receives state updates and the {@link ConnectionService} 97 * receives call-commands such as answer, reject, hold and disconnect. 98 * <p> 99 * When there are no more live calls, telecom will unbind from the {@link ConnectionService}. 100 * <p> 101 * <h1>Self-Managed Connection Services</h1> 102 * A VoIP app can implement a {@link ConnectionService} to ensure that its calls are integrated 103 * into the Android platform. There are numerous benefits to using the Telecom APIs for a VoIP app: 104 * <ul> 105 * <li>Call concurrency is handled - the user is able to swap between calls in different 106 * apps and on the mobile network.</li> 107 * <li>Simplified audio routing - the platform provides your app with a unified list of the 108 * audio routes which are available 109 * (e.g. {@link android.telecom.Connection#onAvailableCallEndpointsChanged(List)}) and a 110 * standardized way to switch audio routes 111 * (e.g. {@link android.telecom.Connection#requestCallEndpointChange(CallEndpoint, Executor, 112 * OutcomeReceiver)} ).</li> 113 * <li>Bluetooth integration - your calls will be visible on and controllable via 114 * bluetooth devices (e.g. car head units and headsets).</li> 115 * <li>Companion device integration - wearable devices such as watches which implement an 116 * {@link InCallService} can optionally subscribe to see self-managed calls. Similar to a 117 * bluetooth headunit, wearables will typically render your call using a generic call UX and 118 * provide the user with basic call controls such as hangup, answer, reject.</li> 119 * <li>Automotive calling experiences - Android supports automotive optimized experiences which 120 * provides a means for calls to be controlled and viewed in an automobile; these experiences 121 * are capable of leveraging call metadata provided by your app.</li> 122 * </ul> 123 * <h2>Registering a Phone Account</h2> 124 * Before your app can handle incoming or outgoing calls through Telecom it needs to register a 125 * {@link PhoneAccount} with Telecom indicating to the platform that your app is capable of calling. 126 * <p> 127 * Your app should create a new instance of {@link PhoneAccount} which meets the following 128 * requirements: 129 * <ul> 130 * <li>Has {@link PhoneAccount#CAPABILITY_SELF_MANAGED} (set using 131 * {@link PhoneAccount.Builder#setCapabilities(int)}). This indicates to Telecom that your 132 * app will report calls but that it provides a primary UI for the calls by itself.</li> 133 * <li>Provide a unique identifier for the {@link PhoneAccount} via the 134 * {@link PhoneAccountHandle#getId()} attribute. As per the {@link PhoneAccountHandle} 135 * documentation, you should NOT use an identifier which contains PII or other sensitive 136 * information. A typical choice is a UUID.</li> 137 * </ul> 138 * Your app should register the new {@link PhoneAccount} with Telecom using 139 * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. {@link PhoneAccount}s persist across 140 * reboot. You can use {@link TelecomManager#getOwnSelfManagedPhoneAccounts()} to confirm the 141 * {@link PhoneAccount} you registered. Your app should generally only register a single 142 * {@link PhoneAccount}. 143 * 144 * <h2>Implementing ConnectionService</h2> 145 * Your app uses {@link TelecomManager#placeCall(Uri, Bundle)} to start new outgoing calls and 146 * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)} to report new incoming 147 * calls. Calling these APIs causes the Telecom stack to bind to your app's 148 * {@link ConnectionService} implementation. Telecom will either inform your app that it cannot 149 * handle a call request at the current time (i.e. there could be an ongoing emergency call, which 150 * means your app is not allowed to handle calls at the current time), or it will ask your app to 151 * create a new instance of {@link Connection} to represent a call in your app. 152 * 153 * Your app should implement the following {@link ConnectionService} methods: 154 * <ul> 155 * <li>{@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, 156 * ConnectionRequest)} - called by Telecom to ask your app to make a new {@link Connection} 157 * to represent an outgoing call your app requested via 158 * {@link TelecomManager#placeCall(Uri, Bundle)}.</li> 159 * <li><{@link ConnectionService#onCreateOutgoingConnectionFailed(PhoneAccountHandle, 160 * ConnectionRequest)} - called by Telecom to inform your app that a call it reported via 161 * {@link TelecomManager#placeCall(Uri, Bundle)} cannot be handled at this time. Your app 162 * should NOT place a call at the current time.</li> 163 * <li>{@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, 164 * ConnectionRequest)} - called by Telecom to ask your app to make a new {@link Connection} 165 * to represent an incoming call your app reported via 166 * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}.</li> 167 * <li>{@link ConnectionService#onCreateIncomingConnectionFailed(PhoneAccountHandle, 168 * ConnectionRequest)} - called by Telecom to inform your app that an incoming call it reported 169 * via {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)} cannot be handled 170 * at this time. Your app should NOT post a new incoming call notification and should silently 171 * reject the call.</li> 172 * </ul> 173 * 174 * <h2>Implementing a Connection</h2> 175 * Your app should extend the {@link Connection} class to represent calls in your app. When you 176 * create new instances of your {@link Connection}, you should ensure the following properties are 177 * set on the new {@link Connection} instance returned by your {@link ConnectionService}: 178 * <ul> 179 * <li>{@link Connection#setAddress(Uri, int)} - the identifier for the other party. For 180 * apps that user phone numbers the {@link Uri} can be a {@link PhoneAccount#SCHEME_TEL} URI 181 * representing the phone number.</li> 182 * <li>{@link Connection#setCallerDisplayName(String, int)} - the display name of the other 183 * party. This is what will be shown on Bluetooth devices and other calling surfaces such 184 * as wearable devices. This is particularly important for calls that do not use a phone 185 * number to identify the caller or called party.</li> 186 * <li>{@link Connection#setConnectionProperties(int)} - ensure you set 187 * {@link Connection#PROPERTY_SELF_MANAGED} to identify to the platform that the call is 188 * handled by your app.</li> 189 * <li>{@link Connection#setConnectionCapabilities(int)} - if your app supports making calls 190 * inactive (i.e. holding calls) you should get {@link Connection#CAPABILITY_SUPPORT_HOLD} and 191 * {@link Connection#CAPABILITY_HOLD} to indicate to the platform that you calls can potentially 192 * be held for concurrent calling scenarios.</li> 193 * <li>{@link Connection#setAudioModeIsVoip(boolean)} - set to {@code true} to ensure that the 194 * platform knows your call is a VoIP call.</li> 195 * <li>For newly created {@link Connection} instances, do NOT change the state of your call 196 * using {@link Connection#setActive()}, {@link Connection#setOnHold()} until the call is added 197 * to Telecom (ie you have returned it via 198 * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} 199 * or 200 * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}). 201 * </li> 202 * </ul> 203 * 204 * <h2>How to Place Outgoing Calls</h2> 205 * When your app wants to place an outgoing call it calls 206 * {@link TelecomManager#placeCall(Uri, Bundle)}. You should specify a {@link Uri} to identify 207 * who the call is being placed to, and specify the {@link PhoneAccountHandle} associated with the 208 * {@link PhoneAccount} you registered for your app using 209 * {@link TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE} in the {@link Bundle} parameter. 210 * <p> 211 * Telecom will bind to your app's {@link ConnectionService} implementation and call either: 212 * <ul> 213 * <li>{@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, 214 * ConnectionRequest)} - the {@link ConnectionRequest#getAddress()} will match the address 215 * you specified when placing the call. You should return a new instance of your app's 216 * {@link Connection} class to represent the outgoing call.</li> 217 * <li>{@link ConnectionService#onCreateOutgoingConnectionFailed(PhoneAccountHandle, 218 * ConnectionRequest)} - your app should not place the call at this time; the call should be 219 * cancelled and the user informed that the call cannot be placed.</li> 220 * </ul> 221 * <p> 222 * New outgoing calls will start in a {@link Connection#STATE_DIALING} state. This state indicates 223 * that your app is in the process of connecting the call to the other party. 224 * <p> 225 * Once the other party answers the call (or it is set up successfully), your app should call 226 * {@link Connection#setActive()} to inform Telecom that the call is now active. 227 * 228 * <h2>How to Add Incoming Calls</h2> 229 * When your app receives an incoming call, it should call 230 * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}. Set the 231 * {@link PhoneAccountHandle} parameter to the {@link PhoneAccountHandle} associated with your 232 * app's {@link PhoneAccount}. 233 * <p> 234 * Telecom will bind to your app's {@link ConnectionService} implementation and call either: 235 * <ul> 236 * <li>{@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, 237 * ConnectionRequest)} - You should return a new instance of your app's 238 * {@link Connection} class to represent the incoming call.</li> 239 * <li>{@link ConnectionService#onCreateIncomingConnectionFailed(PhoneAccountHandle, 240 * ConnectionRequest)} - your app should not receive the call at this time; the call should be 241 * rejected silently; the user may be informed of a missed call.</li> 242 * </ul> 243 * <p> 244 * New incoming calls will start with a {@link Connection#STATE_RINGING} state. This state 245 * indicates that your app has a new incoming call pending. Telecom will NOT play a ringtone or 246 * post a notification for your app. It is up to your app to post an incoming call notification 247 * with an associated ringtone. Telecom will call {@link Connection#onShowIncomingCallUi()} on the 248 * {@link Connection} when your app can post its incoming call notification. See 249 * {@link Connection#onShowIncomingCallUi() the docs} for more information on how to post the 250 * notification. 251 * <p> 252 * Your incoming call notification (or full screen UI) will typically have an "answer" and "decline" 253 * action which the user chooses. When your app receives the "answer" or "decline" 254 * {@link android.app.PendingIntent}, you should must call either {@link Connection#setActive()} to 255 * inform Telecom that the call was answered, or 256 * {@link Connection#setDisconnected(DisconnectCause)} to inform Telecom that the call was rejected. 257 * If the call was rejected, supply an instance of {@link DisconnectCause} with 258 * {@link DisconnectCause#REJECTED}, and then call {@link Connection#destroy()}. 259 * <p> 260 * In addition to handling requests to answer or decline the call via notification actions, your 261 * app should also be implement the {@link Connection#onAnswer(int)} and 262 * {@link Connection#onAnswer()} methods on the {@link Connection}. These will be raised if the 263 * user answers your call via a Bluetooth device or another device like a wearable or automotive 264 * calling UX. In response, your app should call {@link Connection#setActive()} to inform Telecom 265 * that the call was answered. 266 * <p> 267 * Additionally, your app should implement {@link Connection#onReject()} to handle requests to 268 * reject the call which are raised via Bluetooth or other calling surfaces. Your app should call 269 * {@link Connection#setDisconnected(DisconnectCause)} and supply an instance of 270 * {@link DisconnectCause} with {@link DisconnectCause#REJECTED} in this case. 271 * 272 * <h2>Ending Calls</h2> 273 * When an ongoing active call (incoming or outgoing) has ended, your app is responsible for 274 * informing Telecom that the call ended. 275 * <p> 276 * Your app calls: 277 * <ul> 278 * <li>{@link Connection#setDisconnected(DisconnectCause)} - this informs Telecom that the 279 * call has terminated. You should provide a new instance of {@link DisconnectCause} with 280 * either {@link DisconnectCause#LOCAL} or {@link DisconnectCause#REMOTE} to indicate where the 281 * call disconnection took place. {@link DisconnectCause#LOCAL} indicates that the call 282 * terminated in your app on the current device (i.e. via user action), where 283 * {@link DisconnectCause#REMOTE} indicates that the call terminates on the remote device.</li> 284 * <li>{@link Connection#destroy()} - this informs Telecom that your call instance can be 285 * cleaned up. You should always call this when you are finished with a call.</li> 286 * </ul> 287 * <p> 288 * Similar to answering incoming calls, requests to disconnect your call may originate from outside 289 * your app. You can handle these by implementing {@link Connection#onDisconnect()}. Your app 290 * should call {@link Connection#setDisconnected(DisconnectCause)} with an instance of 291 * {@link DisconnectCause} and reason {@link DisconnectCause#LOCAL} to indicate to Telecom that your 292 * app has disconnected the call as requested based on the user's request. 293 * 294 * <h2>Holding and Unholding Calls</h2> 295 * When your app specifies {@link Connection#CAPABILITY_SUPPORT_HOLD} and 296 * {@link Connection#CAPABILITY_HOLD} on your {@link Connection} instance, it is telling Telecom 297 * that your calls can be placed into a suspended, or "held" state if required. If your app 298 * supports holding its calls, it will be possible for the user to switch between calls in your app 299 * and holdable calls in another app or on the mobile network. If your app does not support 300 * holding its calls, you may receive a request to disconnect the call from Telecom if the user 301 * opts to answer an incoming call in another app or on the mobile network; this ensures that the 302 * user can only be in one call at a time. 303 * <p> 304 * Your app is free to change a call between the held and active state using 305 * {@link Connection#setOnHold()} and {@link Connection#setActive()}. 306 * <p> 307 * Your app may receive a request from Telecom to hold or unhold a call via 308 * {@link Connection#onHold()} and {@link Connection#onUnhold()}. Telecom can ask your app to 309 * hold or unhold its {@link Connection} either if the user requests this action through another 310 * calling surface such as Bluetooth, or if the user answers or switches to a call in a different 311 * app or on the mobile network. 312 * <p> 313 * When your app receives an {@link Connection#onHold()} it must call {@link Connection#setOnHold()} 314 * to inform Telecom that the call has been held successfully. 315 * <p> 316 * When your app receives an {@link Connection#onUnhold()} it must call 317 * {@link Connection#setActive()} to inform Telecom that the call has been resumed successfully. 318 */ 319 public abstract class ConnectionService extends Service { 320 /** 321 * The {@link Intent} that must be declared as handled by the service. 322 */ 323 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 324 public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService"; 325 326 /** 327 * Boolean extra used by Telecom to inform a {@link ConnectionService} that the purpose of it 328 * being asked to create a new outgoing {@link Connection} is to perform a handover of an 329 * ongoing call on the device from another {@link PhoneAccount}/{@link ConnectionService}. Will 330 * be specified in the {@link ConnectionRequest#getExtras()} passed by Telecom when 331 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} is called. 332 * <p> 333 * When your {@link ConnectionService} receives this extra, it should communicate the fact that 334 * this is a handover to the other device's matching {@link ConnectionService}. That 335 * {@link ConnectionService} will continue the handover using 336 * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)}, specifying 337 * {@link TelecomManager#EXTRA_IS_HANDOVER}. Telecom will match the phone numbers of the 338 * handover call on the other device with ongoing calls for {@link ConnectionService}s which 339 * support {@link PhoneAccount#EXTRA_SUPPORTS_HANDOVER_FROM}. 340 * @hide 341 */ 342 public static final String EXTRA_IS_HANDOVER = TelecomManager.EXTRA_IS_HANDOVER; 343 344 // Flag controlling whether PII is emitted into the logs 345 private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); 346 347 // Session Definitions 348 private static final String SESSION_HANDLER = "H."; 349 private static final String SESSION_ADD_CS_ADAPTER = "CS.aCSA"; 350 private static final String SESSION_REMOVE_CS_ADAPTER = "CS.rCSA"; 351 private static final String SESSION_CREATE_CONN = "CS.crCo"; 352 private static final String SESSION_CREATE_CONN_COMPLETE = "CS.crCoC"; 353 private static final String SESSION_CREATE_CONN_FAILED = "CS.crCoF"; 354 private static final String SESSION_ABORT = "CS.ab"; 355 private static final String SESSION_ANSWER = "CS.an"; 356 private static final String SESSION_ANSWER_VIDEO = "CS.anV"; 357 private static final String SESSION_DEFLECT = "CS.def"; 358 private static final String SESSION_TRANSFER = "CS.trans"; 359 private static final String SESSION_CONSULTATIVE_TRANSFER = "CS.cTrans"; 360 private static final String SESSION_REJECT = "CS.r"; 361 private static final String SESSION_REJECT_MESSAGE = "CS.rWM"; 362 private static final String SESSION_SILENCE = "CS.s"; 363 private static final String SESSION_DISCONNECT = "CS.d"; 364 private static final String SESSION_HOLD = "CS.h"; 365 private static final String SESSION_UNHOLD = "CS.u"; 366 private static final String SESSION_CALL_AUDIO_SC = "CS.cASC"; 367 private static final String SESSION_USING_ALTERNATIVE_UI = "CS.uAU"; 368 private static final String SESSION_TRACKED_BY_NON_UI_SERVICE = "CS.tBNUS"; 369 private static final String SESSION_PLAY_DTMF = "CS.pDT"; 370 private static final String SESSION_STOP_DTMF = "CS.sDT"; 371 private static final String SESSION_CONFERENCE = "CS.c"; 372 private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC"; 373 private static final String SESSION_MERGE_CONFERENCE = "CS.mC"; 374 private static final String SESSION_SWAP_CONFERENCE = "CS.sC"; 375 private static final String SESSION_ADD_PARTICIPANT = "CS.aP"; 376 private static final String SESSION_POST_DIAL_CONT = "CS.oPDC"; 377 private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC"; 378 private static final String SESSION_SEND_CALL_EVENT = "CS.sCE"; 379 private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC"; 380 private static final String SESSION_HANDOVER_COMPLETE = "CS.hC"; 381 private static final String SESSION_EXTRAS_CHANGED = "CS.oEC"; 382 private static final String SESSION_START_RTT = "CS.+RTT"; 383 private static final String SESSION_UPDATE_RTT_PIPES = "CS.uRTT"; 384 private static final String SESSION_STOP_RTT = "CS.-RTT"; 385 private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR"; 386 private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL"; 387 private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG"; 388 private static final String SESSION_HANDOVER_FAILED = "CS.haF"; 389 private static final String SESSION_CREATE_CONF = "CS.crConf"; 390 private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC"; 391 private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF"; 392 private static final String SESSION_CALL_ENDPOINT_CHANGED = "CS.oCEC"; 393 private static final String SESSION_AVAILABLE_CALL_ENDPOINTS_CHANGED = "CS.oACEC"; 394 private static final String SESSION_MUTE_STATE_CHANGED = "CS.oMSC"; 395 396 private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; 397 private static final int MSG_CREATE_CONNECTION = 2; 398 private static final int MSG_ABORT = 3; 399 private static final int MSG_ANSWER = 4; 400 private static final int MSG_REJECT = 5; 401 private static final int MSG_DISCONNECT = 6; 402 private static final int MSG_HOLD = 7; 403 private static final int MSG_UNHOLD = 8; 404 private static final int MSG_ON_CALL_AUDIO_STATE_CHANGED = 9; 405 private static final int MSG_PLAY_DTMF_TONE = 10; 406 private static final int MSG_STOP_DTMF_TONE = 11; 407 private static final int MSG_CONFERENCE = 12; 408 private static final int MSG_SPLIT_FROM_CONFERENCE = 13; 409 private static final int MSG_ON_POST_DIAL_CONTINUE = 14; 410 private static final int MSG_REMOVE_CONNECTION_SERVICE_ADAPTER = 16; 411 private static final int MSG_ANSWER_VIDEO = 17; 412 private static final int MSG_MERGE_CONFERENCE = 18; 413 private static final int MSG_SWAP_CONFERENCE = 19; 414 private static final int MSG_REJECT_WITH_MESSAGE = 20; 415 private static final int MSG_SILENCE = 21; 416 private static final int MSG_PULL_EXTERNAL_CALL = 22; 417 private static final int MSG_SEND_CALL_EVENT = 23; 418 private static final int MSG_ON_EXTRAS_CHANGED = 24; 419 private static final int MSG_CREATE_CONNECTION_FAILED = 25; 420 private static final int MSG_ON_START_RTT = 26; 421 private static final int MSG_ON_STOP_RTT = 27; 422 private static final int MSG_RTT_UPGRADE_RESPONSE = 28; 423 private static final int MSG_CREATE_CONNECTION_COMPLETE = 29; 424 private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30; 425 private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31; 426 private static final int MSG_HANDOVER_FAILED = 32; 427 private static final int MSG_HANDOVER_COMPLETE = 33; 428 private static final int MSG_DEFLECT = 34; 429 private static final int MSG_CREATE_CONFERENCE = 35; 430 private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36; 431 private static final int MSG_CREATE_CONFERENCE_FAILED = 37; 432 private static final int MSG_REJECT_WITH_REASON = 38; 433 private static final int MSG_ADD_PARTICIPANT = 39; 434 private static final int MSG_EXPLICIT_CALL_TRANSFER = 40; 435 private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41; 436 private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42; 437 private static final int MSG_ON_USING_ALTERNATIVE_UI = 43; 438 private static final int MSG_ON_TRACKED_BY_NON_UI_SERVICE = 44; 439 private static final int MSG_ON_CALL_ENDPOINT_CHANGED = 45; 440 private static final int MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED = 46; 441 private static final int MSG_ON_MUTE_STATE_CHANGED = 47; 442 443 private static Connection sNullConnection; 444 445 private final Map<String, Connection> mConnectionById = new ConcurrentHashMap<>(); 446 private final Map<Connection, String> mIdByConnection = new ConcurrentHashMap<>(); 447 private final Map<String, Conference> mConferenceById = new ConcurrentHashMap<>(); 448 private final Map<Conference, String> mIdByConference = new ConcurrentHashMap<>(); 449 private final RemoteConnectionManager mRemoteConnectionManager = 450 new RemoteConnectionManager(this); 451 private final List<Runnable> mPreInitializationConnectionRequests = new ArrayList<>(); 452 private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter(); 453 454 private boolean mAreAccountsInitialized = false; 455 private Conference sNullConference; 456 private Object mIdSyncRoot = new Object(); 457 private int mId = 0; 458 459 private final IBinder mBinder = new IConnectionService.Stub() { 460 @Override 461 public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter, 462 Session.Info sessionInfo) { 463 Log.startSession(sessionInfo, SESSION_ADD_CS_ADAPTER); 464 try { 465 SomeArgs args = SomeArgs.obtain(); 466 args.arg1 = adapter; 467 args.arg2 = Log.createSubsession(); 468 mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, args).sendToTarget(); 469 } finally { 470 Log.endSession(); 471 } 472 } 473 474 public void removeConnectionServiceAdapter(IConnectionServiceAdapter adapter, 475 Session.Info sessionInfo) { 476 Log.startSession(sessionInfo, SESSION_REMOVE_CS_ADAPTER); 477 try { 478 SomeArgs args = SomeArgs.obtain(); 479 args.arg1 = adapter; 480 args.arg2 = Log.createSubsession(); 481 mHandler.obtainMessage(MSG_REMOVE_CONNECTION_SERVICE_ADAPTER, args).sendToTarget(); 482 } finally { 483 Log.endSession(); 484 } 485 } 486 487 @Override 488 public void createConnection( 489 PhoneAccountHandle connectionManagerPhoneAccount, 490 String id, 491 ConnectionRequest request, 492 boolean isIncoming, 493 boolean isUnknown, 494 Session.Info sessionInfo) { 495 Log.startSession(sessionInfo, SESSION_CREATE_CONN); 496 try { 497 SomeArgs args = SomeArgs.obtain(); 498 args.arg1 = connectionManagerPhoneAccount; 499 args.arg2 = id; 500 args.arg3 = request; 501 args.arg4 = Log.createSubsession(); 502 args.argi1 = isIncoming ? 1 : 0; 503 args.argi2 = isUnknown ? 1 : 0; 504 mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget(); 505 } finally { 506 Log.endSession(); 507 } 508 } 509 510 @Override 511 public void createConnectionComplete(String id, Session.Info sessionInfo) { 512 Log.startSession(sessionInfo, SESSION_CREATE_CONN_COMPLETE); 513 try { 514 SomeArgs args = SomeArgs.obtain(); 515 args.arg1 = id; 516 args.arg2 = Log.createSubsession(); 517 mHandler.obtainMessage(MSG_CREATE_CONNECTION_COMPLETE, args).sendToTarget(); 518 } finally { 519 Log.endSession(); 520 } 521 } 522 523 @Override 524 public void createConnectionFailed( 525 PhoneAccountHandle connectionManagerPhoneAccount, 526 String callId, 527 ConnectionRequest request, 528 boolean isIncoming, 529 Session.Info sessionInfo) { 530 Log.startSession(sessionInfo, SESSION_CREATE_CONN_FAILED); 531 try { 532 SomeArgs args = SomeArgs.obtain(); 533 args.arg1 = callId; 534 args.arg2 = request; 535 args.arg3 = Log.createSubsession(); 536 args.arg4 = connectionManagerPhoneAccount; 537 args.argi1 = isIncoming ? 1 : 0; 538 mHandler.obtainMessage(MSG_CREATE_CONNECTION_FAILED, args).sendToTarget(); 539 } finally { 540 Log.endSession(); 541 } 542 } 543 544 @Override 545 public void createConference( 546 PhoneAccountHandle connectionManagerPhoneAccount, 547 String id, 548 ConnectionRequest request, 549 boolean isIncoming, 550 boolean isUnknown, 551 Session.Info sessionInfo) { 552 Log.startSession(sessionInfo, SESSION_CREATE_CONF); 553 try { 554 SomeArgs args = SomeArgs.obtain(); 555 args.arg1 = connectionManagerPhoneAccount; 556 args.arg2 = id; 557 args.arg3 = request; 558 args.arg4 = Log.createSubsession(); 559 args.argi1 = isIncoming ? 1 : 0; 560 args.argi2 = isUnknown ? 1 : 0; 561 mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget(); 562 } finally { 563 Log.endSession(); 564 } 565 } 566 567 @Override 568 public void createConferenceComplete(String id, Session.Info sessionInfo) { 569 Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE); 570 try { 571 SomeArgs args = SomeArgs.obtain(); 572 args.arg1 = id; 573 args.arg2 = Log.createSubsession(); 574 mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget(); 575 } finally { 576 Log.endSession(); 577 } 578 } 579 580 @Override 581 public void createConferenceFailed( 582 PhoneAccountHandle connectionManagerPhoneAccount, 583 String callId, 584 ConnectionRequest request, 585 boolean isIncoming, 586 Session.Info sessionInfo) { 587 Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED); 588 try { 589 SomeArgs args = SomeArgs.obtain(); 590 args.arg1 = callId; 591 args.arg2 = request; 592 args.arg3 = Log.createSubsession(); 593 args.arg4 = connectionManagerPhoneAccount; 594 args.argi1 = isIncoming ? 1 : 0; 595 mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget(); 596 } finally { 597 Log.endSession(); 598 } 599 } 600 601 @Override 602 public void handoverFailed(String callId, ConnectionRequest request, int reason, 603 Session.Info sessionInfo) { 604 Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED); 605 try { 606 SomeArgs args = SomeArgs.obtain(); 607 args.arg1 = callId; 608 args.arg2 = request; 609 args.arg3 = Log.createSubsession(); 610 args.arg4 = reason; 611 mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget(); 612 } finally { 613 Log.endSession(); 614 } 615 } 616 617 @Override 618 public void handoverComplete(String callId, Session.Info sessionInfo) { 619 Log.startSession(sessionInfo, SESSION_HANDOVER_COMPLETE); 620 try { 621 SomeArgs args = SomeArgs.obtain(); 622 args.arg1 = callId; 623 args.arg2 = Log.createSubsession(); 624 mHandler.obtainMessage(MSG_HANDOVER_COMPLETE, args).sendToTarget(); 625 } finally { 626 Log.endSession(); 627 } 628 } 629 630 @Override 631 public void abort(String callId, Session.Info sessionInfo) { 632 Log.startSession(sessionInfo, SESSION_ABORT); 633 try { 634 SomeArgs args = SomeArgs.obtain(); 635 args.arg1 = callId; 636 args.arg2 = Log.createSubsession(); 637 mHandler.obtainMessage(MSG_ABORT, args).sendToTarget(); 638 } finally { 639 Log.endSession(); 640 } 641 } 642 643 @Override 644 public void answerVideo(String callId, int videoState, Session.Info sessionInfo) { 645 Log.startSession(sessionInfo, SESSION_ANSWER_VIDEO); 646 try { 647 SomeArgs args = SomeArgs.obtain(); 648 args.arg1 = callId; 649 args.arg2 = Log.createSubsession(); 650 args.argi1 = videoState; 651 mHandler.obtainMessage(MSG_ANSWER_VIDEO, args).sendToTarget(); 652 } finally { 653 Log.endSession(); 654 } 655 } 656 657 @Override 658 public void answer(String callId, Session.Info sessionInfo) { 659 Log.startSession(sessionInfo, SESSION_ANSWER); 660 try { 661 SomeArgs args = SomeArgs.obtain(); 662 args.arg1 = callId; 663 args.arg2 = Log.createSubsession(); 664 mHandler.obtainMessage(MSG_ANSWER, args).sendToTarget(); 665 } finally { 666 Log.endSession(); 667 } 668 } 669 670 @Override 671 public void deflect(String callId, Uri address, Session.Info sessionInfo) { 672 Log.startSession(sessionInfo, SESSION_DEFLECT); 673 try { 674 SomeArgs args = SomeArgs.obtain(); 675 args.arg1 = callId; 676 args.arg2 = address; 677 args.arg3 = Log.createSubsession(); 678 mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget(); 679 } finally { 680 Log.endSession(); 681 } 682 } 683 684 @Override 685 public void reject(String callId, Session.Info sessionInfo) { 686 Log.startSession(sessionInfo, SESSION_REJECT); 687 try { 688 SomeArgs args = SomeArgs.obtain(); 689 args.arg1 = callId; 690 args.arg2 = Log.createSubsession(); 691 mHandler.obtainMessage(MSG_REJECT, args).sendToTarget(); 692 } finally { 693 Log.endSession(); 694 } 695 } 696 697 @Override 698 public void rejectWithReason(String callId, 699 @android.telecom.Call.RejectReason int rejectReason, Session.Info sessionInfo) { 700 Log.startSession(sessionInfo, SESSION_REJECT); 701 try { 702 SomeArgs args = SomeArgs.obtain(); 703 args.arg1 = callId; 704 args.argi1 = rejectReason; 705 args.arg2 = Log.createSubsession(); 706 mHandler.obtainMessage(MSG_REJECT_WITH_REASON, args).sendToTarget(); 707 } finally { 708 Log.endSession(); 709 } 710 } 711 712 @Override 713 public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) { 714 Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE); 715 try { 716 SomeArgs args = SomeArgs.obtain(); 717 args.arg1 = callId; 718 args.arg2 = message; 719 args.arg3 = Log.createSubsession(); 720 mHandler.obtainMessage(MSG_REJECT_WITH_MESSAGE, args).sendToTarget(); 721 } finally { 722 Log.endSession(); 723 } 724 } 725 726 @Override 727 public void transfer(@NonNull String callId, @NonNull Uri number, 728 boolean isConfirmationRequired, Session.Info sessionInfo) { 729 Log.startSession(sessionInfo, SESSION_TRANSFER); 730 try { 731 SomeArgs args = SomeArgs.obtain(); 732 args.arg1 = callId; 733 args.arg2 = number; 734 args.argi1 = isConfirmationRequired ? 1 : 0; 735 args.arg3 = Log.createSubsession(); 736 mHandler.obtainMessage(MSG_EXPLICIT_CALL_TRANSFER, args).sendToTarget(); 737 } finally { 738 Log.endSession(); 739 } 740 } 741 742 @Override 743 public void consultativeTransfer(@NonNull String callId, @NonNull String otherCallId, 744 Session.Info sessionInfo) { 745 Log.startSession(sessionInfo, SESSION_CONSULTATIVE_TRANSFER); 746 try { 747 SomeArgs args = SomeArgs.obtain(); 748 args.arg1 = callId; 749 args.arg2 = otherCallId; 750 args.arg3 = Log.createSubsession(); 751 mHandler.obtainMessage( 752 MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE, args).sendToTarget(); 753 } finally { 754 Log.endSession(); 755 } 756 } 757 758 @Override 759 public void silence(String callId, Session.Info sessionInfo) { 760 Log.startSession(sessionInfo, SESSION_SILENCE); 761 try { 762 SomeArgs args = SomeArgs.obtain(); 763 args.arg1 = callId; 764 args.arg2 = Log.createSubsession(); 765 mHandler.obtainMessage(MSG_SILENCE, args).sendToTarget(); 766 } finally { 767 Log.endSession(); 768 } 769 } 770 771 @Override 772 public void disconnect(String callId, Session.Info sessionInfo) { 773 Log.startSession(sessionInfo, SESSION_DISCONNECT); 774 try { 775 SomeArgs args = SomeArgs.obtain(); 776 args.arg1 = callId; 777 args.arg2 = Log.createSubsession(); 778 mHandler.obtainMessage(MSG_DISCONNECT, args).sendToTarget(); 779 } finally { 780 Log.endSession(); 781 } 782 } 783 784 @Override 785 public void hold(String callId, Session.Info sessionInfo) { 786 Log.startSession(sessionInfo, SESSION_HOLD); 787 try { 788 SomeArgs args = SomeArgs.obtain(); 789 args.arg1 = callId; 790 args.arg2 = Log.createSubsession(); 791 mHandler.obtainMessage(MSG_HOLD, args).sendToTarget(); 792 } finally { 793 Log.endSession(); 794 } 795 } 796 797 @Override 798 public void unhold(String callId, Session.Info sessionInfo) { 799 Log.startSession(sessionInfo, SESSION_UNHOLD); 800 try { 801 SomeArgs args = SomeArgs.obtain(); 802 args.arg1 = callId; 803 args.arg2 = Log.createSubsession(); 804 mHandler.obtainMessage(MSG_UNHOLD, args).sendToTarget(); 805 } finally { 806 Log.endSession(); 807 } 808 } 809 810 @Override 811 public void onCallAudioStateChanged(String callId, CallAudioState callAudioState, 812 Session.Info sessionInfo) { 813 Log.startSession(sessionInfo, SESSION_CALL_AUDIO_SC); 814 try { 815 SomeArgs args = SomeArgs.obtain(); 816 args.arg1 = callId; 817 args.arg2 = callAudioState; 818 args.arg3 = Log.createSubsession(); 819 mHandler.obtainMessage(MSG_ON_CALL_AUDIO_STATE_CHANGED, args).sendToTarget(); 820 } finally { 821 Log.endSession(); 822 } 823 } 824 825 @Override 826 public void onCallEndpointChanged(String callId, CallEndpoint callEndpoint, 827 Session.Info sessionInfo) { 828 Log.startSession(sessionInfo, SESSION_CALL_ENDPOINT_CHANGED); 829 try { 830 SomeArgs args = SomeArgs.obtain(); 831 args.arg1 = callId; 832 args.arg2 = callEndpoint; 833 args.arg3 = Log.createSubsession(); 834 mHandler.obtainMessage(MSG_ON_CALL_ENDPOINT_CHANGED, args).sendToTarget(); 835 } finally { 836 Log.endSession(); 837 } 838 } 839 840 @Override 841 public void onAvailableCallEndpointsChanged(String callId, 842 List<CallEndpoint> availableCallEndpoints, Session.Info sessionInfo) { 843 Log.startSession(sessionInfo, SESSION_AVAILABLE_CALL_ENDPOINTS_CHANGED); 844 try { 845 SomeArgs args = SomeArgs.obtain(); 846 args.arg1 = callId; 847 args.arg2 = availableCallEndpoints; 848 args.arg3 = Log.createSubsession(); 849 mHandler.obtainMessage(MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED, args) 850 .sendToTarget(); 851 } finally { 852 Log.endSession(); 853 } 854 } 855 856 @Override 857 public void onMuteStateChanged(String callId, boolean isMuted, Session.Info sessionInfo) { 858 Log.startSession(sessionInfo, SESSION_MUTE_STATE_CHANGED); 859 try { 860 SomeArgs args = SomeArgs.obtain(); 861 args.arg1 = callId; 862 args.arg2 = isMuted; 863 args.arg3 = Log.createSubsession(); 864 mHandler.obtainMessage(MSG_ON_MUTE_STATE_CHANGED, args).sendToTarget(); 865 } finally { 866 Log.endSession(); 867 } 868 } 869 870 @Override 871 public void onUsingAlternativeUi(String callId, boolean usingAlternativeUiShowing, 872 Session.Info sessionInfo) { 873 Log.startSession(sessionInfo, SESSION_USING_ALTERNATIVE_UI); 874 try { 875 SomeArgs args = SomeArgs.obtain(); 876 args.arg1 = callId; 877 args.arg2 = usingAlternativeUiShowing; 878 args.arg3 = Log.createSubsession(); 879 mHandler.obtainMessage(MSG_ON_USING_ALTERNATIVE_UI, args).sendToTarget(); 880 } finally { 881 Log.endSession(); 882 } 883 } 884 885 @Override 886 public void onTrackedByNonUiService(String callId, boolean isTracked, 887 Session.Info sessionInfo) { 888 Log.startSession(sessionInfo, SESSION_TRACKED_BY_NON_UI_SERVICE); 889 try { 890 SomeArgs args = SomeArgs.obtain(); 891 args.arg1 = callId; 892 args.arg2 = isTracked; 893 args.arg3 = Log.createSubsession(); 894 mHandler.obtainMessage(MSG_ON_TRACKED_BY_NON_UI_SERVICE, args).sendToTarget(); 895 } finally { 896 Log.endSession(); 897 } 898 } 899 900 @Override 901 public void playDtmfTone(String callId, char digit, Session.Info sessionInfo) { 902 Log.startSession(sessionInfo, SESSION_PLAY_DTMF); 903 try { 904 SomeArgs args = SomeArgs.obtain(); 905 args.arg1 = digit; 906 args.arg2 = callId; 907 args.arg3 = Log.createSubsession(); 908 mHandler.obtainMessage(MSG_PLAY_DTMF_TONE, args).sendToTarget(); 909 } finally { 910 Log.endSession(); 911 } 912 } 913 914 @Override 915 public void stopDtmfTone(String callId, Session.Info sessionInfo) { 916 Log.startSession(sessionInfo, SESSION_STOP_DTMF); 917 try { 918 SomeArgs args = SomeArgs.obtain(); 919 args.arg1 = callId; 920 args.arg2 = Log.createSubsession(); 921 mHandler.obtainMessage(MSG_STOP_DTMF_TONE, args).sendToTarget(); 922 } finally { 923 Log.endSession(); 924 } 925 } 926 927 @Override 928 public void conference(String callId1, String callId2, Session.Info sessionInfo) { 929 Log.startSession(sessionInfo, SESSION_CONFERENCE); 930 try { 931 SomeArgs args = SomeArgs.obtain(); 932 args.arg1 = callId1; 933 args.arg2 = callId2; 934 args.arg3 = Log.createSubsession(); 935 mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); 936 } finally { 937 Log.endSession(); 938 } 939 } 940 941 @Override 942 public void splitFromConference(String callId, Session.Info sessionInfo) { 943 Log.startSession(sessionInfo, SESSION_SPLIT_CONFERENCE); 944 try { 945 SomeArgs args = SomeArgs.obtain(); 946 args.arg1 = callId; 947 args.arg2 = Log.createSubsession(); 948 mHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget(); 949 } finally { 950 Log.endSession(); 951 } 952 } 953 954 @Override 955 public void mergeConference(String callId, Session.Info sessionInfo) { 956 Log.startSession(sessionInfo, SESSION_MERGE_CONFERENCE); 957 try { 958 SomeArgs args = SomeArgs.obtain(); 959 args.arg1 = callId; 960 args.arg2 = Log.createSubsession(); 961 mHandler.obtainMessage(MSG_MERGE_CONFERENCE, args).sendToTarget(); 962 } finally { 963 Log.endSession(); 964 } 965 } 966 967 @Override 968 public void swapConference(String callId, Session.Info sessionInfo) { 969 Log.startSession(sessionInfo, SESSION_SWAP_CONFERENCE); 970 try { 971 SomeArgs args = SomeArgs.obtain(); 972 args.arg1 = callId; 973 args.arg2 = Log.createSubsession(); 974 mHandler.obtainMessage(MSG_SWAP_CONFERENCE, args).sendToTarget(); 975 } finally { 976 Log.endSession(); 977 } 978 } 979 980 @Override 981 public void addConferenceParticipants(String callId, List<Uri> participants, 982 Session.Info sessionInfo) { 983 Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT); 984 try { 985 SomeArgs args = SomeArgs.obtain(); 986 args.arg1 = callId; 987 args.arg2 = participants; 988 args.arg3 = Log.createSubsession(); 989 mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget(); 990 } finally { 991 Log.endSession(); 992 } 993 } 994 995 @Override 996 public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) { 997 Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT); 998 try { 999 SomeArgs args = SomeArgs.obtain(); 1000 args.arg1 = callId; 1001 args.arg2 = Log.createSubsession(); 1002 args.argi1 = proceed ? 1 : 0; 1003 mHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget(); 1004 } finally { 1005 Log.endSession(); 1006 } 1007 } 1008 1009 @Override 1010 public void pullExternalCall(String callId, Session.Info sessionInfo) { 1011 Log.startSession(sessionInfo, SESSION_PULL_EXTERNAL_CALL); 1012 try { 1013 SomeArgs args = SomeArgs.obtain(); 1014 args.arg1 = callId; 1015 args.arg2 = Log.createSubsession(); 1016 mHandler.obtainMessage(MSG_PULL_EXTERNAL_CALL, args).sendToTarget(); 1017 } finally { 1018 Log.endSession(); 1019 } 1020 } 1021 1022 @Override 1023 public void sendCallEvent(String callId, String event, Bundle extras, 1024 Session.Info sessionInfo) { 1025 Log.startSession(sessionInfo, SESSION_SEND_CALL_EVENT); 1026 try { 1027 SomeArgs args = SomeArgs.obtain(); 1028 args.arg1 = callId; 1029 args.arg2 = event; 1030 args.arg3 = extras; 1031 args.arg4 = Log.createSubsession(); 1032 mHandler.obtainMessage(MSG_SEND_CALL_EVENT, args).sendToTarget(); 1033 } finally { 1034 Log.endSession(); 1035 } 1036 } 1037 1038 @Override 1039 public void onCallFilteringCompleted(String callId, 1040 Connection.CallFilteringCompletionInfo completionInfo, 1041 Session.Info sessionInfo) { 1042 Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED); 1043 try { 1044 SomeArgs args = SomeArgs.obtain(); 1045 args.arg1 = callId; 1046 args.arg2 = completionInfo; 1047 args.arg3 = Log.createSubsession(); 1048 mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget(); 1049 } finally { 1050 Log.endSession(); 1051 } 1052 } 1053 1054 @Override 1055 public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) { 1056 Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED); 1057 try { 1058 SomeArgs args = SomeArgs.obtain(); 1059 args.arg1 = callId; 1060 args.arg2 = extras; 1061 args.arg3 = Log.createSubsession(); 1062 mHandler.obtainMessage(MSG_ON_EXTRAS_CHANGED, args).sendToTarget(); 1063 } finally { 1064 Log.endSession(); 1065 } 1066 } 1067 1068 @Override 1069 public void startRtt(String callId, ParcelFileDescriptor fromInCall, 1070 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException { 1071 Log.startSession(sessionInfo, SESSION_START_RTT); 1072 try { 1073 SomeArgs args = SomeArgs.obtain(); 1074 args.arg1 = callId; 1075 args.arg2 = new Connection.RttTextStream(toInCall, fromInCall); 1076 args.arg3 = Log.createSubsession(); 1077 mHandler.obtainMessage(MSG_ON_START_RTT, args).sendToTarget(); 1078 } finally { 1079 Log.endSession(); 1080 } 1081 } 1082 1083 @Override 1084 public void stopRtt(String callId, Session.Info sessionInfo) throws RemoteException { 1085 Log.startSession(sessionInfo, SESSION_STOP_RTT); 1086 try { 1087 SomeArgs args = SomeArgs.obtain(); 1088 args.arg1 = callId; 1089 args.arg2 = Log.createSubsession(); 1090 mHandler.obtainMessage(MSG_ON_STOP_RTT, args).sendToTarget(); 1091 } finally { 1092 Log.endSession(); 1093 } 1094 } 1095 1096 @Override 1097 public void respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall, 1098 ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException { 1099 Log.startSession(sessionInfo, SESSION_RTT_UPGRADE_RESPONSE); 1100 try { 1101 SomeArgs args = SomeArgs.obtain(); 1102 args.arg1 = callId; 1103 if (toInCall == null || fromInCall == null) { 1104 args.arg2 = null; 1105 } else { 1106 args.arg2 = new Connection.RttTextStream(toInCall, fromInCall); 1107 } 1108 args.arg3 = Log.createSubsession(); 1109 mHandler.obtainMessage(MSG_RTT_UPGRADE_RESPONSE, args).sendToTarget(); 1110 } finally { 1111 Log.endSession(); 1112 } 1113 } 1114 1115 @Override 1116 public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException { 1117 Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_LOST); 1118 try { 1119 mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_LOST).sendToTarget(); 1120 } finally { 1121 Log.endSession(); 1122 } 1123 } 1124 1125 @Override 1126 public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException { 1127 Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_GAINED); 1128 try { 1129 mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_GAINED).sendToTarget(); 1130 } finally { 1131 Log.endSession(); 1132 } 1133 } 1134 }; 1135 1136 private final Handler mHandler = new Handler(Looper.getMainLooper()) { 1137 @Override 1138 public void handleMessage(Message msg) { 1139 switch (msg.what) { 1140 case MSG_ADD_CONNECTION_SERVICE_ADAPTER: { 1141 SomeArgs args = (SomeArgs) msg.obj; 1142 try { 1143 IConnectionServiceAdapter adapter = (IConnectionServiceAdapter) args.arg1; 1144 Log.continueSession((Session) args.arg2, 1145 SESSION_HANDLER + SESSION_ADD_CS_ADAPTER); 1146 mAdapter.addAdapter(adapter); 1147 onAdapterAttached(); 1148 } finally { 1149 args.recycle(); 1150 Log.endSession(); 1151 } 1152 break; 1153 } 1154 case MSG_REMOVE_CONNECTION_SERVICE_ADAPTER: { 1155 SomeArgs args = (SomeArgs) msg.obj; 1156 try { 1157 Log.continueSession((Session) args.arg2, 1158 SESSION_HANDLER + SESSION_REMOVE_CS_ADAPTER); 1159 mAdapter.removeAdapter((IConnectionServiceAdapter) args.arg1); 1160 } finally { 1161 args.recycle(); 1162 Log.endSession(); 1163 } 1164 break; 1165 } 1166 case MSG_CREATE_CONNECTION: { 1167 SomeArgs args = (SomeArgs) msg.obj; 1168 Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN); 1169 try { 1170 final PhoneAccountHandle connectionManagerPhoneAccount = 1171 (PhoneAccountHandle) args.arg1; 1172 final String id = (String) args.arg2; 1173 final ConnectionRequest request = (ConnectionRequest) args.arg3; 1174 final boolean isIncoming = args.argi1 == 1; 1175 final boolean isUnknown = args.argi2 == 1; 1176 if (!mAreAccountsInitialized) { 1177 Log.d(this, "Enqueueing pre-init request %s", id); 1178 mPreInitializationConnectionRequests.add( 1179 new android.telecom.Logging.Runnable( 1180 SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR", 1181 null /*lock*/) { 1182 @Override 1183 public void loggedRun() { 1184 createConnection( 1185 connectionManagerPhoneAccount, 1186 id, 1187 request, 1188 isIncoming, 1189 isUnknown); 1190 } 1191 }.prepare()); 1192 } else { 1193 createConnection( 1194 connectionManagerPhoneAccount, 1195 id, 1196 request, 1197 isIncoming, 1198 isUnknown); 1199 } 1200 } finally { 1201 args.recycle(); 1202 Log.endSession(); 1203 } 1204 break; 1205 } 1206 case MSG_CREATE_CONNECTION_COMPLETE: { 1207 SomeArgs args = (SomeArgs) msg.obj; 1208 Log.continueSession((Session) args.arg2, 1209 SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE); 1210 try { 1211 final String id = (String) args.arg1; 1212 if (!mAreAccountsInitialized) { 1213 Log.d(this, "Enqueueing pre-init request %s", id); 1214 mPreInitializationConnectionRequests.add( 1215 new android.telecom.Logging.Runnable( 1216 SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE 1217 + ".pICR", 1218 null /*lock*/) { 1219 @Override 1220 public void loggedRun() { 1221 notifyCreateConnectionComplete(id); 1222 } 1223 }.prepare()); 1224 } else { 1225 notifyCreateConnectionComplete(id); 1226 } 1227 } finally { 1228 args.recycle(); 1229 Log.endSession(); 1230 } 1231 break; 1232 } 1233 case MSG_CREATE_CONNECTION_FAILED: { 1234 SomeArgs args = (SomeArgs) msg.obj; 1235 Log.continueSession((Session) args.arg3, SESSION_HANDLER + 1236 SESSION_CREATE_CONN_FAILED); 1237 try { 1238 final String id = (String) args.arg1; 1239 final ConnectionRequest request = (ConnectionRequest) args.arg2; 1240 final boolean isIncoming = args.argi1 == 1; 1241 final PhoneAccountHandle connectionMgrPhoneAccount = 1242 (PhoneAccountHandle) args.arg4; 1243 if (!mAreAccountsInitialized) { 1244 Log.d(this, "Enqueueing pre-init request %s", id); 1245 mPreInitializationConnectionRequests.add( 1246 new android.telecom.Logging.Runnable( 1247 SESSION_HANDLER + SESSION_CREATE_CONN_FAILED + ".pICR", 1248 null /*lock*/) { 1249 @Override 1250 public void loggedRun() { 1251 createConnectionFailed(connectionMgrPhoneAccount, id, 1252 request, isIncoming); 1253 } 1254 }.prepare()); 1255 } else { 1256 Log.i(this, "createConnectionFailed %s", id); 1257 createConnectionFailed(connectionMgrPhoneAccount, id, request, 1258 isIncoming); 1259 } 1260 } finally { 1261 args.recycle(); 1262 Log.endSession(); 1263 } 1264 break; 1265 } 1266 case MSG_CREATE_CONFERENCE: { 1267 SomeArgs args = (SomeArgs) msg.obj; 1268 Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN); 1269 try { 1270 final PhoneAccountHandle connectionManagerPhoneAccount = 1271 (PhoneAccountHandle) args.arg1; 1272 final String id = (String) args.arg2; 1273 final ConnectionRequest request = (ConnectionRequest) args.arg3; 1274 final boolean isIncoming = args.argi1 == 1; 1275 final boolean isUnknown = args.argi2 == 1; 1276 if (!mAreAccountsInitialized) { 1277 Log.d(this, "Enqueueing pre-initconference request %s", id); 1278 mPreInitializationConnectionRequests.add( 1279 new android.telecom.Logging.Runnable( 1280 SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR", 1281 null /*lock*/) { 1282 @Override 1283 public void loggedRun() { 1284 createConference(connectionManagerPhoneAccount, 1285 id, 1286 request, 1287 isIncoming, 1288 isUnknown); 1289 } 1290 }.prepare()); 1291 } else { 1292 createConference(connectionManagerPhoneAccount, 1293 id, 1294 request, 1295 isIncoming, 1296 isUnknown); 1297 } 1298 } finally { 1299 args.recycle(); 1300 Log.endSession(); 1301 } 1302 break; 1303 } 1304 case MSG_CREATE_CONFERENCE_COMPLETE: { 1305 SomeArgs args = (SomeArgs) msg.obj; 1306 Log.continueSession((Session) args.arg2, 1307 SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE); 1308 try { 1309 final String id = (String) args.arg1; 1310 if (!mAreAccountsInitialized) { 1311 Log.d(this, "Enqueueing pre-init conference request %s", id); 1312 mPreInitializationConnectionRequests.add( 1313 new android.telecom.Logging.Runnable( 1314 SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE 1315 + ".pIConfR", 1316 null /*lock*/) { 1317 @Override 1318 public void loggedRun() { 1319 notifyCreateConferenceComplete(id); 1320 } 1321 }.prepare()); 1322 } else { 1323 notifyCreateConferenceComplete(id); 1324 } 1325 } finally { 1326 args.recycle(); 1327 Log.endSession(); 1328 } 1329 break; 1330 } 1331 case MSG_CREATE_CONFERENCE_FAILED: { 1332 SomeArgs args = (SomeArgs) msg.obj; 1333 Log.continueSession((Session) args.arg3, SESSION_HANDLER + 1334 SESSION_CREATE_CONN_FAILED); 1335 try { 1336 final String id = (String) args.arg1; 1337 final ConnectionRequest request = (ConnectionRequest) args.arg2; 1338 final boolean isIncoming = args.argi1 == 1; 1339 final PhoneAccountHandle connectionMgrPhoneAccount = 1340 (PhoneAccountHandle) args.arg4; 1341 if (!mAreAccountsInitialized) { 1342 Log.d(this, "Enqueueing pre-init conference request %s", id); 1343 mPreInitializationConnectionRequests.add( 1344 new android.telecom.Logging.Runnable( 1345 SESSION_HANDLER + SESSION_CREATE_CONF_FAILED 1346 + ".pIConfR", 1347 null /*lock*/) { 1348 @Override 1349 public void loggedRun() { 1350 createConferenceFailed(connectionMgrPhoneAccount, id, 1351 request, isIncoming); 1352 } 1353 }.prepare()); 1354 } else { 1355 Log.i(this, "createConferenceFailed %s", id); 1356 createConferenceFailed(connectionMgrPhoneAccount, id, request, 1357 isIncoming); 1358 } 1359 } finally { 1360 args.recycle(); 1361 Log.endSession(); 1362 } 1363 break; 1364 } 1365 1366 case MSG_HANDOVER_FAILED: { 1367 SomeArgs args = (SomeArgs) msg.obj; 1368 Log.continueSession((Session) args.arg3, SESSION_HANDLER + 1369 SESSION_HANDOVER_FAILED); 1370 try { 1371 final String id = (String) args.arg1; 1372 final ConnectionRequest request = (ConnectionRequest) args.arg2; 1373 final int reason = (int) args.arg4; 1374 if (!mAreAccountsInitialized) { 1375 Log.d(this, "Enqueueing pre-init request %s", id); 1376 mPreInitializationConnectionRequests.add( 1377 new android.telecom.Logging.Runnable( 1378 SESSION_HANDLER 1379 + SESSION_HANDOVER_FAILED + ".pICR", 1380 null /*lock*/) { 1381 @Override 1382 public void loggedRun() { 1383 handoverFailed(id, request, reason); 1384 } 1385 }.prepare()); 1386 } else { 1387 Log.i(this, "createConnectionFailed %s", id); 1388 handoverFailed(id, request, reason); 1389 } 1390 } finally { 1391 args.recycle(); 1392 Log.endSession(); 1393 } 1394 break; 1395 } 1396 case MSG_ABORT: { 1397 SomeArgs args = (SomeArgs) msg.obj; 1398 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT); 1399 try { 1400 abort((String) args.arg1); 1401 } finally { 1402 args.recycle(); 1403 Log.endSession(); 1404 } 1405 break; 1406 } 1407 case MSG_ANSWER: { 1408 SomeArgs args = (SomeArgs) msg.obj; 1409 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ANSWER); 1410 try { 1411 answer((String) args.arg1); 1412 } finally { 1413 args.recycle(); 1414 Log.endSession(); 1415 } 1416 break; 1417 } 1418 case MSG_ANSWER_VIDEO: { 1419 SomeArgs args = (SomeArgs) msg.obj; 1420 Log.continueSession((Session) args.arg2, 1421 SESSION_HANDLER + SESSION_ANSWER_VIDEO); 1422 try { 1423 String callId = (String) args.arg1; 1424 int videoState = args.argi1; 1425 answerVideo(callId, videoState); 1426 } finally { 1427 args.recycle(); 1428 Log.endSession(); 1429 } 1430 break; 1431 } 1432 case MSG_DEFLECT: { 1433 SomeArgs args = (SomeArgs) msg.obj; 1434 Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT); 1435 try { 1436 deflect((String) args.arg1, (Uri) args.arg2); 1437 } finally { 1438 args.recycle(); 1439 Log.endSession(); 1440 } 1441 break; 1442 } 1443 case MSG_REJECT: { 1444 SomeArgs args = (SomeArgs) msg.obj; 1445 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT); 1446 try { 1447 reject((String) args.arg1); 1448 } finally { 1449 args.recycle(); 1450 Log.endSession(); 1451 } 1452 break; 1453 } 1454 case MSG_REJECT_WITH_REASON: { 1455 SomeArgs args = (SomeArgs) msg.obj; 1456 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT); 1457 try { 1458 reject((String) args.arg1, args.argi1); 1459 } finally { 1460 args.recycle(); 1461 Log.endSession(); 1462 } 1463 break; 1464 } 1465 case MSG_REJECT_WITH_MESSAGE: { 1466 SomeArgs args = (SomeArgs) msg.obj; 1467 Log.continueSession((Session) args.arg3, 1468 SESSION_HANDLER + SESSION_REJECT_MESSAGE); 1469 try { 1470 reject((String) args.arg1, (String) args.arg2); 1471 } finally { 1472 args.recycle(); 1473 Log.endSession(); 1474 } 1475 break; 1476 } 1477 case MSG_EXPLICIT_CALL_TRANSFER: { 1478 SomeArgs args = (SomeArgs) msg.obj; 1479 Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_TRANSFER); 1480 try { 1481 final boolean isConfirmationRequired = args.argi1 == 1; 1482 transfer((String) args.arg1, (Uri) args.arg2, isConfirmationRequired); 1483 } finally { 1484 args.recycle(); 1485 Log.endSession(); 1486 } 1487 break; 1488 } 1489 case MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE: { 1490 SomeArgs args = (SomeArgs) msg.obj; 1491 Log.continueSession( 1492 (Session) args.arg3, SESSION_HANDLER + SESSION_CONSULTATIVE_TRANSFER); 1493 try { 1494 consultativeTransfer((String) args.arg1, (String) args.arg2); 1495 } finally { 1496 args.recycle(); 1497 Log.endSession(); 1498 } 1499 break; 1500 } 1501 case MSG_DISCONNECT: { 1502 SomeArgs args = (SomeArgs) msg.obj; 1503 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT); 1504 try { 1505 disconnect((String) args.arg1); 1506 } finally { 1507 args.recycle(); 1508 Log.endSession(); 1509 } 1510 break; 1511 } 1512 case MSG_SILENCE: { 1513 SomeArgs args = (SomeArgs) msg.obj; 1514 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_SILENCE); 1515 try { 1516 silence((String) args.arg1); 1517 } finally { 1518 args.recycle(); 1519 Log.endSession(); 1520 } 1521 break; 1522 } 1523 case MSG_HOLD: { 1524 SomeArgs args = (SomeArgs) msg.obj; 1525 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT); 1526 try { 1527 hold((String) args.arg1); 1528 } finally { 1529 args.recycle(); 1530 Log.endSession(); 1531 } 1532 break; 1533 } 1534 case MSG_UNHOLD: { 1535 SomeArgs args = (SomeArgs) msg.obj; 1536 Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_UNHOLD); 1537 try { 1538 unhold((String) args.arg1); 1539 } finally { 1540 args.recycle(); 1541 Log.endSession(); 1542 } 1543 break; 1544 } 1545 case MSG_ON_CALL_AUDIO_STATE_CHANGED: { 1546 SomeArgs args = (SomeArgs) msg.obj; 1547 Log.continueSession((Session) args.arg3, 1548 SESSION_HANDLER + SESSION_CALL_AUDIO_SC); 1549 try { 1550 String callId = (String) args.arg1; 1551 CallAudioState audioState = (CallAudioState) args.arg2; 1552 onCallAudioStateChanged(callId, new CallAudioState(audioState)); 1553 } finally { 1554 args.recycle(); 1555 Log.endSession(); 1556 } 1557 break; 1558 } 1559 case MSG_ON_USING_ALTERNATIVE_UI: { 1560 SomeArgs args = (SomeArgs) msg.obj; 1561 Log.continueSession((Session) args.arg3, 1562 SESSION_HANDLER + SESSION_USING_ALTERNATIVE_UI); 1563 try { 1564 String callId = (String) args.arg1; 1565 boolean isUsingAlternativeUi = (boolean) args.arg2; 1566 onUsingAlternativeUi(callId, isUsingAlternativeUi); 1567 } finally { 1568 args.recycle(); 1569 Log.endSession(); 1570 } 1571 break; 1572 } 1573 case MSG_ON_TRACKED_BY_NON_UI_SERVICE: { 1574 SomeArgs args = (SomeArgs) msg.obj; 1575 Log.continueSession((Session) args.arg3, 1576 SESSION_HANDLER + SESSION_TRACKED_BY_NON_UI_SERVICE); 1577 try { 1578 String callId = (String) args.arg1; 1579 boolean isTracked = (boolean) args.arg2; 1580 onTrackedByNonUiService(callId, isTracked); 1581 } finally { 1582 args.recycle(); 1583 Log.endSession(); 1584 } 1585 break; 1586 } 1587 case MSG_PLAY_DTMF_TONE: { 1588 SomeArgs args = (SomeArgs) msg.obj; 1589 try { 1590 Log.continueSession((Session) args.arg3, 1591 SESSION_HANDLER + SESSION_PLAY_DTMF); 1592 playDtmfTone((String) args.arg2, (char) args.arg1); 1593 } finally { 1594 args.recycle(); 1595 Log.endSession(); 1596 } 1597 break; 1598 } 1599 case MSG_STOP_DTMF_TONE: { 1600 SomeArgs args = (SomeArgs) msg.obj; 1601 try { 1602 Log.continueSession((Session) args.arg2, 1603 SESSION_HANDLER + SESSION_STOP_DTMF); 1604 stopDtmfTone((String) args.arg1); 1605 } finally { 1606 args.recycle(); 1607 Log.endSession(); 1608 } 1609 break; 1610 } 1611 case MSG_CONFERENCE: { 1612 SomeArgs args = (SomeArgs) msg.obj; 1613 try { 1614 Log.continueSession((Session) args.arg3, 1615 SESSION_HANDLER + SESSION_CONFERENCE); 1616 String callId1 = (String) args.arg1; 1617 String callId2 = (String) args.arg2; 1618 conference(callId1, callId2); 1619 } finally { 1620 args.recycle(); 1621 Log.endSession(); 1622 } 1623 break; 1624 } 1625 case MSG_SPLIT_FROM_CONFERENCE: { 1626 SomeArgs args = (SomeArgs) msg.obj; 1627 try { 1628 Log.continueSession((Session) args.arg2, 1629 SESSION_HANDLER + SESSION_SPLIT_CONFERENCE); 1630 splitFromConference((String) args.arg1); 1631 } finally { 1632 args.recycle(); 1633 Log.endSession(); 1634 } 1635 break; 1636 } 1637 case MSG_MERGE_CONFERENCE: { 1638 SomeArgs args = (SomeArgs) msg.obj; 1639 try { 1640 Log.continueSession((Session) args.arg2, 1641 SESSION_HANDLER + SESSION_MERGE_CONFERENCE); 1642 mergeConference((String) args.arg1); 1643 } finally { 1644 args.recycle(); 1645 Log.endSession(); 1646 } 1647 break; 1648 } 1649 case MSG_SWAP_CONFERENCE: { 1650 SomeArgs args = (SomeArgs) msg.obj; 1651 try { 1652 Log.continueSession((Session) args.arg2, 1653 SESSION_HANDLER + SESSION_SWAP_CONFERENCE); 1654 swapConference((String) args.arg1); 1655 } finally { 1656 args.recycle(); 1657 Log.endSession(); 1658 } 1659 break; 1660 } 1661 case MSG_ADD_PARTICIPANT: { 1662 SomeArgs args = (SomeArgs) msg.obj; 1663 try { 1664 Log.continueSession((Session) args.arg3, 1665 SESSION_HANDLER + SESSION_ADD_PARTICIPANT); 1666 addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2); 1667 } finally { 1668 args.recycle(); 1669 Log.endSession(); 1670 } 1671 break; 1672 } 1673 1674 case MSG_ON_POST_DIAL_CONTINUE: { 1675 SomeArgs args = (SomeArgs) msg.obj; 1676 try { 1677 Log.continueSession((Session) args.arg2, 1678 SESSION_HANDLER + SESSION_POST_DIAL_CONT); 1679 String callId = (String) args.arg1; 1680 boolean proceed = (args.argi1 == 1); 1681 onPostDialContinue(callId, proceed); 1682 } finally { 1683 args.recycle(); 1684 Log.endSession(); 1685 } 1686 break; 1687 } 1688 case MSG_PULL_EXTERNAL_CALL: { 1689 SomeArgs args = (SomeArgs) msg.obj; 1690 try { 1691 Log.continueSession((Session) args.arg2, 1692 SESSION_HANDLER + SESSION_PULL_EXTERNAL_CALL); 1693 pullExternalCall((String) args.arg1); 1694 } finally { 1695 args.recycle(); 1696 Log.endSession(); 1697 } 1698 break; 1699 } 1700 case MSG_SEND_CALL_EVENT: { 1701 SomeArgs args = (SomeArgs) msg.obj; 1702 try { 1703 Log.continueSession((Session) args.arg4, 1704 SESSION_HANDLER + SESSION_SEND_CALL_EVENT); 1705 String callId = (String) args.arg1; 1706 String event = (String) args.arg2; 1707 Bundle extras = (Bundle) args.arg3; 1708 sendCallEvent(callId, event, extras); 1709 } finally { 1710 args.recycle(); 1711 Log.endSession(); 1712 } 1713 break; 1714 } 1715 case MSG_ON_CALL_FILTERING_COMPLETED: { 1716 SomeArgs args = (SomeArgs) msg.obj; 1717 try { 1718 Log.continueSession((Session) args.arg3, 1719 SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED); 1720 String callId = (String) args.arg1; 1721 Connection.CallFilteringCompletionInfo completionInfo = 1722 (Connection.CallFilteringCompletionInfo) args.arg2; 1723 onCallFilteringCompleted(callId, completionInfo); 1724 } finally { 1725 args.recycle(); 1726 Log.endSession(); 1727 } 1728 break; 1729 } 1730 case MSG_HANDOVER_COMPLETE: { 1731 SomeArgs args = (SomeArgs) msg.obj; 1732 try { 1733 Log.continueSession((Session) args.arg2, 1734 SESSION_HANDLER + SESSION_HANDOVER_COMPLETE); 1735 String callId = (String) args.arg1; 1736 notifyHandoverComplete(callId); 1737 } finally { 1738 args.recycle(); 1739 Log.endSession(); 1740 } 1741 break; 1742 } 1743 case MSG_ON_EXTRAS_CHANGED: { 1744 SomeArgs args = (SomeArgs) msg.obj; 1745 try { 1746 Log.continueSession((Session) args.arg3, 1747 SESSION_HANDLER + SESSION_EXTRAS_CHANGED); 1748 String callId = (String) args.arg1; 1749 Bundle extras = (Bundle) args.arg2; 1750 handleExtrasChanged(callId, extras); 1751 } finally { 1752 args.recycle(); 1753 Log.endSession(); 1754 } 1755 break; 1756 } 1757 case MSG_ON_START_RTT: { 1758 SomeArgs args = (SomeArgs) msg.obj; 1759 try { 1760 Log.continueSession((Session) args.arg3, 1761 SESSION_HANDLER + SESSION_START_RTT); 1762 String callId = (String) args.arg1; 1763 Connection.RttTextStream rttTextStream = 1764 (Connection.RttTextStream) args.arg2; 1765 startRtt(callId, rttTextStream); 1766 } finally { 1767 args.recycle(); 1768 Log.endSession(); 1769 } 1770 break; 1771 } 1772 case MSG_ON_STOP_RTT: { 1773 SomeArgs args = (SomeArgs) msg.obj; 1774 try { 1775 Log.continueSession((Session) args.arg2, 1776 SESSION_HANDLER + SESSION_STOP_RTT); 1777 String callId = (String) args.arg1; 1778 stopRtt(callId); 1779 } finally { 1780 args.recycle(); 1781 Log.endSession(); 1782 } 1783 break; 1784 } 1785 case MSG_RTT_UPGRADE_RESPONSE: { 1786 SomeArgs args = (SomeArgs) msg.obj; 1787 try { 1788 Log.continueSession((Session) args.arg3, 1789 SESSION_HANDLER + SESSION_RTT_UPGRADE_RESPONSE); 1790 String callId = (String) args.arg1; 1791 Connection.RttTextStream rttTextStream = 1792 (Connection.RttTextStream) args.arg2; 1793 handleRttUpgradeResponse(callId, rttTextStream); 1794 } finally { 1795 args.recycle(); 1796 Log.endSession(); 1797 } 1798 break; 1799 } 1800 case MSG_CONNECTION_SERVICE_FOCUS_GAINED: 1801 onConnectionServiceFocusGained(); 1802 break; 1803 case MSG_CONNECTION_SERVICE_FOCUS_LOST: 1804 onConnectionServiceFocusLost(); 1805 break; 1806 case MSG_ON_CALL_ENDPOINT_CHANGED: { 1807 SomeArgs args = (SomeArgs) msg.obj; 1808 Log.continueSession((Session) args.arg3, 1809 SESSION_HANDLER + SESSION_CALL_AUDIO_SC); 1810 try { 1811 String callId = (String) args.arg1; 1812 CallEndpoint callEndpoint = (CallEndpoint) args.arg2; 1813 onCallEndpointChanged(callId, callEndpoint); 1814 } finally { 1815 args.recycle(); 1816 Log.endSession(); 1817 } 1818 break; 1819 } 1820 case MSG_ON_AVAILABLE_CALL_ENDPOINTS_CHANGED: { 1821 SomeArgs args = (SomeArgs) msg.obj; 1822 Log.continueSession((Session) args.arg3, 1823 SESSION_HANDLER + SESSION_CALL_AUDIO_SC); 1824 try { 1825 String callId = (String) args.arg1; 1826 List<CallEndpoint> availableCallEndpoints = (List<CallEndpoint>) args.arg2; 1827 onAvailableCallEndpointsChanged(callId, availableCallEndpoints); 1828 } finally { 1829 args.recycle(); 1830 Log.endSession(); 1831 } 1832 break; 1833 } 1834 case MSG_ON_MUTE_STATE_CHANGED: { 1835 SomeArgs args = (SomeArgs) msg.obj; 1836 Log.continueSession((Session) args.arg3, 1837 SESSION_HANDLER + SESSION_CALL_AUDIO_SC); 1838 try { 1839 String callId = (String) args.arg1; 1840 boolean isMuted = (boolean) args.arg2; 1841 onMuteStateChanged(callId, isMuted); 1842 } finally { 1843 args.recycle(); 1844 Log.endSession(); 1845 } 1846 break; 1847 } 1848 default: 1849 break; 1850 } 1851 } 1852 }; 1853 1854 private final Conference.Listener mConferenceListener = new Conference.Listener() { 1855 @Override 1856 public void onStateChanged(Conference conference, int oldState, int newState) { 1857 String id = mIdByConference.get(conference); 1858 switch (newState) { 1859 case Connection.STATE_RINGING: 1860 mAdapter.setRinging(id); 1861 break; 1862 case Connection.STATE_DIALING: 1863 mAdapter.setDialing(id); 1864 break; 1865 case Connection.STATE_ACTIVE: 1866 mAdapter.setActive(id); 1867 break; 1868 case Connection.STATE_HOLDING: 1869 mAdapter.setOnHold(id); 1870 break; 1871 case Connection.STATE_DISCONNECTED: 1872 // handled by onDisconnected 1873 break; 1874 } 1875 } 1876 1877 @Override 1878 public void onDisconnected(Conference conference, DisconnectCause disconnectCause) { 1879 String id = mIdByConference.get(conference); 1880 mAdapter.setDisconnected(id, disconnectCause); 1881 } 1882 1883 @Override 1884 public void onConnectionAdded(Conference conference, Connection connection) { 1885 } 1886 1887 @Override 1888 public void onConnectionRemoved(Conference conference, Connection connection) { 1889 } 1890 1891 @Override 1892 public void onConferenceableConnectionsChanged( 1893 Conference conference, List<Connection> conferenceableConnections) { 1894 mAdapter.setConferenceableConnections( 1895 mIdByConference.get(conference), 1896 createConnectionIdList(conferenceableConnections)); 1897 } 1898 1899 @Override 1900 public void onDestroyed(Conference conference) { 1901 removeConference(conference); 1902 } 1903 1904 @Override 1905 public void onConnectionCapabilitiesChanged( 1906 Conference conference, 1907 int connectionCapabilities) { 1908 String id = mIdByConference.get(conference); 1909 Log.d(this, "call capabilities: conference: %s", 1910 Connection.capabilitiesToString(connectionCapabilities)); 1911 mAdapter.setConnectionCapabilities(id, connectionCapabilities); 1912 } 1913 1914 @Override 1915 public void onConnectionPropertiesChanged( 1916 Conference conference, 1917 int connectionProperties) { 1918 String id = mIdByConference.get(conference); 1919 Log.d(this, "call capabilities: conference: %s", 1920 Connection.propertiesToString(connectionProperties)); 1921 mAdapter.setConnectionProperties(id, connectionProperties); 1922 } 1923 1924 @Override 1925 public void onVideoStateChanged(Conference c, int videoState) { 1926 String id = mIdByConference.get(c); 1927 Log.d(this, "onVideoStateChanged set video state %d", videoState); 1928 mAdapter.setVideoState(id, videoState); 1929 } 1930 1931 @Override 1932 public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) { 1933 String id = mIdByConference.get(c); 1934 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c, 1935 videoProvider); 1936 mAdapter.setVideoProvider(id, videoProvider); 1937 } 1938 1939 @Override 1940 public void onStatusHintsChanged(Conference conference, StatusHints statusHints) { 1941 String id = mIdByConference.get(conference); 1942 if (id != null) { 1943 mAdapter.setStatusHints(id, statusHints); 1944 } 1945 } 1946 1947 @Override 1948 public void onExtrasChanged(Conference c, Bundle extras) { 1949 String id = mIdByConference.get(c); 1950 if (id != null) { 1951 mAdapter.putExtras(id, extras); 1952 } 1953 } 1954 1955 @Override 1956 public void onExtrasRemoved(Conference c, List<String> keys) { 1957 String id = mIdByConference.get(c); 1958 if (id != null) { 1959 mAdapter.removeExtras(id, keys); 1960 } 1961 } 1962 1963 @Override 1964 public void onConferenceStateChanged(Conference c, boolean isConference) { 1965 String id = mIdByConference.get(c); 1966 if (id != null) { 1967 mAdapter.setConferenceState(id, isConference); 1968 } 1969 } 1970 1971 @Override 1972 public void onCallDirectionChanged(Conference c, int direction) { 1973 String id = mIdByConference.get(c); 1974 if (id != null) { 1975 mAdapter.setCallDirection(id, direction); 1976 } 1977 } 1978 1979 @Override 1980 public void onAddressChanged(Conference c, Uri newAddress, int presentation) { 1981 String id = mIdByConference.get(c); 1982 if (id != null) { 1983 mAdapter.setAddress(id, newAddress, presentation); 1984 } 1985 } 1986 1987 @Override 1988 public void onCallerDisplayNameChanged(Conference c, String callerDisplayName, 1989 int presentation) { 1990 String id = mIdByConference.get(c); 1991 if (id != null) { 1992 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); 1993 } 1994 } 1995 1996 @Override 1997 public void onConnectionEvent(Conference c, String event, Bundle extras) { 1998 String id = mIdByConference.get(c); 1999 if (id != null) { 2000 mAdapter.onConnectionEvent(id, event, extras); 2001 } 2002 } 2003 2004 @Override 2005 public void onRingbackRequested(Conference c, boolean ringback) { 2006 String id = mIdByConference.get(c); 2007 Log.d(this, "Adapter conference onRingback %b", ringback); 2008 mAdapter.setRingbackRequested(id, ringback); 2009 } 2010 }; 2011 2012 private final Connection.Listener mConnectionListener = new Connection.Listener() { 2013 @Override 2014 public void onStateChanged(Connection c, int state) { 2015 String id = mIdByConnection.get(c); 2016 Log.d(this, "Adapter set state %s %s", id, Connection.stateToString(state)); 2017 switch (state) { 2018 case Connection.STATE_ACTIVE: 2019 mAdapter.setActive(id); 2020 break; 2021 case Connection.STATE_DIALING: 2022 mAdapter.setDialing(id); 2023 break; 2024 case Connection.STATE_PULLING_CALL: 2025 mAdapter.setPulling(id); 2026 break; 2027 case Connection.STATE_DISCONNECTED: 2028 // Handled in onDisconnected() 2029 break; 2030 case Connection.STATE_HOLDING: 2031 mAdapter.setOnHold(id); 2032 break; 2033 case Connection.STATE_NEW: 2034 // Nothing to tell Telecom 2035 break; 2036 case Connection.STATE_RINGING: 2037 mAdapter.setRinging(id); 2038 break; 2039 } 2040 } 2041 2042 @Override 2043 public void onDisconnected(Connection c, DisconnectCause disconnectCause) { 2044 String id = mIdByConnection.get(c); 2045 Log.d(this, "Adapter set disconnected %s", disconnectCause); 2046 mAdapter.setDisconnected(id, disconnectCause); 2047 } 2048 2049 @Override 2050 public void onVideoStateChanged(Connection c, int videoState) { 2051 String id = mIdByConnection.get(c); 2052 Log.d(this, "Adapter set video state %d", videoState); 2053 mAdapter.setVideoState(id, videoState); 2054 } 2055 2056 @Override 2057 public void onAddressChanged(Connection c, Uri address, int presentation) { 2058 String id = mIdByConnection.get(c); 2059 mAdapter.setAddress(id, address, presentation); 2060 } 2061 2062 @Override 2063 public void onCallerDisplayNameChanged( 2064 Connection c, String callerDisplayName, int presentation) { 2065 String id = mIdByConnection.get(c); 2066 mAdapter.setCallerDisplayName(id, callerDisplayName, presentation); 2067 } 2068 2069 @Override 2070 public void onDestroyed(Connection c) { 2071 removeConnection(c); 2072 } 2073 2074 @Override 2075 public void onPostDialWait(Connection c, String remaining) { 2076 String id = mIdByConnection.get(c); 2077 Log.d(this, "Adapter onPostDialWait %s, %s", c, remaining); 2078 mAdapter.onPostDialWait(id, remaining); 2079 } 2080 2081 @Override 2082 public void onPostDialChar(Connection c, char nextChar) { 2083 String id = mIdByConnection.get(c); 2084 Log.d(this, "Adapter onPostDialChar %s, %s", c, nextChar); 2085 mAdapter.onPostDialChar(id, nextChar); 2086 } 2087 2088 @Override 2089 public void onRingbackRequested(Connection c, boolean ringback) { 2090 String id = mIdByConnection.get(c); 2091 Log.d(this, "Adapter onRingback %b", ringback); 2092 mAdapter.setRingbackRequested(id, ringback); 2093 } 2094 2095 @Override 2096 public void onConnectionCapabilitiesChanged(Connection c, int capabilities) { 2097 String id = mIdByConnection.get(c); 2098 Log.d(this, "capabilities: parcelableconnection: %s", 2099 Connection.capabilitiesToString(capabilities)); 2100 mAdapter.setConnectionCapabilities(id, capabilities); 2101 } 2102 2103 @Override 2104 public void onConnectionPropertiesChanged(Connection c, int properties) { 2105 String id = mIdByConnection.get(c); 2106 Log.d(this, "properties: parcelableconnection: %s", 2107 Connection.propertiesToString(properties)); 2108 mAdapter.setConnectionProperties(id, properties); 2109 } 2110 2111 @Override 2112 public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) { 2113 String id = mIdByConnection.get(c); 2114 Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c, 2115 videoProvider); 2116 mAdapter.setVideoProvider(id, videoProvider); 2117 } 2118 2119 @Override 2120 public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) { 2121 String id = mIdByConnection.get(c); 2122 mAdapter.setIsVoipAudioMode(id, isVoip); 2123 } 2124 2125 @Override 2126 public void onStatusHintsChanged(Connection c, StatusHints statusHints) { 2127 String id = mIdByConnection.get(c); 2128 mAdapter.setStatusHints(id, statusHints); 2129 } 2130 2131 @Override 2132 public void onConferenceablesChanged( 2133 Connection connection, List<Conferenceable> conferenceables) { 2134 mAdapter.setConferenceableConnections( 2135 mIdByConnection.get(connection), 2136 createIdList(conferenceables)); 2137 } 2138 2139 @Override 2140 public void onConferenceChanged(Connection connection, Conference conference) { 2141 String id = mIdByConnection.get(connection); 2142 if (id != null) { 2143 String conferenceId = null; 2144 if (conference != null) { 2145 conferenceId = mIdByConference.get(conference); 2146 } 2147 mAdapter.setIsConferenced(id, conferenceId); 2148 } 2149 } 2150 2151 @Override 2152 public void onConferenceMergeFailed(Connection connection) { 2153 String id = mIdByConnection.get(connection); 2154 if (id != null) { 2155 mAdapter.onConferenceMergeFailed(id); 2156 } 2157 } 2158 2159 @Override 2160 public void onExtrasChanged(Connection c, Bundle extras) { 2161 String id = mIdByConnection.get(c); 2162 if (id != null) { 2163 mAdapter.putExtras(id, extras); 2164 } 2165 } 2166 2167 @Override 2168 public void onExtrasRemoved(Connection c, List<String> keys) { 2169 String id = mIdByConnection.get(c); 2170 if (id != null) { 2171 mAdapter.removeExtras(id, keys); 2172 } 2173 } 2174 2175 @Override 2176 public void onConnectionEvent(Connection connection, String event, Bundle extras) { 2177 String id = mIdByConnection.get(connection); 2178 if (id != null) { 2179 mAdapter.onConnectionEvent(id, event, extras); 2180 } 2181 } 2182 2183 @Override 2184 public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) { 2185 String id = mIdByConnection.get(c); 2186 if (id != null) { 2187 mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress); 2188 } 2189 } 2190 2191 @Override 2192 public void onRttInitiationSuccess(Connection c) { 2193 String id = mIdByConnection.get(c); 2194 if (id != null) { 2195 mAdapter.onRttInitiationSuccess(id); 2196 } 2197 } 2198 2199 @Override 2200 public void onRttInitiationFailure(Connection c, int reason) { 2201 String id = mIdByConnection.get(c); 2202 if (id != null) { 2203 mAdapter.onRttInitiationFailure(id, reason); 2204 } 2205 } 2206 2207 @Override 2208 public void onRttSessionRemotelyTerminated(Connection c) { 2209 String id = mIdByConnection.get(c); 2210 if (id != null) { 2211 mAdapter.onRttSessionRemotelyTerminated(id); 2212 } 2213 } 2214 2215 @Override 2216 public void onRemoteRttRequest(Connection c) { 2217 String id = mIdByConnection.get(c); 2218 if (id != null) { 2219 mAdapter.onRemoteRttRequest(id); 2220 } 2221 } 2222 2223 @Override 2224 public void onPhoneAccountChanged(Connection c, PhoneAccountHandle pHandle) { 2225 String id = mIdByConnection.get(c); 2226 if (id != null) { 2227 mAdapter.onPhoneAccountChanged(id, pHandle); 2228 } 2229 } 2230 2231 public void onConnectionTimeReset(Connection c) { 2232 String id = mIdByConnection.get(c); 2233 if (id != null) { 2234 mAdapter.resetConnectionTime(id); 2235 } 2236 } 2237 2238 @Override 2239 public void onEndpointChanged(Connection c, CallEndpoint endpoint, Executor executor, 2240 OutcomeReceiver<Void, CallEndpointException> callback) { 2241 String id = mIdByConnection.get(c); 2242 if (id != null) { 2243 mAdapter.requestCallEndpointChange(id, endpoint, executor, callback); 2244 } 2245 } 2246 2247 @Override 2248 public void onQueryLocation(Connection c, long timeoutMillis, @NonNull String provider, 2249 @NonNull @CallbackExecutor Executor executor, 2250 @NonNull OutcomeReceiver<Location, QueryLocationException> callback) { 2251 String id = mIdByConnection.get(c); 2252 if (id != null) { 2253 mAdapter.queryLocation(id, timeoutMillis, provider, executor, callback); 2254 } 2255 } 2256 }; 2257 2258 /** {@inheritDoc} */ 2259 @Override onBind(Intent intent)2260 public final IBinder onBind(Intent intent) { 2261 onBindClient(intent); 2262 return mBinder; 2263 } 2264 2265 /** {@inheritDoc} */ 2266 @Override onUnbind(Intent intent)2267 public boolean onUnbind(Intent intent) { 2268 endAllConnections(); 2269 return super.onUnbind(intent); 2270 } 2271 2272 /** 2273 * Used for testing to let the test suite know when the connection service has been bound. 2274 * @hide 2275 */ 2276 @TestApi onBindClient(@ullable Intent intent)2277 public void onBindClient(@Nullable Intent intent) { 2278 } 2279 2280 /** 2281 * This can be used by telecom to either create a new outgoing conference call or attach 2282 * to an existing incoming conference call. In either case, telecom will cycle through a 2283 * set of services and call createConference until a connection service cancels the process 2284 * or completes it successfully. 2285 */ createConference( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown)2286 private void createConference( 2287 final PhoneAccountHandle callManagerAccount, 2288 final String callId, 2289 final ConnectionRequest request, 2290 boolean isIncoming, 2291 boolean isUnknown) { 2292 2293 Conference conference = null; 2294 conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request) 2295 : onCreateOutgoingConference(callManagerAccount, request); 2296 2297 Log.d(this, "createConference, conference: %s", conference); 2298 if (conference == null) { 2299 Log.i(this, "createConference, implementation returned null conference."); 2300 conference = Conference.createFailedConference( 2301 new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"), 2302 request.getAccountHandle()); 2303 } 2304 2305 Bundle extras = request.getExtras(); 2306 Bundle newExtras = new Bundle(); 2307 newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); 2308 if (extras != null) { 2309 // If the request originated from a remote connection service, we will add some 2310 // tracking information that Telecom can use to keep informed of which package 2311 // made the remote request, and which remote connection service was used. 2312 if (extras.containsKey(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) { 2313 newExtras.putString( 2314 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME, 2315 extras.getString( 2316 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)); 2317 newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, 2318 request.getAccountHandle()); 2319 } 2320 } 2321 conference.putExtras(newExtras); 2322 2323 mConferenceById.put(callId, conference); 2324 mIdByConference.put(conference, callId); 2325 2326 conference.addListener(mConferenceListener); 2327 ParcelableConference parcelableConference = new ParcelableConference.Builder( 2328 request.getAccountHandle(), conference.getState()) 2329 .setConnectionCapabilities(conference.getConnectionCapabilities()) 2330 .setConnectionProperties(conference.getConnectionProperties()) 2331 .setVideoAttributes(conference.getVideoProvider() == null 2332 ? null : conference.getVideoProvider().getInterface(), 2333 conference.getVideoState()) 2334 .setConnectTimeMillis(conference.getConnectTimeMillis(), 2335 conference.getConnectionStartElapsedRealtimeMillis()) 2336 .setStatusHints(conference.getStatusHints()) 2337 .setExtras(conference.getExtras()) 2338 .setAddress(conference.getAddress(), conference.getAddressPresentation()) 2339 .setCallerDisplayName(conference.getCallerDisplayName(), 2340 conference.getCallerDisplayNamePresentation()) 2341 .setDisconnectCause(conference.getDisconnectCause()) 2342 .setRingbackRequested(conference.isRingbackRequested()) 2343 .build(); 2344 if (conference.getState() != Connection.STATE_DISCONNECTED) { 2345 conference.setTelecomCallId(callId); 2346 mAdapter.setVideoProvider(callId, conference.getVideoProvider()); 2347 mAdapter.setVideoState(callId, conference.getVideoState()); 2348 onConferenceAdded(conference); 2349 } 2350 2351 Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId); 2352 mAdapter.handleCreateConferenceComplete( 2353 callId, 2354 request, 2355 parcelableConference); 2356 } 2357 2358 /** 2359 * This can be used by telecom to either create a new outgoing call or attach to an existing 2360 * incoming call. In either case, telecom will cycle through a set of services and call 2361 * createConnection util a connection service cancels the process or completes it successfully. 2362 */ createConnection( final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming, boolean isUnknown)2363 private void createConnection( 2364 final PhoneAccountHandle callManagerAccount, 2365 final String callId, 2366 final ConnectionRequest request, 2367 boolean isIncoming, 2368 boolean isUnknown) { 2369 boolean isLegacyHandover = request.getExtras() != null && 2370 request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER, false); 2371 boolean isHandover = request.getExtras() != null && request.getExtras().getBoolean( 2372 TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, false); 2373 boolean addSelfManaged = request.getExtras() != null && request.getExtras().getBoolean( 2374 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true); 2375 Log.i(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " 2376 + "isIncoming: %b, isUnknown: %b, isLegacyHandover: %b, isHandover: %b, " 2377 + " addSelfManaged: %b", callManagerAccount, callId, request, isIncoming, 2378 isUnknown, isLegacyHandover, isHandover, addSelfManaged); 2379 2380 Connection connection = null; 2381 if (isHandover) { 2382 PhoneAccountHandle fromPhoneAccountHandle = request.getExtras() != null 2383 ? (PhoneAccountHandle) request.getExtras().getParcelable( 2384 TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, android.telecom.PhoneAccountHandle.class) : null; 2385 if (!isIncoming) { 2386 connection = onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request); 2387 } else { 2388 connection = onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request); 2389 } 2390 } else { 2391 connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) 2392 : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) 2393 : onCreateOutgoingConnection(callManagerAccount, request); 2394 } 2395 Log.d(this, "createConnection, connection: %s", connection); 2396 if (connection == null) { 2397 Log.i(this, "createConnection, implementation returned null connection."); 2398 connection = Connection.createFailedConnection( 2399 new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION")); 2400 } else { 2401 try { 2402 Bundle extras = request.getExtras(); 2403 if (extras != null) { 2404 // If the request originated from a remote connection service, we will add some 2405 // tracking information that Telecom can use to keep informed of which package 2406 // made the remote request, and which remote connection service was used. 2407 if (extras.containsKey( 2408 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) { 2409 Bundle newExtras = new Bundle(); 2410 newExtras.putString( 2411 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME, 2412 extras.getString( 2413 Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME 2414 )); 2415 newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, 2416 request.getAccountHandle()); 2417 connection.putExtras(newExtras); 2418 } 2419 } 2420 } catch (UnsupportedOperationException ose) { 2421 // Do nothing; if the ConnectionService reported a failure it will be an instance 2422 // of an immutable Connection which we cannot edit, so we're out of luck. 2423 } 2424 } 2425 2426 boolean isSelfManaged = 2427 (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) 2428 == Connection.PROPERTY_SELF_MANAGED; 2429 // Self-managed Connections should always use voip audio mode; we default here so that the 2430 // local state within the ConnectionService matches the default we assume in Telecom. 2431 if (isSelfManaged) { 2432 connection.setAudioModeIsVoip(true); 2433 } 2434 connection.setTelecomCallId(callId); 2435 PhoneAccountHandle phoneAccountHandle = connection.getPhoneAccountHandle() == null 2436 ? request.getAccountHandle() : connection.getPhoneAccountHandle(); 2437 if (connection.getState() != Connection.STATE_DISCONNECTED) { 2438 addConnection(phoneAccountHandle, callId, connection); 2439 } 2440 2441 Uri address = connection.getAddress(); 2442 String number = address == null ? "null" : address.getSchemeSpecificPart(); 2443 Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s", 2444 Connection.toLogSafePhoneNumber(number), 2445 Connection.stateToString(connection.getState()), 2446 Connection.capabilitiesToString(connection.getConnectionCapabilities()), 2447 Connection.propertiesToString(connection.getConnectionProperties())); 2448 2449 Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId); 2450 mAdapter.handleCreateConnectionComplete( 2451 callId, 2452 request, 2453 new ParcelableConnection( 2454 phoneAccountHandle, 2455 connection.getState(), 2456 connection.getConnectionCapabilities(), 2457 connection.getConnectionProperties(), 2458 connection.getSupportedAudioRoutes(), 2459 connection.getAddress(), 2460 connection.getAddressPresentation(), 2461 connection.getCallerDisplayName(), 2462 connection.getCallerDisplayNamePresentation(), 2463 connection.getVideoProvider() == null ? 2464 null : connection.getVideoProvider().getInterface(), 2465 connection.getVideoState(), 2466 connection.isRingbackRequested(), 2467 connection.getAudioModeIsVoip(), 2468 connection.getConnectTimeMillis(), 2469 connection.getConnectionStartElapsedRealtimeMillis(), 2470 connection.getStatusHints(), 2471 connection.getDisconnectCause(), 2472 createIdList(connection.getConferenceables()), 2473 connection.getExtras(), 2474 connection.getCallerNumberVerificationStatus())); 2475 2476 if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) { 2477 // Tell ConnectionService to show its incoming call UX. 2478 connection.onShowIncomingCallUi(); 2479 } 2480 if (isUnknown) { 2481 triggerConferenceRecalculate(); 2482 } 2483 } 2484 createConnectionFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)2485 private void createConnectionFailed(final PhoneAccountHandle callManagerAccount, 2486 final String callId, final ConnectionRequest request, 2487 boolean isIncoming) { 2488 2489 Log.i(this, "createConnectionFailed %s", callId); 2490 if (isIncoming) { 2491 onCreateIncomingConnectionFailed(callManagerAccount, request); 2492 } else { 2493 onCreateOutgoingConnectionFailed(callManagerAccount, request); 2494 } 2495 } 2496 createConferenceFailed(final PhoneAccountHandle callManagerAccount, final String callId, final ConnectionRequest request, boolean isIncoming)2497 private void createConferenceFailed(final PhoneAccountHandle callManagerAccount, 2498 final String callId, final ConnectionRequest request, 2499 boolean isIncoming) { 2500 2501 Log.i(this, "createConferenceFailed %s", callId); 2502 if (isIncoming) { 2503 onCreateIncomingConferenceFailed(callManagerAccount, request); 2504 } else { 2505 onCreateOutgoingConferenceFailed(callManagerAccount, request); 2506 } 2507 } 2508 handoverFailed(final String callId, final ConnectionRequest request, int reason)2509 private void handoverFailed(final String callId, final ConnectionRequest request, 2510 int reason) { 2511 2512 Log.i(this, "handoverFailed %s", callId); 2513 onHandoverFailed(request, reason); 2514 } 2515 2516 /** 2517 * Called by Telecom when the creation of a new Connection has completed and it is now added 2518 * to Telecom. 2519 * @param callId The ID of the connection. 2520 */ notifyCreateConnectionComplete(final String callId)2521 private void notifyCreateConnectionComplete(final String callId) { 2522 Log.i(this, "notifyCreateConnectionComplete %s", callId); 2523 if (callId == null) { 2524 // This could happen if the connection fails quickly and is removed from the 2525 // ConnectionService before Telecom sends the create connection complete callback. 2526 Log.w(this, "notifyCreateConnectionComplete: callId is null."); 2527 return; 2528 } 2529 onCreateConnectionComplete(findConnectionForAction(callId, 2530 "notifyCreateConnectionComplete")); 2531 } 2532 2533 /** 2534 * Called by Telecom when the creation of a new Conference has completed and it is now added 2535 * to Telecom. 2536 * @param callId The ID of the connection. 2537 */ notifyCreateConferenceComplete(final String callId)2538 private void notifyCreateConferenceComplete(final String callId) { 2539 Log.i(this, "notifyCreateConferenceComplete %s", callId); 2540 if (callId == null) { 2541 // This could happen if the conference fails quickly and is removed from the 2542 // ConnectionService before Telecom sends the create conference complete callback. 2543 Log.w(this, "notifyCreateConferenceComplete: callId is null."); 2544 return; 2545 } 2546 onCreateConferenceComplete(findConferenceForAction(callId, 2547 "notifyCreateConferenceComplete")); 2548 } 2549 2550 abort(String callId)2551 private void abort(String callId) { 2552 Log.i(this, "abort %s", callId); 2553 findConnectionForAction(callId, "abort").onAbort(); 2554 } 2555 answerVideo(String callId, int videoState)2556 private void answerVideo(String callId, int videoState) { 2557 Log.i(this, "answerVideo %s", callId); 2558 if (mConnectionById.containsKey(callId)) { 2559 findConnectionForAction(callId, "answer").onAnswer(videoState); 2560 } else { 2561 findConferenceForAction(callId, "answer").onAnswer(videoState); 2562 } 2563 } 2564 answer(String callId)2565 private void answer(String callId) { 2566 Log.i(this, "answer %s", callId); 2567 if (mConnectionById.containsKey(callId)) { 2568 findConnectionForAction(callId, "answer").onAnswer(); 2569 } else { 2570 findConferenceForAction(callId, "answer").onAnswer(); 2571 } 2572 } 2573 deflect(String callId, Uri address)2574 private void deflect(String callId, Uri address) { 2575 Log.i(this, "deflect %s", callId); 2576 findConnectionForAction(callId, "deflect").onDeflect(address); 2577 } 2578 reject(String callId)2579 private void reject(String callId) { 2580 Log.i(this, "reject %s", callId); 2581 if (mConnectionById.containsKey(callId)) { 2582 findConnectionForAction(callId, "reject").onReject(); 2583 } else { 2584 findConferenceForAction(callId, "reject").onReject(); 2585 } 2586 } 2587 reject(String callId, String rejectWithMessage)2588 private void reject(String callId, String rejectWithMessage) { 2589 Log.i(this, "reject %s with message", callId); 2590 findConnectionForAction(callId, "reject").onReject(rejectWithMessage); 2591 } 2592 reject(String callId, @android.telecom.Call.RejectReason int rejectReason)2593 private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) { 2594 Log.i(this, "reject %s with reason %d", callId, rejectReason); 2595 findConnectionForAction(callId, "reject").onReject(rejectReason); 2596 } 2597 transfer(String callId, Uri number, boolean isConfirmationRequired)2598 private void transfer(String callId, Uri number, boolean isConfirmationRequired) { 2599 Log.i(this, "transfer %s", callId); 2600 findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired); 2601 } 2602 consultativeTransfer(String callId, String otherCallId)2603 private void consultativeTransfer(String callId, String otherCallId) { 2604 Log.i(this, "consultativeTransfer %s", callId); 2605 Connection connection1 = findConnectionForAction(callId, "consultativeTransfer"); 2606 Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer"); 2607 connection1.onTransfer(connection2); 2608 } 2609 silence(String callId)2610 private void silence(String callId) { 2611 Log.i(this, "silence %s", callId); 2612 findConnectionForAction(callId, "silence").onSilence(); 2613 } 2614 disconnect(String callId)2615 private void disconnect(String callId) { 2616 Log.i(this, "disconnect %s", callId); 2617 if (mConnectionById.containsKey(callId)) { 2618 findConnectionForAction(callId, "disconnect").onDisconnect(); 2619 } else { 2620 findConferenceForAction(callId, "disconnect").onDisconnect(); 2621 } 2622 } 2623 hold(String callId)2624 private void hold(String callId) { 2625 Log.i(this, "hold %s", callId); 2626 if (mConnectionById.containsKey(callId)) { 2627 findConnectionForAction(callId, "hold").onHold(); 2628 } else { 2629 findConferenceForAction(callId, "hold").onHold(); 2630 } 2631 } 2632 unhold(String callId)2633 private void unhold(String callId) { 2634 Log.i(this, "unhold %s", callId); 2635 if (mConnectionById.containsKey(callId)) { 2636 findConnectionForAction(callId, "unhold").onUnhold(); 2637 } else { 2638 findConferenceForAction(callId, "unhold").onUnhold(); 2639 } 2640 } 2641 onCallAudioStateChanged(String callId, CallAudioState callAudioState)2642 private void onCallAudioStateChanged(String callId, CallAudioState callAudioState) { 2643 Log.i(this, "onAudioStateChanged %s %s", callId, callAudioState); 2644 if (mConnectionById.containsKey(callId)) { 2645 findConnectionForAction(callId, "onCallAudioStateChanged").setCallAudioState( 2646 callAudioState); 2647 } else { 2648 findConferenceForAction(callId, "onCallAudioStateChanged").setCallAudioState( 2649 callAudioState); 2650 } 2651 } 2652 onCallEndpointChanged(String callId, CallEndpoint callEndpoint)2653 private void onCallEndpointChanged(String callId, CallEndpoint callEndpoint) { 2654 Log.i(this, "onCallEndpointChanged %s %s", callId, callEndpoint); 2655 if (mConnectionById.containsKey(callId)) { 2656 findConnectionForAction(callId, "onCallEndpointChanged").setCallEndpoint(callEndpoint); 2657 } else { 2658 findConferenceForAction(callId, "onCallEndpointChanged").setCallEndpoint(callEndpoint); 2659 } 2660 } 2661 onAvailableCallEndpointsChanged(String callId, List<CallEndpoint> availableCallEndpoints)2662 private void onAvailableCallEndpointsChanged(String callId, 2663 List<CallEndpoint> availableCallEndpoints) { 2664 Log.i(this, "onAvailableCallEndpointsChanged %s", callId); 2665 if (mConnectionById.containsKey(callId)) { 2666 findConnectionForAction(callId, "onAvailableCallEndpointsChanged") 2667 .setAvailableCallEndpoints(availableCallEndpoints); 2668 } else { 2669 findConferenceForAction(callId, "onAvailableCallEndpointsChanged") 2670 .setAvailableCallEndpoints(availableCallEndpoints); 2671 } 2672 } 2673 onMuteStateChanged(String callId, boolean isMuted)2674 private void onMuteStateChanged(String callId, boolean isMuted) { 2675 Log.i(this, "onMuteStateChanged %s %s", callId, isMuted); 2676 if (mConnectionById.containsKey(callId)) { 2677 findConnectionForAction(callId, "onMuteStateChanged").setMuteState(isMuted); 2678 } else { 2679 findConferenceForAction(callId, "onMuteStateChanged").setMuteState(isMuted); 2680 } 2681 } 2682 onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi)2683 private void onUsingAlternativeUi(String callId, boolean isUsingAlternativeUi) { 2684 Log.i(this, "onUsingAlternativeUi %s %s", callId, isUsingAlternativeUi); 2685 if (mConnectionById.containsKey(callId)) { 2686 findConnectionForAction(callId, "onUsingAlternativeUi") 2687 .onUsingAlternativeUi(isUsingAlternativeUi); 2688 } 2689 } 2690 onTrackedByNonUiService(String callId, boolean isTracked)2691 private void onTrackedByNonUiService(String callId, boolean isTracked) { 2692 Log.i(this, "onTrackedByNonUiService %s %s", callId, isTracked); 2693 if (mConnectionById.containsKey(callId)) { 2694 findConnectionForAction(callId, "onTrackedByNonUiService") 2695 .onTrackedByNonUiService(isTracked); 2696 } 2697 } 2698 playDtmfTone(String callId, char digit)2699 private void playDtmfTone(String callId, char digit) { 2700 Log.i(this, "playDtmfTone %s %s", callId, Log.pii(digit)); 2701 if (mConnectionById.containsKey(callId)) { 2702 findConnectionForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 2703 } else { 2704 findConferenceForAction(callId, "playDtmfTone").onPlayDtmfTone(digit); 2705 } 2706 } 2707 stopDtmfTone(String callId)2708 private void stopDtmfTone(String callId) { 2709 Log.i(this, "stopDtmfTone %s", callId); 2710 if (mConnectionById.containsKey(callId)) { 2711 findConnectionForAction(callId, "stopDtmfTone").onStopDtmfTone(); 2712 } else { 2713 findConferenceForAction(callId, "stopDtmfTone").onStopDtmfTone(); 2714 } 2715 } 2716 conference(String callId1, String callId2)2717 private void conference(String callId1, String callId2) { 2718 Log.i(this, "conference %s, %s", callId1, callId2); 2719 2720 // Attempt to get second connection or conference. 2721 Connection connection2 = findConnectionForAction(callId2, "conference"); 2722 Conference conference2 = getNullConference(); 2723 if (connection2 == getNullConnection()) { 2724 conference2 = findConferenceForAction(callId2, "conference"); 2725 if (conference2 == getNullConference()) { 2726 Log.w(this, "Connection2 or Conference2 missing in conference request %s.", 2727 callId2); 2728 return; 2729 } 2730 } 2731 2732 // Attempt to get first connection or conference and perform merge. 2733 Connection connection1 = findConnectionForAction(callId1, "conference"); 2734 if (connection1 == getNullConnection()) { 2735 Conference conference1 = findConferenceForAction(callId1, "addConnection"); 2736 if (conference1 == getNullConference()) { 2737 Log.w(this, 2738 "Connection1 or Conference1 missing in conference request %s.", 2739 callId1); 2740 } else { 2741 // Call 1 is a conference. 2742 if (connection2 != getNullConnection()) { 2743 // Call 2 is a connection so merge via call 1 (conference). 2744 conference1.onMerge(connection2); 2745 } else { 2746 // Call 2 is ALSO a conference; this should never happen. 2747 Log.wtf(this, "There can only be one conference and an attempt was made to " + 2748 "merge two conferences."); 2749 return; 2750 } 2751 } 2752 } else { 2753 // Call 1 is a connection. 2754 if (conference2 != getNullConference()) { 2755 // Call 2 is a conference, so merge via call 2. 2756 conference2.onMerge(connection1); 2757 } else { 2758 // Call 2 is a connection, so merge together. 2759 onConference(connection1, connection2); 2760 } 2761 } 2762 } 2763 splitFromConference(String callId)2764 private void splitFromConference(String callId) { 2765 Log.i(this, "splitFromConference(%s)", callId); 2766 2767 Connection connection = findConnectionForAction(callId, "splitFromConference"); 2768 if (connection == getNullConnection()) { 2769 Log.w(this, "Connection missing in conference request %s.", callId); 2770 return; 2771 } 2772 2773 Conference conference = connection.getConference(); 2774 if (conference != null) { 2775 conference.onSeparate(connection); 2776 } 2777 } 2778 mergeConference(String callId)2779 private void mergeConference(String callId) { 2780 Log.i(this, "mergeConference(%s)", callId); 2781 Conference conference = findConferenceForAction(callId, "mergeConference"); 2782 if (conference != null) { 2783 conference.onMerge(); 2784 } 2785 } 2786 swapConference(String callId)2787 private void swapConference(String callId) { 2788 Log.i(this, "swapConference(%s)", callId); 2789 Conference conference = findConferenceForAction(callId, "swapConference"); 2790 if (conference != null) { 2791 conference.onSwap(); 2792 } 2793 } 2794 addConferenceParticipants(String callId, List<Uri> participants)2795 private void addConferenceParticipants(String callId, List<Uri> participants) { 2796 Log.i(this, "addConferenceParticipants(%s)", callId); 2797 if (mConnectionById.containsKey(callId)) { 2798 findConnectionForAction(callId, "addConferenceParticipants") 2799 .onAddConferenceParticipants(participants); 2800 } else { 2801 findConferenceForAction(callId, "addConferenceParticipants") 2802 .onAddConferenceParticipants(participants); 2803 } 2804 } 2805 2806 /** 2807 * Notifies a {@link Connection} of a request to pull an external call. 2808 * 2809 * See {@link Call#pullExternalCall()}. 2810 * 2811 * @param callId The ID of the call to pull. 2812 */ pullExternalCall(String callId)2813 private void pullExternalCall(String callId) { 2814 Log.i(this, "pullExternalCall(%s)", callId); 2815 Connection connection = findConnectionForAction(callId, "pullExternalCall"); 2816 if (connection != null) { 2817 connection.onPullExternalCall(); 2818 } 2819 } 2820 2821 /** 2822 * Notifies a {@link Connection} of a call event. 2823 * 2824 * See {@link Call#sendCallEvent(String, Bundle)}. 2825 * 2826 * @param callId The ID of the call receiving the event. 2827 * @param event The event. 2828 * @param extras Extras associated with the event. 2829 */ sendCallEvent(String callId, String event, Bundle extras)2830 private void sendCallEvent(String callId, String event, Bundle extras) { 2831 Log.i(this, "sendCallEvent(%s, %s)", callId, event); 2832 Connection connection = findConnectionForAction(callId, "sendCallEvent"); 2833 if (connection != null) { 2834 connection.onCallEvent(event, extras); 2835 } 2836 } 2837 onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo callFilteringCompletionInfo)2838 private void onCallFilteringCompleted(String callId, Connection.CallFilteringCompletionInfo 2839 callFilteringCompletionInfo) { 2840 Log.i(this, "onCallFilteringCompleted(%s, %s)", callId, callFilteringCompletionInfo); 2841 Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted"); 2842 if (connection != null) { 2843 connection.onCallFilteringCompleted(callFilteringCompletionInfo); 2844 } 2845 } 2846 2847 /** 2848 * Notifies a {@link Connection} that a handover has completed. 2849 * 2850 * @param callId The ID of the call which completed handover. 2851 */ notifyHandoverComplete(String callId)2852 private void notifyHandoverComplete(String callId) { 2853 Log.i(this, "notifyHandoverComplete(%s)", callId); 2854 Connection connection = findConnectionForAction(callId, "notifyHandoverComplete"); 2855 if (connection != null) { 2856 connection.onHandoverComplete(); 2857 } 2858 } 2859 2860 /** 2861 * Notifies a {@link Connection} or {@link Conference} of a change to the extras from Telecom. 2862 * <p> 2863 * These extra changes can originate from Telecom itself, or from an {@link InCallService} via 2864 * the {@link android.telecom.Call#putExtra(String, boolean)}, 2865 * {@link android.telecom.Call#putExtra(String, int)}, 2866 * {@link android.telecom.Call#putExtra(String, String)}, 2867 * {@link Call#removeExtras(List)}. 2868 * 2869 * @param callId The ID of the call receiving the event. 2870 * @param extras The new extras bundle. 2871 */ handleExtrasChanged(String callId, Bundle extras)2872 private void handleExtrasChanged(String callId, Bundle extras) { 2873 Log.i(this, "handleExtrasChanged(%s, %s)", callId, extras); 2874 if (mConnectionById.containsKey(callId)) { 2875 findConnectionForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 2876 } else if (mConferenceById.containsKey(callId)) { 2877 findConferenceForAction(callId, "handleExtrasChanged").handleExtrasChanged(extras); 2878 } 2879 } 2880 startRtt(String callId, Connection.RttTextStream rttTextStream)2881 private void startRtt(String callId, Connection.RttTextStream rttTextStream) { 2882 Log.i(this, "startRtt(%s)", callId); 2883 if (mConnectionById.containsKey(callId)) { 2884 findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream); 2885 } else if (mConferenceById.containsKey(callId)) { 2886 Log.w(this, "startRtt called on a conference."); 2887 } 2888 } 2889 stopRtt(String callId)2890 private void stopRtt(String callId) { 2891 Log.i(this, "stopRtt(%s)", callId); 2892 if (mConnectionById.containsKey(callId)) { 2893 findConnectionForAction(callId, "stopRtt").onStopRtt(); 2894 } else if (mConferenceById.containsKey(callId)) { 2895 Log.w(this, "stopRtt called on a conference."); 2896 } 2897 } 2898 handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream)2899 private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) { 2900 Log.i(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null); 2901 if (mConnectionById.containsKey(callId)) { 2902 findConnectionForAction(callId, "handleRttUpgradeResponse") 2903 .handleRttUpgradeResponse(rttTextStream); 2904 } else if (mConferenceById.containsKey(callId)) { 2905 Log.w(this, "handleRttUpgradeResponse called on a conference."); 2906 } 2907 } 2908 onPostDialContinue(String callId, boolean proceed)2909 private void onPostDialContinue(String callId, boolean proceed) { 2910 Log.i(this, "onPostDialContinue(%s)", callId); 2911 findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed); 2912 } 2913 onAdapterAttached()2914 private void onAdapterAttached() { 2915 if (mAreAccountsInitialized) { 2916 // No need to query again if we already did it. 2917 return; 2918 } 2919 2920 String callingPackage = getOpPackageName(); 2921 2922 mAdapter.queryRemoteConnectionServices(new RemoteServiceCallback.Stub() { 2923 @Override 2924 public void onResult( 2925 final List<ComponentName> componentNames, 2926 final List<IBinder> services) { 2927 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) { 2928 @Override 2929 public void loggedRun() { 2930 for (int i = 0; i < componentNames.size() && i < services.size(); i++) { 2931 mRemoteConnectionManager.addConnectionService( 2932 componentNames.get(i), 2933 IConnectionService.Stub.asInterface(services.get(i))); 2934 } 2935 onAccountsInitialized(); 2936 Log.d(this, "remote connection services found: " + services); 2937 } 2938 }.prepare()); 2939 } 2940 2941 @Override 2942 public void onError() { 2943 mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) { 2944 @Override 2945 public void loggedRun() { 2946 mAreAccountsInitialized = true; 2947 } 2948 }.prepare()); 2949 } 2950 }, callingPackage); 2951 } 2952 2953 /** 2954 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 2955 * incoming request. This is used by {@code ConnectionService}s that are registered with 2956 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to manage 2957 * SIM-based incoming calls. 2958 * 2959 * @param connectionManagerPhoneAccount See description at 2960 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2961 * @param request Details about the incoming call. 2962 * @return The {@code Connection} object to satisfy this call, or {@code null} to 2963 * not handle the call. 2964 */ createRemoteIncomingConnection( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)2965 public final @Nullable RemoteConnection createRemoteIncomingConnection( 2966 @NonNull PhoneAccountHandle connectionManagerPhoneAccount, 2967 @NonNull ConnectionRequest request) { 2968 return mRemoteConnectionManager.createRemoteConnection( 2969 connectionManagerPhoneAccount, request, true); 2970 } 2971 2972 /** 2973 * Ask some other {@code ConnectionService} to create a {@code RemoteConnection} given an 2974 * outgoing request. This is used by {@code ConnectionService}s that are registered with 2975 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} and want to be able to use the 2976 * SIM-based {@code ConnectionService} to place its outgoing calls. 2977 * 2978 * @param connectionManagerPhoneAccount See description at 2979 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2980 * @param request Details about the outgoing call. 2981 * @return The {@code Connection} object to satisfy this call, or {@code null} to 2982 * not handle the call. 2983 */ createRemoteOutgoingConnection( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)2984 public final @Nullable RemoteConnection createRemoteOutgoingConnection( 2985 @NonNull PhoneAccountHandle connectionManagerPhoneAccount, 2986 @NonNull ConnectionRequest request) { 2987 return mRemoteConnectionManager.createRemoteConnection( 2988 connectionManagerPhoneAccount, request, false); 2989 } 2990 2991 /** 2992 * Ask some other {@code ConnectionService} to create a {@code RemoteConference} given an 2993 * incoming request. This is used by {@code ConnectionService}s that are registered with 2994 * {@link PhoneAccount#CAPABILITY_ADHOC_CONFERENCE_CALLING}. 2995 * 2996 * @param connectionManagerPhoneAccount See description at 2997 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 2998 * @param request Details about the incoming conference call. 2999 * @return The {@code RemoteConference} object to satisfy this call, or {@code null} to not 3000 * handle the call. 3001 */ createRemoteIncomingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)3002 public final @Nullable RemoteConference createRemoteIncomingConference( 3003 @Nullable PhoneAccountHandle connectionManagerPhoneAccount, 3004 @Nullable ConnectionRequest request) { 3005 return mRemoteConnectionManager.createRemoteConference(connectionManagerPhoneAccount, 3006 request, true); 3007 } 3008 3009 /** 3010 * Ask some other {@code ConnectionService} to create a {@code RemoteConference} given an 3011 * outgoing request. This is used by {@code ConnectionService}s that are registered with 3012 * {@link PhoneAccount#CAPABILITY_ADHOC_CONFERENCE_CALLING}. 3013 * 3014 * @param connectionManagerPhoneAccount See description at 3015 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3016 * @param request Details about the outgoing conference call. 3017 * @return The {@code RemoteConference} object to satisfy this call, or {@code null} to not 3018 * handle the call. 3019 */ createRemoteOutgoingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)3020 public final @Nullable RemoteConference createRemoteOutgoingConference( 3021 @Nullable PhoneAccountHandle connectionManagerPhoneAccount, 3022 @Nullable ConnectionRequest request) { 3023 return mRemoteConnectionManager.createRemoteConference(connectionManagerPhoneAccount, 3024 request, false); 3025 } 3026 3027 /** 3028 * Indicates to the relevant {@code RemoteConnectionService} that the specified 3029 * {@link RemoteConnection}s should be merged into a conference call. 3030 * <p> 3031 * If the conference request is successful, the method {@link #onRemoteConferenceAdded} will 3032 * be invoked. 3033 * 3034 * @param remoteConnection1 The first of the remote connections to conference. 3035 * @param remoteConnection2 The second of the remote connections to conference. 3036 */ conferenceRemoteConnections( RemoteConnection remoteConnection1, RemoteConnection remoteConnection2)3037 public final void conferenceRemoteConnections( 3038 RemoteConnection remoteConnection1, 3039 RemoteConnection remoteConnection2) { 3040 mRemoteConnectionManager.conferenceRemoteConnections(remoteConnection1, remoteConnection2); 3041 } 3042 3043 /** 3044 * Adds a new conference call. When a conference call is created either as a result of an 3045 * explicit request via {@link #onConference} or otherwise, the connection service should supply 3046 * an instance of {@link Conference} by invoking this method. A conference call provided by this 3047 * method will persist until {@link Conference#destroy} is invoked on the conference instance. 3048 * 3049 * @param conference The new conference object. 3050 */ addConference(Conference conference)3051 public final void addConference(Conference conference) { 3052 Log.d(this, "addConference: conference=%s", conference); 3053 3054 String id = addConferenceInternal(conference); 3055 if (id != null) { 3056 List<String> connectionIds = new ArrayList<>(2); 3057 for (Connection connection : conference.getConnections()) { 3058 if (mIdByConnection.containsKey(connection)) { 3059 connectionIds.add(mIdByConnection.get(connection)); 3060 } 3061 } 3062 conference.setTelecomCallId(id); 3063 ParcelableConference parcelableConference = new ParcelableConference.Builder( 3064 conference.getPhoneAccountHandle(), conference.getState()) 3065 .setConnectionCapabilities(conference.getConnectionCapabilities()) 3066 .setConnectionProperties(conference.getConnectionProperties()) 3067 .setConnectionIds(connectionIds) 3068 .setVideoAttributes(conference.getVideoProvider() == null 3069 ? null : conference.getVideoProvider().getInterface(), 3070 conference.getVideoState()) 3071 .setConnectTimeMillis(conference.getConnectTimeMillis(), 3072 conference.getConnectionStartElapsedRealtimeMillis()) 3073 .setStatusHints(conference.getStatusHints()) 3074 .setExtras(conference.getExtras()) 3075 .setAddress(conference.getAddress(), conference.getAddressPresentation()) 3076 .setCallerDisplayName(conference.getCallerDisplayName(), 3077 conference.getCallerDisplayNamePresentation()) 3078 .setDisconnectCause(conference.getDisconnectCause()) 3079 .setRingbackRequested(conference.isRingbackRequested()) 3080 .setCallDirection(conference.getCallDirection()) 3081 .build(); 3082 3083 mAdapter.addConferenceCall(id, parcelableConference); 3084 mAdapter.setVideoProvider(id, conference.getVideoProvider()); 3085 mAdapter.setVideoState(id, conference.getVideoState()); 3086 // In some instances a conference can start its life as a standalone call with just a 3087 // single participant; ensure we signal to Telecom in this case. 3088 if (!conference.isMultiparty()) { 3089 mAdapter.setConferenceState(id, conference.isMultiparty()); 3090 } 3091 3092 // Go through any child calls and set the parent. 3093 for (Connection connection : conference.getConnections()) { 3094 String connectionId = mIdByConnection.get(connection); 3095 if (connectionId != null) { 3096 mAdapter.setIsConferenced(connectionId, id); 3097 } 3098 } 3099 onConferenceAdded(conference); 3100 } 3101 } 3102 3103 /** 3104 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 3105 * connection. 3106 * 3107 * @param phoneAccountHandle The phone account handle for the connection. 3108 * @param connection The connection to add. 3109 */ addExistingConnection(PhoneAccountHandle phoneAccountHandle, Connection connection)3110 public final void addExistingConnection(PhoneAccountHandle phoneAccountHandle, 3111 Connection connection) { 3112 addExistingConnection(phoneAccountHandle, connection, null /* conference */); 3113 } 3114 3115 /** 3116 * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g 3117 * microphone, camera). 3118 * 3119 * <p> 3120 * The {@link ConnectionService} will be disconnected when it failed to call this method within 3121 * 5 seconds after {@link #onConnectionServiceFocusLost()} is called. 3122 * 3123 * @see ConnectionService#onConnectionServiceFocusLost() 3124 */ connectionServiceFocusReleased()3125 public final void connectionServiceFocusReleased() { 3126 mAdapter.onConnectionServiceFocusReleased(); 3127 } 3128 3129 /** 3130 * Adds a connection created by the {@link ConnectionService} and informs telecom of the new 3131 * connection, as well as adding that connection to the specified conference. 3132 * <p> 3133 * Note: This API is intended ONLY for use by the Telephony stack to provide an easy way to add 3134 * IMS conference participants to be added to a conference in a single step; this helps ensure 3135 * UI updates happen atomically, rather than adding the connection and then adding it to 3136 * the conference in another step. 3137 * 3138 * @param phoneAccountHandle The phone account handle for the connection. 3139 * @param connection The connection to add. 3140 * @param conference The parent conference of the new connection. 3141 * @hide 3142 */ 3143 @SystemApi addExistingConnection(@onNull PhoneAccountHandle phoneAccountHandle, @NonNull Connection connection, @NonNull Conference conference)3144 public final void addExistingConnection(@NonNull PhoneAccountHandle phoneAccountHandle, 3145 @NonNull Connection connection, @NonNull Conference conference) { 3146 3147 String id = addExistingConnectionInternal(phoneAccountHandle, connection); 3148 if (id != null) { 3149 List<String> emptyList = new ArrayList<>(0); 3150 String conferenceId = null; 3151 if (conference != null) { 3152 conferenceId = mIdByConference.get(conference); 3153 } 3154 3155 ParcelableConnection parcelableConnection = new ParcelableConnection( 3156 phoneAccountHandle, 3157 connection.getState(), 3158 connection.getConnectionCapabilities(), 3159 connection.getConnectionProperties(), 3160 connection.getSupportedAudioRoutes(), 3161 connection.getAddress(), 3162 connection.getAddressPresentation(), 3163 connection.getCallerDisplayName(), 3164 connection.getCallerDisplayNamePresentation(), 3165 connection.getVideoProvider() == null ? 3166 null : connection.getVideoProvider().getInterface(), 3167 connection.getVideoState(), 3168 connection.isRingbackRequested(), 3169 connection.getAudioModeIsVoip(), 3170 connection.getConnectTimeMillis(), 3171 connection.getConnectionStartElapsedRealtimeMillis(), 3172 connection.getStatusHints(), 3173 connection.getDisconnectCause(), 3174 emptyList, 3175 connection.getExtras(), 3176 conferenceId, 3177 connection.getCallDirection(), 3178 Connection.VERIFICATION_STATUS_NOT_VERIFIED); 3179 mAdapter.addExistingConnection(id, parcelableConnection); 3180 } 3181 } 3182 3183 /** 3184 * Returns all the active {@code Connection}s for which this {@code ConnectionService} 3185 * has taken responsibility. 3186 * 3187 * @return A collection of {@code Connection}s created by this {@code ConnectionService}. 3188 */ getAllConnections()3189 public final Collection<Connection> getAllConnections() { 3190 return mConnectionById.values(); 3191 } 3192 3193 /** 3194 * Returns all the active {@code Conference}s for which this {@code ConnectionService} 3195 * has taken responsibility. 3196 * 3197 * @return A collection of {@code Conference}s created by this {@code ConnectionService}. 3198 */ getAllConferences()3199 public final Collection<Conference> getAllConferences() { 3200 return mConferenceById.values(); 3201 } 3202 3203 /** 3204 * Create a {@code Connection} given an incoming request. This is used to attach to existing 3205 * incoming calls. 3206 * 3207 * @param connectionManagerPhoneAccount See description at 3208 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3209 * @param request Details about the incoming call. 3210 * @return The {@code Connection} object to satisfy this call, or {@code null} to 3211 * not handle the call. 3212 */ onCreateIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3213 public Connection onCreateIncomingConnection( 3214 PhoneAccountHandle connectionManagerPhoneAccount, 3215 ConnectionRequest request) { 3216 return null; 3217 } 3218 /** 3219 * Create a {@code Conference} given an incoming request. This is used to attach to an incoming 3220 * conference call initiated via 3221 * {@link TelecomManager#addNewIncomingConference(PhoneAccountHandle, Bundle)}. 3222 * 3223 * @param connectionManagerPhoneAccount See description at 3224 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3225 * @param request Details about the incoming conference call. 3226 * @return The {@code Conference} object to satisfy this call. If the conference attempt is 3227 * failed, the return value will be a result of an invocation of 3228 * {@link Connection#createFailedConnection(DisconnectCause)}. 3229 * Return {@code null} if the {@link ConnectionService} cannot handle the call. 3230 */ onCreateIncomingConference( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3231 public @Nullable Conference onCreateIncomingConference( 3232 @NonNull PhoneAccountHandle connectionManagerPhoneAccount, 3233 @NonNull ConnectionRequest request) { 3234 return null; 3235 } 3236 3237 /** 3238 * Called after the {@link Connection} returned by 3239 * {@link #onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} 3240 * or {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)} has been 3241 * added to the {@link ConnectionService} and sent to Telecom. 3242 * 3243 * @param connection the {@link Connection}. 3244 * @hide 3245 */ onCreateConnectionComplete(Connection connection)3246 public void onCreateConnectionComplete(Connection connection) { 3247 } 3248 3249 /** 3250 * Called after the {@link Conference} returned by 3251 * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)} 3252 * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been 3253 * added to the {@link ConnectionService} and sent to Telecom. 3254 * 3255 * @param conference the {@link Conference}. 3256 * @hide 3257 */ onCreateConferenceComplete(Conference conference)3258 public void onCreateConferenceComplete(Conference conference) { 3259 } 3260 3261 3262 /** 3263 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 3264 * incoming {@link Connection} was denied. 3265 * <p> 3266 * Used when a self-managed {@link ConnectionService} attempts to create a new incoming 3267 * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time. 3268 * The {@link ConnectionService} is responsible for silently rejecting the new incoming 3269 * {@link Connection}. 3270 * <p> 3271 * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. 3272 * 3273 * @param connectionManagerPhoneAccount See description at 3274 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3275 * @param request The incoming connection request. 3276 */ onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3277 public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 3278 ConnectionRequest request) { 3279 } 3280 3281 /** 3282 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 3283 * outgoing {@link Connection} was denied. 3284 * <p> 3285 * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing 3286 * {@link Connection}, but Telecom has determined that the call cannot be placed at this time. 3287 * The {@link ConnectionService} is responisible for informing the user that the 3288 * {@link Connection} cannot be made at this time. 3289 * <p> 3290 * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. 3291 * 3292 * @param connectionManagerPhoneAccount See description at 3293 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3294 * @param request The outgoing connection request. 3295 */ onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3296 public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, 3297 ConnectionRequest request) { 3298 } 3299 3300 /** 3301 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 3302 * incoming {@link Conference} was denied. 3303 * <p> 3304 * Used when a self-managed {@link ConnectionService} attempts to create a new incoming 3305 * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time. 3306 * The {@link ConnectionService} is responsible for silently rejecting the new incoming 3307 * {@link Conference}. 3308 * <p> 3309 * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information. 3310 * 3311 * @param connectionManagerPhoneAccount See description at 3312 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3313 * @param request The incoming connection request. 3314 */ onCreateIncomingConferenceFailed( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @Nullable ConnectionRequest request)3315 public void onCreateIncomingConferenceFailed( 3316 @Nullable PhoneAccountHandle connectionManagerPhoneAccount, 3317 @Nullable ConnectionRequest request) { 3318 } 3319 3320 /** 3321 * Called by Telecom to inform the {@link ConnectionService} that its request to create a new 3322 * outgoing {@link Conference} was denied. 3323 * <p> 3324 * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing 3325 * {@link Conference}, but Telecom has determined that the call cannot be placed at this time. 3326 * The {@link ConnectionService} is responisible for informing the user that the 3327 * {@link Conference} cannot be made at this time. 3328 * <p> 3329 * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information. 3330 * 3331 * @param connectionManagerPhoneAccount See description at 3332 * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. 3333 * @param request The outgoing connection request. 3334 */ onCreateOutgoingConferenceFailed( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3335 public void onCreateOutgoingConferenceFailed( 3336 @NonNull PhoneAccountHandle connectionManagerPhoneAccount, 3337 @NonNull ConnectionRequest request) { 3338 } 3339 3340 3341 /** 3342 * Trigger recalculate functinality for conference calls. This is used when a Telephony 3343 * Connection is part of a conference controller but is not yet added to Connection 3344 * Service and hence cannot be added to the conference call. 3345 * 3346 * @hide 3347 */ triggerConferenceRecalculate()3348 public void triggerConferenceRecalculate() { 3349 } 3350 3351 /** 3352 * Create a {@code Connection} given an outgoing request. This is used to initiate new 3353 * outgoing calls. 3354 * 3355 * @param connectionManagerPhoneAccount The connection manager account to use for managing 3356 * this call. 3357 * <p> 3358 * If this parameter is not {@code null}, it means that this {@code ConnectionService} 3359 * has registered one or more {@code PhoneAccount}s having 3360 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain 3361 * one of these {@code PhoneAccount}s, while the {@code request} will contain another 3362 * (usually but not always distinct) {@code PhoneAccount} to be used for actually 3363 * making the connection. 3364 * <p> 3365 * If this parameter is {@code null}, it means that this {@code ConnectionService} is 3366 * being asked to make a direct connection. The 3367 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be 3368 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for 3369 * making the connection. 3370 * @param request Details about the outgoing call. 3371 * @return The {@code Connection} object to satisfy this call, or the result of an invocation 3372 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. 3373 */ onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)3374 public Connection onCreateOutgoingConnection( 3375 PhoneAccountHandle connectionManagerPhoneAccount, 3376 ConnectionRequest request) { 3377 return null; 3378 } 3379 3380 /** 3381 * Create a {@code Conference} given an outgoing request. This is used to initiate new 3382 * outgoing conference call requested via 3383 * {@link TelecomManager#startConference(List, Bundle)}. 3384 * 3385 * @param connectionManagerPhoneAccount The connection manager account to use for managing 3386 * this call. 3387 * <p> 3388 * If this parameter is not {@code null}, it means that this {@code ConnectionService} 3389 * has registered one or more {@code PhoneAccount}s having 3390 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain 3391 * one of these {@code PhoneAccount}s, while the {@code request} will contain another 3392 * (usually but not always distinct) {@code PhoneAccount} to be used for actually 3393 * making the connection. 3394 * <p> 3395 * If this parameter is {@code null}, it means that this {@code ConnectionService} is 3396 * being asked to make a direct connection. The 3397 * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be 3398 * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for 3399 * making the connection. 3400 * @param request Details about the outgoing call. 3401 * @return The {@code Conference} object to satisfy this call. If the conference attempt is 3402 * failed, the return value will be a result of an invocation of 3403 * {@link Connection#createFailedConnection(DisconnectCause)}. 3404 * Return {@code null} if the {@link ConnectionService} cannot handle the call. 3405 */ onCreateOutgoingConference( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3406 public @Nullable Conference onCreateOutgoingConference( 3407 @NonNull PhoneAccountHandle connectionManagerPhoneAccount, 3408 @NonNull ConnectionRequest request) { 3409 return null; 3410 } 3411 3412 3413 /** 3414 * Called by Telecom to request that a {@link ConnectionService} creates an instance of an 3415 * outgoing handover {@link Connection}. 3416 * <p> 3417 * A call handover is the process where an ongoing call is transferred from one app (i.e. 3418 * {@link ConnectionService} to another app. The user could, for example, choose to continue a 3419 * mobile network call in a video calling app. The mobile network call via the Telephony stack 3420 * is referred to as the source of the handover, and the video calling app is referred to as the 3421 * destination. 3422 * <p> 3423 * When considering a handover scenario the <em>initiating</em> device is where a user initiated 3424 * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo( 3425 * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em> 3426 * device. 3427 * <p> 3428 * This method is called on the destination {@link ConnectionService} on <em>initiating</em> 3429 * device when the user initiates a handover request from one app to another. The user request 3430 * originates in the {@link InCallService} via 3431 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 3432 * <p> 3433 * For a full discussion of the handover process and the APIs involved, see 3434 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 3435 * <p> 3436 * Implementations of this method should return an instance of {@link Connection} which 3437 * represents the handover. If your app does not wish to accept a handover to it at this time, 3438 * you can return {@code null}. The code below shows an example of how this is done. 3439 * <pre> 3440 * {@code 3441 * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle 3442 * fromPhoneAccountHandle, ConnectionRequest request) { 3443 * if (!isHandoverAvailable()) { 3444 * return null; 3445 * } 3446 * MyConnection connection = new MyConnection(); 3447 * connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); 3448 * connection.setVideoState(request.getVideoState()); 3449 * return connection; 3450 * } 3451 * } 3452 * </pre> 3453 * 3454 * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the 3455 * ConnectionService which needs to handover the call. 3456 * @param request Details about the call to handover. 3457 * @return {@link Connection} instance corresponding to the handover call. 3458 */ onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)3459 public Connection onCreateOutgoingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, 3460 ConnectionRequest request) { 3461 return null; 3462 } 3463 3464 /** 3465 * Called by Telecom to request that a {@link ConnectionService} creates an instance of an 3466 * incoming handover {@link Connection}. 3467 * <p> 3468 * A call handover is the process where an ongoing call is transferred from one app (i.e. 3469 * {@link ConnectionService} to another app. The user could, for example, choose to continue a 3470 * mobile network call in a video calling app. The mobile network call via the Telephony stack 3471 * is referred to as the source of the handover, and the video calling app is referred to as the 3472 * destination. 3473 * <p> 3474 * When considering a handover scenario the <em>initiating</em> device is where a user initiated 3475 * the handover process (e.g. by calling {@link android.telecom.Call#handoverTo( 3476 * PhoneAccountHandle, int, Bundle)}, and the other device is considered the <em>receiving</em> 3477 * device. 3478 * <p> 3479 * This method is called on the destination app on the <em>receiving</em> device when the 3480 * destination app calls {@link TelecomManager#acceptHandover(Uri, int, PhoneAccountHandle)} to 3481 * accept an incoming handover from the <em>initiating</em> device. 3482 * <p> 3483 * For a full discussion of the handover process and the APIs involved, see 3484 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)}. 3485 * <p> 3486 * Implementations of this method should return an instance of {@link Connection} which 3487 * represents the handover. The code below shows an example of how this is done. 3488 * <pre> 3489 * {@code 3490 * public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle 3491 * fromPhoneAccountHandle, ConnectionRequest request) { 3492 * // Given that your app requested to accept the handover, you should not return null here. 3493 * MyConnection connection = new MyConnection(); 3494 * connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); 3495 * connection.setVideoState(request.getVideoState()); 3496 * return connection; 3497 * } 3498 * } 3499 * </pre> 3500 * 3501 * @param fromPhoneAccountHandle {@link PhoneAccountHandle} associated with the 3502 * ConnectionService which needs to handover the call. 3503 * @param request Details about the call which needs to be handover. 3504 * @return {@link Connection} instance corresponding to the handover call. 3505 */ onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, ConnectionRequest request)3506 public Connection onCreateIncomingHandoverConnection(PhoneAccountHandle fromPhoneAccountHandle, 3507 ConnectionRequest request) { 3508 return null; 3509 } 3510 3511 /** 3512 * Called by Telecom in response to a {@code TelecomManager#acceptHandover()} 3513 * invocation which failed. 3514 * <p> 3515 * For a full discussion of the handover process and the APIs involved, see 3516 * {@link android.telecom.Call#handoverTo(PhoneAccountHandle, int, Bundle)} 3517 * 3518 * @param request Details about the call which failed to handover. 3519 * @param error Reason for handover failure. Will be one of the 3520 */ onHandoverFailed(ConnectionRequest request, @Call.Callback.HandoverFailureErrors int error)3521 public void onHandoverFailed(ConnectionRequest request, 3522 @Call.Callback.HandoverFailureErrors int error) { 3523 return; 3524 } 3525 3526 /** 3527 * Calls of this type are created using 3528 * {@link TelecomManager#addNewUnknownCall(PhoneAccountHandle, Bundle)}. Unknown calls 3529 * are used for representing calls which become known to the {@link ConnectionService} 3530 * midway through the call. 3531 * 3532 * For example, a call transferred from one device to answer would surface as an active 3533 * call in Telecom instead of going through a typical Ringing to Active transition, or 3534 * Dialing to Active transition. 3535 * 3536 * A {@link ConnectionService} can return {@code null} (the default behavior) 3537 * if it is not able to handle a request for the requested unknown connection. 3538 * 3539 * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, android.os.Bundle)}. 3540 * 3541 * @param connectionManagerPhoneAccount The connection manager account to use for managing 3542 * this call 3543 * @param request Details about the outgoing call 3544 * @return The {@code Connection} object to satisfy this call, or the result of an invocation 3545 * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call 3546 * @hide 3547 */ 3548 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 3549 @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) onCreateUnknownConnection( @onNull PhoneAccountHandle connectionManagerPhoneAccount, @NonNull ConnectionRequest request)3550 public @Nullable Connection onCreateUnknownConnection( 3551 @NonNull PhoneAccountHandle connectionManagerPhoneAccount, 3552 @NonNull ConnectionRequest request) { 3553 return null; 3554 } 3555 3556 /** 3557 * Conference two specified connections. Invoked when the user has made a request to merge the 3558 * specified connections into a conference call. In response, the connection service should 3559 * create an instance of {@link Conference} and pass it into {@link #addConference}. 3560 * 3561 * @param connection1 A connection to merge into a conference call. 3562 * @param connection2 A connection to merge into a conference call. 3563 */ onConference(Connection connection1, Connection connection2)3564 public void onConference(Connection connection1, Connection connection2) {} 3565 3566 /** 3567 * Called when a connection is added. 3568 * @hide 3569 */ onConnectionAdded(Connection connection)3570 public void onConnectionAdded(Connection connection) {} 3571 3572 /** 3573 * Called when a connection is removed. 3574 * @hide 3575 */ onConnectionRemoved(Connection connection)3576 public void onConnectionRemoved(Connection connection) {} 3577 3578 /** 3579 * Called when a conference is added. 3580 * @hide 3581 */ onConferenceAdded(Conference conference)3582 public void onConferenceAdded(Conference conference) {} 3583 3584 /** 3585 * Called when a conference is removed. 3586 * @hide 3587 */ onConferenceRemoved(Conference conference)3588 public void onConferenceRemoved(Conference conference) {} 3589 3590 /** 3591 * Indicates that a remote conference has been created for existing {@link RemoteConnection}s. 3592 * When this method is invoked, this {@link ConnectionService} should create its own 3593 * representation of the conference call and send it to telecom using {@link #addConference}. 3594 * <p> 3595 * This is only relevant to {@link ConnectionService}s which are registered with 3596 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. 3597 * 3598 * @param conference The remote conference call. 3599 */ onRemoteConferenceAdded(RemoteConference conference)3600 public void onRemoteConferenceAdded(RemoteConference conference) {} 3601 3602 /** 3603 * Called when an existing connection is added remotely. 3604 * @param connection The existing connection which was added. 3605 */ onRemoteExistingConnectionAdded(RemoteConnection connection)3606 public void onRemoteExistingConnectionAdded(RemoteConnection connection) {} 3607 3608 /** 3609 * Called when the {@link ConnectionService} has lost the call focus. 3610 * The {@link ConnectionService} should release the call resources and invokes 3611 * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has 3612 * released the call resources. 3613 */ onConnectionServiceFocusLost()3614 public void onConnectionServiceFocusLost() {} 3615 3616 /** 3617 * Called when the {@link ConnectionService} has gained the call focus. The 3618 * {@link ConnectionService} can acquire the call resources at this time. 3619 */ onConnectionServiceFocusGained()3620 public void onConnectionServiceFocusGained() {} 3621 3622 /** 3623 * @hide 3624 */ containsConference(Conference conference)3625 public boolean containsConference(Conference conference) { 3626 return mIdByConference.containsKey(conference); 3627 } 3628 3629 /** {@hide} */ addRemoteConference(RemoteConference remoteConference)3630 void addRemoteConference(RemoteConference remoteConference) { 3631 onRemoteConferenceAdded(remoteConference); 3632 } 3633 3634 /** {@hide} */ addRemoteExistingConnection(RemoteConnection remoteConnection)3635 void addRemoteExistingConnection(RemoteConnection remoteConnection) { 3636 onRemoteExistingConnectionAdded(remoteConnection); 3637 } 3638 onAccountsInitialized()3639 private void onAccountsInitialized() { 3640 mAreAccountsInitialized = true; 3641 for (Runnable r : mPreInitializationConnectionRequests) { 3642 r.run(); 3643 } 3644 mPreInitializationConnectionRequests.clear(); 3645 } 3646 3647 /** 3648 * Adds an existing connection to the list of connections, identified by a new call ID unique 3649 * to this connection service. 3650 * 3651 * @param connection The connection. 3652 * @return The ID of the connection (e.g. the call-id). 3653 */ addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection)3654 private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) { 3655 String id; 3656 3657 if (connection.getExtras() != null && connection.getExtras() 3658 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 3659 id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 3660 Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s", 3661 connection.getTelecomCallId(), id); 3662 } else if (handle == null) { 3663 // If no phone account handle was provided, we cannot be sure the call ID is unique, 3664 // so just use a random UUID. 3665 id = UUID.randomUUID().toString(); 3666 } else { 3667 // Phone account handle was provided, so use the ConnectionService class name as a 3668 // prefix for a unique incremental call ID. 3669 id = handle.getComponentName().getClassName() + "@" + getNextCallId(); 3670 } 3671 addConnection(handle, id, connection); 3672 return id; 3673 } 3674 addConnection(PhoneAccountHandle handle, String callId, Connection connection)3675 private void addConnection(PhoneAccountHandle handle, String callId, Connection connection) { 3676 connection.setTelecomCallId(callId); 3677 mConnectionById.put(callId, connection); 3678 mIdByConnection.put(connection, callId); 3679 connection.addConnectionListener(mConnectionListener); 3680 connection.setConnectionService(this); 3681 connection.setPhoneAccountHandle(handle); 3682 onConnectionAdded(connection); 3683 } 3684 3685 /** {@hide} */ removeConnection(Connection connection)3686 protected void removeConnection(Connection connection) { 3687 connection.unsetConnectionService(this); 3688 connection.removeConnectionListener(mConnectionListener); 3689 String id = mIdByConnection.get(connection); 3690 if (id != null) { 3691 mConnectionById.remove(id); 3692 mIdByConnection.remove(connection); 3693 mAdapter.removeCall(id); 3694 onConnectionRemoved(connection); 3695 } 3696 } 3697 addConferenceInternal(Conference conference)3698 private String addConferenceInternal(Conference conference) { 3699 String originalId = null; 3700 if (conference.getExtras() != null && conference.getExtras() 3701 .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 3702 originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); 3703 Log.d(this, "addConferenceInternal: conf %s reusing original id %s", 3704 conference.getTelecomCallId(), 3705 originalId); 3706 } 3707 if (mIdByConference.containsKey(conference)) { 3708 Log.w(this, "Re-adding an existing conference: %s.", conference); 3709 } else if (conference != null) { 3710 // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we 3711 // cannot determine a ConnectionService class name to associate with the ID, so use 3712 // a unique UUID (for now). 3713 String id = originalId == null ? UUID.randomUUID().toString() : originalId; 3714 mConferenceById.put(id, conference); 3715 mIdByConference.put(conference, id); 3716 conference.addListener(mConferenceListener); 3717 return id; 3718 } 3719 3720 return null; 3721 } 3722 removeConference(Conference conference)3723 private void removeConference(Conference conference) { 3724 if (mIdByConference.containsKey(conference)) { 3725 conference.removeListener(mConferenceListener); 3726 3727 String id = mIdByConference.get(conference); 3728 mConferenceById.remove(id); 3729 mIdByConference.remove(conference); 3730 mAdapter.removeCall(id); 3731 3732 onConferenceRemoved(conference); 3733 } 3734 } 3735 findConnectionForAction(String callId, String action)3736 private Connection findConnectionForAction(String callId, String action) { 3737 if (callId != null && mConnectionById.containsKey(callId)) { 3738 return mConnectionById.get(callId); 3739 } 3740 Log.w(this, "%s - Cannot find Connection %s", action, callId); 3741 return getNullConnection(); 3742 } 3743 getNullConnection()3744 static synchronized Connection getNullConnection() { 3745 if (sNullConnection == null) { 3746 sNullConnection = new Connection() {}; 3747 } 3748 return sNullConnection; 3749 } 3750 findConferenceForAction(String conferenceId, String action)3751 private Conference findConferenceForAction(String conferenceId, String action) { 3752 if (mConferenceById.containsKey(conferenceId)) { 3753 return mConferenceById.get(conferenceId); 3754 } 3755 Log.w(this, "%s - Cannot find conference %s", action, conferenceId); 3756 return getNullConference(); 3757 } 3758 createConnectionIdList(List<Connection> connections)3759 private List<String> createConnectionIdList(List<Connection> connections) { 3760 List<String> ids = new ArrayList<>(); 3761 for (Connection c : connections) { 3762 if (mIdByConnection.containsKey(c)) { 3763 ids.add(mIdByConnection.get(c)); 3764 } 3765 } 3766 Collections.sort(ids); 3767 return ids; 3768 } 3769 3770 /** 3771 * Builds a list of {@link Connection} and {@link Conference} IDs based on the list of 3772 * {@link Conferenceable}s passed in. 3773 * 3774 * @param conferenceables The {@link Conferenceable} connections and conferences. 3775 * @return List of string conference and call Ids. 3776 */ createIdList(List<Conferenceable> conferenceables)3777 private List<String> createIdList(List<Conferenceable> conferenceables) { 3778 List<String> ids = new ArrayList<>(); 3779 for (Conferenceable c : conferenceables) { 3780 // Only allow Connection and Conference conferenceables. 3781 if (c instanceof Connection) { 3782 Connection connection = (Connection) c; 3783 if (mIdByConnection.containsKey(connection)) { 3784 ids.add(mIdByConnection.get(connection)); 3785 } 3786 } else if (c instanceof Conference) { 3787 Conference conference = (Conference) c; 3788 if (mIdByConference.containsKey(conference)) { 3789 ids.add(mIdByConference.get(conference)); 3790 } 3791 } 3792 } 3793 Collections.sort(ids); 3794 return ids; 3795 } 3796 getNullConference()3797 private Conference getNullConference() { 3798 if (sNullConference == null) { 3799 sNullConference = new Conference(null) {}; 3800 } 3801 return sNullConference; 3802 } 3803 endAllConnections()3804 private void endAllConnections() { 3805 // Unbound from telecomm. We should end all connections and conferences. 3806 for (Connection connection : mIdByConnection.keySet()) { 3807 // only operate on top-level calls. Conference calls will be removed on their own. 3808 if (connection.getConference() == null) { 3809 connection.onDisconnect(); 3810 } 3811 } 3812 for (Conference conference : mIdByConference.keySet()) { 3813 conference.onDisconnect(); 3814 } 3815 } 3816 3817 /** 3818 * Retrieves the next call ID as maintainted by the connection service. 3819 * 3820 * @return The call ID. 3821 */ getNextCallId()3822 private int getNextCallId() { 3823 synchronized (mIdSyncRoot) { 3824 return ++mId; 3825 } 3826 } 3827 3828 /** 3829 * Returns this handler, ONLY FOR TESTING. 3830 * @hide 3831 */ 3832 @VisibleForTesting getHandler()3833 public Handler getHandler() { 3834 return mHandler; 3835 } 3836 3837 /** 3838 * Sets this {@link ConnectionService} ready for testing purposes. 3839 * @hide 3840 */ 3841 @VisibleForTesting setReadyForTest()3842 public void setReadyForTest() { 3843 mAreAccountsInitialized = true; 3844 } 3845 } 3846