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