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 com.android.services.telephony;
18 
19 import static android.telephony.ims.ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
20 import static android.telephony.ims.ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL;
21 import static android.telephony.ims.ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.res.Resources;
28 import android.graphics.drawable.Icon;
29 import android.net.Uri;
30 import android.os.AsyncResult;
31 import android.os.Bundle;
32 import android.os.Handler;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.Messenger;
36 import android.os.PersistableBundle;
37 import android.telecom.CallAudioState;
38 import android.telecom.CallDiagnostics;
39 import android.telecom.CallScreeningService;
40 import android.telecom.Conference;
41 import android.telecom.Connection;
42 import android.telecom.ConnectionService;
43 import android.telecom.PhoneAccount;
44 import android.telecom.PhoneAccountHandle;
45 import android.telecom.StatusHints;
46 import android.telecom.TelecomManager;
47 import android.telecom.VideoProfile;
48 import android.telephony.CarrierConfigManager;
49 import android.telephony.DisconnectCause;
50 import android.telephony.PhoneNumberUtils;
51 import android.telephony.ServiceState;
52 import android.telephony.ServiceState.RilRadioTechnology;
53 import android.telephony.SubscriptionManager;
54 import android.telephony.TelephonyManager;
55 import android.telephony.emergency.EmergencyNumber;
56 import android.telephony.ims.ImsCallProfile;
57 import android.telephony.ims.ImsReasonInfo;
58 import android.telephony.ims.ImsStreamMediaProfile;
59 import android.telephony.ims.RtpHeaderExtension;
60 import android.telephony.ims.RtpHeaderExtensionType;
61 import android.telephony.ims.feature.MmTelFeature;
62 import android.text.TextUtils;
63 import android.util.ArraySet;
64 import android.util.Pair;
65 
66 import com.android.ims.ImsCall;
67 import com.android.ims.ImsException;
68 import com.android.ims.internal.ConferenceParticipant;
69 import com.android.internal.annotations.VisibleForTesting;
70 import com.android.internal.os.SomeArgs;
71 import com.android.internal.telephony.Call;
72 import com.android.internal.telephony.CallFailCause;
73 import com.android.internal.telephony.CallStateException;
74 import com.android.internal.telephony.Connection.Capability;
75 import com.android.internal.telephony.Connection.PostDialListener;
76 import com.android.internal.telephony.Phone;
77 import com.android.internal.telephony.PhoneConstants;
78 import com.android.internal.telephony.d2d.Communicator;
79 import com.android.internal.telephony.d2d.DtmfAdapter;
80 import com.android.internal.telephony.d2d.DtmfTransport;
81 import com.android.internal.telephony.d2d.MessageTypeAndValueHelper;
82 import com.android.internal.telephony.d2d.RtpAdapter;
83 import com.android.internal.telephony.d2d.RtpTransport;
84 import com.android.internal.telephony.d2d.Timeouts;
85 import com.android.internal.telephony.d2d.TransportProtocol;
86 import com.android.internal.telephony.flags.Flags;
87 import com.android.internal.telephony.gsm.SuppServiceNotification;
88 import com.android.internal.telephony.imsphone.ImsPhone;
89 import com.android.internal.telephony.imsphone.ImsPhoneCall;
90 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
91 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
92 import com.android.phone.ImsUtil;
93 import com.android.phone.PhoneGlobals;
94 import com.android.phone.PhoneUtils;
95 import com.android.phone.R;
96 import com.android.phone.callcomposer.CallComposerPictureManager;
97 import com.android.phone.callcomposer.CallComposerPictureTransfer;
98 import com.android.telephony.Rlog;
99 
100 import java.util.ArrayList;
101 import java.util.Arrays;
102 import java.util.Collections;
103 import java.util.HashMap;
104 import java.util.List;
105 import java.util.Locale;
106 import java.util.Map;
107 import java.util.Objects;
108 import java.util.Set;
109 import java.util.concurrent.ConcurrentHashMap;
110 import java.util.concurrent.Executors;
111 import java.util.stream.Collectors;
112 
113 /**
114  * Base class for CDMA and GSM connections.
115  */
116 abstract class TelephonyConnection extends Connection implements Holdable, Communicator.Callback {
117     private static final String LOG_TAG = "TelephonyConnection";
118 
119     private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
120     private static final int MSG_RINGBACK_TONE = 2;
121     private static final int MSG_HANDOVER_STATE_CHANGED = 3;
122     private static final int MSG_DISCONNECT = 4;
123     private static final int MSG_MULTIPARTY_STATE_CHANGED = 5;
124     private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
125     private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
126 
127     // the threshold used to compare mAudioCodecBitrateKbps and mAudioCodecBandwidth.
128     private static final float THRESHOLD = 0.01f;
129 
130     /**
131      * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
132      * equivalents defined in {@link android.telecom.Connection}.
133      */
134     private static final Map<String, String> sExtrasMap = createExtrasMap();
135 
136     private static final int MSG_SET_VIDEO_STATE = 8;
137     private static final int MSG_SET_VIDEO_PROVIDER = 9;
138     private static final int MSG_SET_AUDIO_QUALITY = 10;
139     private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 11;
140     private static final int MSG_CONNECTION_EXTRAS_CHANGED = 12;
141     private static final int MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES = 13;
142     private static final int MSG_ON_HOLD_TONE = 14;
143     private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
144     private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
145     private static final int MSG_HANGUP = 17;
146     private static final int MSG_SET_CALL_RADIO_TECH = 18;
147     private static final int MSG_ON_CONNECTION_EVENT = 19;
148     private static final int MSG_REDIAL_CONNECTION_CHANGED = 20;
149     private static final int MSG_REJECT = 21;
150     private static final int MSG_DTMF_DONE = 22;
151     private static final int MSG_MEDIA_ATTRIBUTES_CHANGED = 23;
152     private static final int MSG_ON_RTT_INITIATED = 24;
153     private static final int MSG_HOLD = 25;
154     private static final int MSG_UNHOLD = 26;
155 
156     private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
157     private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
158 
159     private List<Uri> mParticipants;
160     private boolean mIsAdhocConferenceCall;
161 
162     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
163         @Override
164         public void handleMessage(Message msg) {
165             switch (msg.what) {
166                 case MSG_PRECISE_CALL_STATE_CHANGED:
167                     Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED");
168                     updateState();
169                     break;
170                 case MSG_HANDOVER_STATE_CHANGED:
171                     // fall through
172                 case MSG_REDIAL_CONNECTION_CHANGED:
173                     String what = (msg.what == MSG_HANDOVER_STATE_CHANGED)
174                             ? "MSG_HANDOVER_STATE_CHANGED" : "MSG_REDIAL_CONNECTION_CHANGED";
175                     Log.i(TelephonyConnection.this, "Connection changed due to: %s", what);
176                     AsyncResult ar = (AsyncResult) msg.obj;
177                     com.android.internal.telephony.Connection connection =
178                          (com.android.internal.telephony.Connection) ar.result;
179                     onOriginalConnectionRedialed(connection);
180                     break;
181                 case MSG_RINGBACK_TONE:
182                     Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE");
183                     // TODO: This code assumes that there is only one connection in the foreground
184                     // call, in other words, it punts on network-mediated conference calling.
185                     if (getOriginalConnection() != getForegroundConnection()) {
186                         Log.v(TelephonyConnection.this, "handleMessage, original connection is " +
187                                 "not foreground connection, skipping");
188                         return;
189                     }
190                     boolean ringback = (Boolean) ((AsyncResult) msg.obj).result;
191                     setRingbackRequested(ringback);
192                     notifyRingbackRequested(ringback);
193                     break;
194                 case MSG_DISCONNECT:
195                     updateState();
196                     break;
197                 case MSG_MULTIPARTY_STATE_CHANGED:
198                     boolean isMultiParty = (Boolean) msg.obj;
199                     Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
200                     mIsMultiParty = isMultiParty;
201                     if (isMultiParty) {
202                         notifyConferenceStarted();
203                     }
204                     break;
205                 case MSG_CONFERENCE_MERGE_FAILED:
206                     notifyConferenceMergeFailed();
207                     break;
208                 case MSG_SUPP_SERVICE_NOTIFY:
209                     Phone phone = getPhone();
210                     Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : "
211                             + (phone != null ? Integer.toString(phone.getPhoneId())
212                             : "null"));
213                     SuppServiceNotification mSsNotification = null;
214                     if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
215                         mSsNotification =
216                                 (SuppServiceNotification)((AsyncResult) msg.obj).result;
217                         if (mOriginalConnection != null) {
218                             handleSuppServiceNotification(mSsNotification);
219                         }
220                     }
221                     break;
222 
223                 case MSG_SET_VIDEO_STATE:
224                     int videoState = (int) msg.obj;
225                     setTelephonyVideoState(videoState);
226 
227                     // A change to the video state of the call can influence whether or not it
228                     // can be part of a conference, whether another call can be added, and
229                     // whether the call should have the HD audio property set.
230                     refreshConferenceSupported();
231                     refreshDisableAddCall();
232                     refreshHoldSupported();
233                     updateConnectionProperties();
234                     break;
235 
236                 case MSG_SET_VIDEO_PROVIDER:
237                     VideoProvider videoProvider = (VideoProvider) msg.obj;
238                     setTelephonyVideoProvider(videoProvider);
239                     break;
240 
241                 case MSG_SET_AUDIO_QUALITY:
242                     int audioQuality = (int) msg.obj;
243                     setAudioQuality(audioQuality);
244                     break;
245 
246                 case MSG_MEDIA_ATTRIBUTES_CHANGED:
247                     refreshCodec();
248                     break;
249 
250                 case MSG_SET_CONFERENCE_PARTICIPANTS:
251                     List<ConferenceParticipant> participants = (List<ConferenceParticipant>) msg.obj;
252                     updateConferenceParticipants(participants);
253                     break;
254 
255                 case MSG_CONNECTION_EXTRAS_CHANGED:
256                     final Bundle extras = (Bundle) msg.obj;
257                     updateExtras(extras);
258                     break;
259 
260                 case MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES:
261                     setOriginalConnectionCapabilities(msg.arg1);
262                     break;
263 
264                 case MSG_ON_HOLD_TONE:
265                     AsyncResult asyncResult = (AsyncResult) msg.obj;
266                     Pair<com.android.internal.telephony.Connection, Boolean> heldInfo =
267                             (Pair<com.android.internal.telephony.Connection, Boolean>)
268                                     asyncResult.result;
269 
270                     // Determines if the hold tone is starting or stopping.
271                     boolean playTone = ((Boolean) (heldInfo.second)).booleanValue();
272 
273                     // Determine which connection the hold tone is stopping or starting for
274                     com.android.internal.telephony.Connection heldConnection = heldInfo.first;
275 
276                     // Only start or stop the hold tone if this is the connection which is starting
277                     // or stopping the hold tone.
278                     if (heldConnection == mOriginalConnection) {
279                         // If starting the hold tone, send a connection event to Telecom which will
280                         // cause it to play the on hold tone.
281                         if (playTone) {
282                             sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
283                         } else {
284                             sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
285                         }
286                     }
287                     break;
288 
289                 case MSG_CDMA_VOICE_PRIVACY_ON:
290                     Log.d(this, "MSG_CDMA_VOICE_PRIVACY_ON received");
291                     setCdmaVoicePrivacy(true);
292                     break;
293                 case MSG_CDMA_VOICE_PRIVACY_OFF:
294                     Log.d(this, "MSG_CDMA_VOICE_PRIVACY_OFF received");
295                     setCdmaVoicePrivacy(false);
296                     break;
297                 case MSG_HANGUP:
298                     int cause = (int) msg.obj;
299                     hangup(cause);
300                     break;
301                 case MSG_REJECT:
302                     int rejectReason = (int) msg.obj;
303                     reject(rejectReason);
304                     break;
305                 case MSG_DTMF_DONE:
306                     Log.i(this, "MSG_DTMF_DONE");
307                     break;
308 
309                 case MSG_SET_CALL_RADIO_TECH:
310                     int vrat = (int) msg.obj;
311                     // Check whether Wi-Fi call tech is changed, it means call radio tech is:
312                     //  a) changed from IWLAN to other value, or
313                     //  b) changed from other value to IWLAN.
314                     //
315                     // In other word, below conditions are all met:
316                     // 1) {@link #getCallRadioTech} is different from new vrat
317                     // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi}
318                     //    is true, or new vrat indicates Wi-Fi call.
319                     boolean isWifiTechChange = getCallRadioTech() != vrat
320                             && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
321 
322                     // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related
323                     // update actions are taken correctly.
324                     setCallRadioTech(vrat);
325 
326                     // Step 2) Handles Wi-Fi call tech change.
327                     if (isWifiTechChange) {
328                         updateConnectionProperties();
329                         updateStatusHints();
330                         refreshDisableAddCall();
331                     }
332                     break;
333                 case MSG_ON_CONNECTION_EVENT:
334                     SomeArgs args = (SomeArgs) msg.obj;
335                     try {
336                         sendTelephonyConnectionEvent((String) args.arg1, (Bundle) args.arg2);
337                     } finally {
338                         args.recycle();
339                     }
340                     break;
341                 case MSG_ON_RTT_INITIATED:
342                     if (mOriginalConnection != null) {
343                         // if mOriginalConnection is null, the properties will get set when
344                         // mOriginalConnection gets set.
345                         updateConnectionProperties();
346                         refreshConferenceSupported();
347                     }
348                     sendRttInitiationSuccess();
349                     break;
350                 case MSG_HOLD:
351                     performHold();
352                     break;
353                 case MSG_UNHOLD:
354                     performUnhold();
355                     break;
356             }
357         }
358     };
359 
360     private final Messenger mHandlerMessenger = new Messenger(mHandler);
361 
362     /**
363      * The underlying telephony Connection has been redialed on a different domain (CS or IMS).
364      * Track the new telephony Connection and set back up appropriate callbacks.
365      * @param connection The new telephony Connection associated with this TelephonyConnection.
366      */
367     @VisibleForTesting
onOriginalConnectionRedialed( com.android.internal.telephony.Connection connection)368     public void onOriginalConnectionRedialed(
369             com.android.internal.telephony.Connection connection) {
370         if (connection == null) {
371             setDisconnected(DisconnectCauseUtil
372                     .toTelecomDisconnectCause(DisconnectCause.OUT_OF_NETWORK,
373                             "handover failure, no connection"));
374             close();
375             return;
376         }
377         if (mOriginalConnection != null) {
378             if ((connection.getAddress() != null
379                     && mOriginalConnection.getAddress() != null
380                     && mOriginalConnection.getAddress().equals(connection.getAddress()))
381                     || connection.getState() == mOriginalConnection.getStateBeforeHandover()) {
382                 Log.i(TelephonyConnection.this, "Setting original connection after"
383                         + " handover or redial, current original connection="
384                         + mOriginalConnection.toString()
385                         + ", new original connection="
386                         + connection.toString());
387                 setOriginalConnection(connection);
388                 mWasImsConnection = false;
389                 if (mHangupDisconnectCause != DisconnectCause.NOT_VALID) {
390                     // A hangup request was initiated during the handover process, so
391                     // go ahead and initiate the hangup on the new connection.
392                     try {
393                         Log.i(TelephonyConnection.this, "user has tried to hangup "
394                                 + "during handover, retrying hangup.");
395                         connection.hangup();
396                     } catch (CallStateException e) {
397                         // Call state exception may be thrown if the connection was
398                         // already disconnected, so just log this case.
399                         Log.w(TelephonyConnection.this, "hangup during "
400                                 + "handover or redial resulted in an exception:" + e);
401                     }
402                 }
403             }
404         } else {
405             Log.w(TelephonyConnection.this, " mOriginalConnection==null --"
406                     + " invalid state (not cleaned up)");
407         }
408     }
409 
410     /**
411      * Handles {@link SuppServiceNotification}s pertinent to Telephony.
412      * @param ssn the notification.
413      */
handleSuppServiceNotification(SuppServiceNotification ssn)414     private void handleSuppServiceNotification(SuppServiceNotification ssn) {
415         Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType,
416                 ssn.code);
417         if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
418                 && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
419             sendTelephonyConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
420         }
421         sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
422     }
423 
424     /**
425      * Sends a supplementary service notification connection event.
426      * This connection event includes the type and code, as well as a human readable message which
427      * is suitable for display to the user if the UI chooses to do so.
428      * @param type the {@link SuppServiceNotification#type}.
429      * @param code the {@link SuppServiceNotification#code}.
430      */
sendSuppServiceNotificationEvent(int type, int code)431     private void sendSuppServiceNotificationEvent(int type, int code) {
432         Bundle extras = new Bundle();
433         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
434         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
435         extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
436                 getSuppServiceMessage(type, code));
437         sendTelephonyConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION,
438                 extras);
439     }
440 
441     /**
442      * Retrieves a human-readable message for a supplementary service notification.
443      * This message is suitable for display to the user.
444      * @param type the code group.
445      * @param code the code.
446      * @return A {@link CharSequence} containing the message, or {@code null} if none defined.
447      */
getSuppServiceMessage(int type, int code)448     private CharSequence getSuppServiceMessage(int type, int code) {
449         int messageId = -1;
450         if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) {
451             switch (code) {
452                 case SuppServiceNotification.CODE_1_CALL_DEFLECTED:
453                     messageId = R.string.supp_service_notification_call_deflected;
454                     break;
455                 case SuppServiceNotification.CODE_1_CALL_FORWARDED:
456                     messageId = R.string.supp_service_notification_call_forwarded;
457                     break;
458                 case SuppServiceNotification.CODE_1_CALL_IS_WAITING:
459                     messageId = R.string.supp_service_notification_call_waiting;
460                     break;
461                 case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED:
462                     messageId = R.string.supp_service_clir_suppression_rejected;
463                     break;
464                 case SuppServiceNotification.CODE_1_CUG_CALL:
465                     messageId = R.string.supp_service_closed_user_group_call;
466                     break;
467                 case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED:
468                     messageId = R.string.supp_service_incoming_calls_barred;
469                     break;
470                 case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED:
471                     messageId = R.string.supp_service_outgoing_calls_barred;
472                     break;
473                 case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE:
474                     // Intentional fall through.
475                 case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE:
476                     messageId = R.string.supp_service_call_forwarding_active;
477                     break;
478             }
479         } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) {
480             switch (code) {
481                 case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED:
482                     messageId = R.string.supp_service_additional_call_forwarded;
483                     break;
484                 case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT:
485                     messageId = R.string.supp_service_additional_ect_connected;
486                     break;
487                 case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT:
488                     messageId = R.string.supp_service_additional_ect_connecting;
489                     break;
490                 case SuppServiceNotification.CODE_2_CALL_ON_HOLD:
491                     messageId = R.string.supp_service_call_on_hold;
492                     break;
493                 case SuppServiceNotification.CODE_2_CALL_RETRIEVED:
494                     messageId = R.string.supp_service_call_resumed;
495                     break;
496                 case SuppServiceNotification.CODE_2_CUG_CALL:
497                     messageId = R.string.supp_service_closed_user_group_call;
498                     break;
499                 case SuppServiceNotification.CODE_2_DEFLECTED_CALL:
500                     messageId = R.string.supp_service_deflected_call;
501                     break;
502                 case SuppServiceNotification.CODE_2_FORWARDED_CALL:
503                     messageId = R.string.supp_service_forwarded_call;
504                     break;
505                 case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL:
506                     messageId = R.string.supp_service_conference_call;
507                     break;
508                 case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED:
509                     messageId = R.string.supp_service_held_call_released;
510                     break;
511             }
512         }
513         if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
514             return getResourceText(messageId);
515         } else {
516             return null;
517         }
518     }
519 
520     @VisibleForTesting
getResourceText(int id)521     public CharSequence getResourceText(int id) {
522         Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(),
523                 getPhone().getSubId());
524         return resources.getText(id);
525     }
526 
527     @VisibleForTesting
getResourceString(int id)528     public String getResourceString(int id) {
529         Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(),
530                 getPhone().getSubId());
531         return resources.getString(id);
532     }
533 
534     /**
535      * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
536      */
isCarrierVideoConferencingSupported()537     public boolean isCarrierVideoConferencingSupported() {
538         return mIsCarrierVideoConferencingSupported;
539     }
540 
541     /**
542      * A listener/callback mechanism that is specific communication from TelephonyConnections
543      * to TelephonyConnectionService (for now). It is more specific that Connection.Listener
544      * because it is only exposed in Telephony.
545      */
546     public abstract static class TelephonyConnectionListener {
onOriginalConnectionConfigured(TelephonyConnection c)547         public void onOriginalConnectionConfigured(TelephonyConnection c) {}
onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure)548         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
onConferenceParticipantsChanged(Connection c, List<ConferenceParticipant> participants)549         public void onConferenceParticipantsChanged(Connection c,
550                 List<ConferenceParticipant> participants) {}
onConferenceStarted()551         public void onConferenceStarted() {}
onConferenceSupportedChanged(Connection c, boolean isConferenceSupported)552         public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
553 
onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities)554         public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {}
onConnectionEvent(Connection c, String event, Bundle extras)555         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
onConnectionPropertiesChanged(Connection c, int connectionProperties)556         public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {}
onExtrasChanged(Connection c, Bundle extras)557         public void onExtrasChanged(Connection c, Bundle extras) {}
onExtrasRemoved(Connection c, List<String> keys)558         public void onExtrasRemoved(Connection c, List<String> keys) {}
onStateChanged(android.telecom.Connection c, int state)559         public void onStateChanged(android.telecom.Connection c, int state) {}
onStatusHintsChanged(Connection c, StatusHints statusHints)560         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
onDestroyed(Connection c)561         public void onDestroyed(Connection c) {}
onDisconnected(android.telecom.Connection c, android.telecom.DisconnectCause disconnectCause)562         public void onDisconnected(android.telecom.Connection c,
563                 android.telecom.DisconnectCause disconnectCause) {}
onVideoProviderChanged(android.telecom.Connection c, Connection.VideoProvider videoProvider)564         public void onVideoProviderChanged(android.telecom.Connection c,
565                 Connection.VideoProvider videoProvider) {}
onVideoStateChanged(android.telecom.Connection c, int videoState)566         public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
onRingbackRequested(Connection c, boolean ringback)567         public void onRingbackRequested(Connection c, boolean ringback) {}
568     }
569 
570     public static class D2DCallStateAdapter extends TelephonyConnectionListener {
571         private Communicator mCommunicator;
572 
D2DCallStateAdapter(Communicator communicator)573         D2DCallStateAdapter(Communicator communicator) {
574             mCommunicator = communicator;
575         }
576 
577         @Override
onStateChanged(android.telecom.Connection c, int state)578         public void onStateChanged(android.telecom.Connection c, int state) {
579             mCommunicator.onStateChanged(c.getTelecomCallId(), state);
580         }
581     }
582 
583     private final PostDialListener mPostDialListener = new PostDialListener() {
584         @Override
585         public void onPostDialWait() {
586             Log.v(TelephonyConnection.this, "onPostDialWait");
587             if (mOriginalConnection != null) {
588                 setPostDialWait(mOriginalConnection.getRemainingPostDialString());
589             }
590         }
591 
592         @Override
593         public void onPostDialChar(char c) {
594             Log.v(TelephonyConnection.this, "onPostDialChar: %s", c);
595             if (mOriginalConnection != null) {
596                 setNextPostDialChar(c);
597             }
598         }
599     };
600 
601     /**
602      * Listener for listening to events in the {@link com.android.internal.telephony.Connection}.
603      */
604     private final com.android.internal.telephony.Connection.Listener mOriginalConnectionListener =
605             new com.android.internal.telephony.Connection.ListenerBase() {
606         @Override
607         public void onVideoStateChanged(int videoState) {
608             mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState).sendToTarget();
609         }
610 
611         /*
612          * The {@link com.android.internal.telephony.Connection} has reported a change in
613          * connection capability.
614          * @param capabilities bit mask containing voice or video or both capabilities.
615          */
616         @Override
617         public void onConnectionCapabilitiesChanged(int capabilities) {
618             mHandler.obtainMessage(MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES,
619                     capabilities, 0).sendToTarget();
620         }
621 
622         /**
623          * The {@link com.android.internal.telephony.Connection} has reported a change in the
624          * video call provider.
625          *
626          * @param videoProvider The video call provider.
627          */
628         @Override
629         public void onVideoProviderChanged(VideoProvider videoProvider) {
630             mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, videoProvider).sendToTarget();
631         }
632 
633         /**
634          * Used by {@link com.android.internal.telephony.Connection} to report a change for
635          * the call radio technology.
636          *
637          * @param vrat the RIL Voice Radio Technology used for current connection.
638          */
639         @Override
640         public void onCallRadioTechChanged(@RilRadioTechnology int vrat) {
641             mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
642         }
643 
644         /**
645          * Used by the {@link com.android.internal.telephony.Connection} to report a change in the
646          * audio quality for the current call.
647          *
648          * @param audioQuality The audio quality.
649          */
650         @Override
651         public void onAudioQualityChanged(int audioQuality) {
652             mHandler.obtainMessage(MSG_SET_AUDIO_QUALITY, audioQuality).sendToTarget();
653         }
654 
655         @Override
656         public void onMediaAttributesChanged() {
657             mHandler.obtainMessage(MSG_MEDIA_ATTRIBUTES_CHANGED).sendToTarget();
658         }
659 
660         /**
661          * Handles a change in the state of conference participant(s), as reported by the
662          * {@link com.android.internal.telephony.Connection}.
663          *
664          * @param participants The participant(s) which changed.
665          */
666         @Override
667         public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {
668             mHandler.obtainMessage(MSG_SET_CONFERENCE_PARTICIPANTS, participants).sendToTarget();
669         }
670 
671         /*
672          * Handles a change to the multiparty state for this connection.
673          *
674          * @param isMultiParty {@code true} if the call became multiparty, {@code false}
675          *      otherwise.
676          */
677         @Override
678         public void onMultipartyStateChanged(boolean isMultiParty) {
679             handleMultipartyStateChange(isMultiParty);
680         }
681 
682         /**
683          * Handles the event that the request to merge calls failed.
684          */
685         @Override
686         public void onConferenceMergedFailed() {
687             handleConferenceMergeFailed();
688         }
689 
690         @Override
691         public void onExtrasChanged(Bundle extras) {
692             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, extras).sendToTarget();
693         }
694 
695         /**
696          * Handles the phone exiting ECM mode by updating the connection capabilities.  During an
697          * ongoing call, if ECM mode is exited, we will re-enable mute for CDMA calls.
698          */
699         @Override
700         public void onExitedEcmMode() {
701             handleExitedEcmMode();
702         }
703 
704         /**
705          * Called from {@link ImsPhoneCallTracker} when a request to pull an external call has
706          * failed.
707          * @param externalConnection
708          */
709         @Override
710         public void onCallPullFailed(com.android.internal.telephony.Connection externalConnection) {
711             if (externalConnection == null) {
712                 return;
713             }
714 
715             Log.i(this, "onCallPullFailed - pull failed; swapping back to call: %s",
716                     externalConnection);
717 
718             // Inform the InCallService of the fact that the call pull failed (it may choose to
719             // display a message informing the user of the pull failure).
720             sendTelephonyConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
721 
722             // Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection
723             // which originally represented the call.
724             setOriginalConnection(externalConnection);
725 
726             // Set our state to active again since we're no longer pulling.
727             setActiveInternal();
728         }
729 
730         /**
731          * Called from {@link ImsPhoneCallTracker} when a handover to WIFI has failed.
732          */
733         @Override
734         public void onHandoverToWifiFailed() {
735             sendTelephonyConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
736         }
737 
738         /**
739          * Informs the {@link android.telecom.ConnectionService} of a connection event raised by the
740          * original connection.
741          * @param event The connection event.
742          * @param extras The extras.
743          */
744         @Override
745         public void onConnectionEvent(String event, Bundle extras) {
746             SomeArgs args = SomeArgs.obtain();
747             args.arg1 = event;
748             args.arg2 = extras;
749             if (EVENT_MERGE_COMPLETE.equals(event)){
750                 // To ensure the MERGE_COMPLETE event logs before the listeners are removed,
751                 // circumvent the handler by sending the connection event directly:
752                 sendTelephonyConnectionEvent(event, extras);
753             } else {
754                 mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
755             }
756         }
757 
758         @Override
759         public void onRttModifyRequestReceived() {
760             sendRemoteRttRequest();
761         }
762 
763         @Override
764         public void onRttModifyResponseReceived(int status) {
765             updateConnectionProperties();
766             refreshConferenceSupported();
767             if (status == RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
768                 sendRttInitiationSuccess();
769             } else {
770                 sendRttInitiationFailure(status);
771             }
772         }
773 
774         @Override
775         public void onDisconnect(int cause) {
776             Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(),
777                     DisconnectCause.toString(cause));
778             mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget();
779         }
780 
781         @Override
782         public void onRttInitiated() {
783             Log.i(TelephonyConnection.this, "onRttInitiated: callId=%s", getTelecomCallId());
784             // Post RTT initiation to the Handler associated with this TelephonyConnection.
785             // This avoids a race condition where a call starts as RTT but ConnectionService call to
786             // handleCreateConnectionComplete happens AFTER the RTT status is reported to Telecom.
787             mHandler.obtainMessage(MSG_ON_RTT_INITIATED).sendToTarget();
788         }
789 
790         @Override
791         public void onRttTerminated() {
792             updateConnectionProperties();
793             refreshConferenceSupported();
794             sendRttSessionRemotelyTerminated();
795         }
796 
797         @Override
798         public void onOriginalConnectionReplaced(
799                 com.android.internal.telephony.Connection newConnection) {
800             Log.i(TelephonyConnection.this, "onOriginalConnectionReplaced; newConn=%s",
801                     newConnection);
802             setOriginalConnection(newConnection);
803         }
804 
805         @Override
806         public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) {
807             setIsNetworkIdentifiedEmergencyCall(isEmergencyCall);
808         }
809 
810         /**
811          * Indicates data from an RTP header extension has been received from the network.
812          * @param extensionData The extension data.
813          */
814         @Override
815         public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {
816             if (mRtpTransport == null) {
817                 return;
818             }
819             Log.i(this, "onReceivedRtpHeaderExtensions: received %d extensions",
820                     extensionData.size());
821             mRtpTransport.onRtpHeaderExtensionsReceived(extensionData);
822         }
823 
824         @Override
825         public void onReceivedDtmfDigit(char digit) {
826             if (mDtmfTransport == null) {
827                 return;
828             }
829             Log.i(this, "onReceivedDtmfDigit: digit=%c", digit);
830             mDtmfTransport.onDtmfReceived(digit);
831         }
832 
833         @Override
834         public void onAudioModeIsVoipChanged(int imsAudioHandler) {
835             boolean isVoip = imsAudioHandler == MmTelFeature.AUDIO_HANDLER_ANDROID;
836             Log.i(this, "onAudioModeIsVoipChanged isVoip =" + isVoip);
837             setAudioModeIsVoip(isVoip);
838         }
839     };
840 
841     private TelephonyConnectionService mTelephonyConnectionService;
842     protected com.android.internal.telephony.Connection mOriginalConnection;
843     private Phone mPhoneForEvents;
844     private Call.State mConnectionState = Call.State.IDLE;
845     private Bundle mOriginalConnectionExtras = new Bundle();
846     private boolean mIsStateOverridden = false;
847     private Call.State mOriginalConnectionState = Call.State.IDLE;
848     private Call.State mConnectionOverriddenState = Call.State.IDLE;
849     private RttTextStream mRttTextStream = null;
850 
851     private boolean mWasImsConnection;
852     private boolean mWasCrossSim;
853 
854     /**
855      * Tracks the multiparty state of the ImsCall so that changes in the bit state can be detected.
856      */
857     private boolean mIsMultiParty = false;
858 
859     /**
860      * The {@link com.android.internal.telephony.Connection} capabilities associated with the
861      * current {@link #mOriginalConnection}.
862      */
863     private int mOriginalConnectionCapabilities;
864 
865     /**
866      * Determines the audio quality is high for the {@link TelephonyConnection}.
867      * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to
868      * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
869      */
870     private boolean mHasHighDefAudio;
871 
872     /**
873      * Indicates that the connection should be treated as an emergency call because the
874      * number dialed matches an internal list of emergency numbers. Does not guarantee whether
875      * the network will treat the call as an emergency call.
876      */
877     private boolean mTreatAsEmergencyCall;
878 
879     /**
880      * Indicates whether the network has identified this call as an emergency call.  Where
881      * {@link #mTreatAsEmergencyCall} is based on comparing dialed numbers to a list of known
882      * emergency numbers, this property is based on whether the network itself has identified the
883      * call as an emergency call (which can be the case for an incoming call from emergency
884      * services).
885      */
886     private boolean mIsNetworkIdentifiedEmergencyCall;
887 
888     /**
889      * For video calls, indicates whether the outgoing video for the call can be paused using
890      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
891      */
892     private boolean mIsVideoPauseSupported;
893 
894     /**
895      * Indicates whether this connection supports being a part of a conference..
896      */
897     private boolean mIsConferenceSupported;
898 
899     /**
900      * Indicates whether managing conference call is supported after this connection being
901      * a part of a IMS conference.
902      */
903     private boolean mIsManageImsConferenceCallSupported;
904 
905     /**
906      * Indicates whether the carrier supports video conferencing; captures the current state of the
907      * carrier config
908      * {@link android.telephony.CarrierConfigManager#KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL}.
909      */
910     private boolean mIsCarrierVideoConferencingSupported;
911 
912     /**
913      * Indicates whether or not this connection has CDMA Enhanced Voice Privacy enabled.
914      */
915     private boolean mIsCdmaVoicePrivacyEnabled;
916 
917     /**
918      * Indicates whether the connection can be held. This filed combined with the state of the
919      * connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the
920      * connection.
921      */
922     private boolean mIsHoldable;
923 
924     /**
925      * Indicates whether TTY is enabled; used to determine whether a call is VT capable.
926      */
927     private boolean mIsTtyEnabled;
928 
929     /**
930      * Indicates whether this call is using assisted dialing.
931      */
932     private boolean mIsUsingAssistedDialing;
933 
934     /**
935      * Indicates whether this connection supports showing preciese call failed cause.
936      */
937     private boolean mShowPreciseFailedCause;
938 
939     /**
940      * Provides a DisconnectCause associated with a hang up request.
941      */
942     private int mHangupDisconnectCause = DisconnectCause.NOT_VALID;
943 
944     /**
945      * Provides a means for a {@link Communicator} to be informed of call state changes.
946      */
947     private D2DCallStateAdapter mD2DCallStateAdapter;
948 
949     private RtpTransport mRtpTransport;
950 
951     private DtmfTransport mDtmfTransport;
952 
953     /**
954      * Facilitates device to device communication.
955      */
956     private Communicator mCommunicator;
957 
958     /**
959      * Listeners to our TelephonyConnection specific callbacks
960      */
961     private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
962             new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
963 
964     private Integer mEmergencyServiceCategory = null;
965     private List<String> mEmergencyUrns = null;
966 
TelephonyConnection(com.android.internal.telephony.Connection originalConnection, String callId, int callDirection)967     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
968             String callId, int callDirection) {
969         setCallDirection(callDirection);
970         setTelecomCallId(callId);
971         if (originalConnection != null) {
972             setOriginalConnection(originalConnection);
973         }
974     }
975 
976     @VisibleForTesting
TelephonyConnection()977     protected TelephonyConnection() {
978         // Do nothing
979     }
980 
981     @Override
onCallEvent(String event, Bundle extras)982     public void onCallEvent(String event, Bundle extras) {
983         switch (event) {
984             case Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE:
985                 // A Device to device message is being sent by a CallDiagnosticService.
986                 handleOutgoingDeviceToDeviceMessage(extras);
987                 break;
988             default:
989                 break;
990         }
991 
992     }
993     /**
994      * Creates a clone of the current {@link TelephonyConnection}.
995      *
996      * @return The clone.
997      */
cloneConnection()998     public abstract TelephonyConnection cloneConnection();
999 
1000     @Override
onCallAudioStateChanged(CallAudioState audioState)1001     public void onCallAudioStateChanged(CallAudioState audioState) {
1002         // TODO: update TTY mode.
1003         if (getPhone() != null) {
1004             getPhone().setEchoSuppressionEnabled();
1005         }
1006     }
1007 
1008     @Override
onStateChanged(int state)1009     public void onStateChanged(int state) {
1010         Log.v(this, "onStateChanged, state: " + Connection.stateToString(state));
1011         updateStatusHints();
1012     }
1013 
1014     @Override
onDisconnect()1015     public void onDisconnect() {
1016         Log.v(this, "onDisconnect");
1017         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
1018     }
1019 
1020     @Override
onSeparate()1021     public void onSeparate() {
1022         Log.v(this, "onSeparate");
1023         if (mOriginalConnection != null) {
1024             try {
1025                 mOriginalConnection.separate();
1026             } catch (CallStateException e) {
1027                 Log.e(this, e, "Call to Connection.separate failed with exception");
1028             }
1029         }
1030     }
1031 
1032     @Override
onAddConferenceParticipants(List<Uri> participants)1033     public void onAddConferenceParticipants(List<Uri> participants) {
1034         performAddConferenceParticipants(participants);
1035     }
1036 
1037     @Override
onAbort()1038     public void onAbort() {
1039         Log.v(this, "onAbort");
1040         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
1041     }
1042 
1043     @Override
onHold()1044     public void onHold() {
1045         mHandler.obtainMessage(MSG_HOLD).sendToTarget();
1046     }
1047 
1048     @Override
onUnhold()1049     public void onUnhold() {
1050         mHandler.obtainMessage(MSG_UNHOLD).sendToTarget();
1051     }
1052 
1053     @Override
onAnswer(int videoState)1054     public void onAnswer(int videoState) {
1055         performAnswer(videoState);
1056     }
1057 
1058     @Override
onDeflect(Uri address)1059     public void onDeflect(Uri address) {
1060         Log.v(this, "onDeflect");
1061         if (mOriginalConnection != null && isValidRingingCall()) {
1062             if (address == null) {
1063                 Log.w(this, "call deflect address uri is null");
1064                 return;
1065             }
1066             String scheme = address.getScheme();
1067             String deflectNumber = "";
1068             String uriString = address.getSchemeSpecificPart();
1069             if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
1070                 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
1071                     Log.w(this, "onDeflect, address scheme is not of type tel instead: " +
1072                             scheme);
1073                     return;
1074                 }
1075                 if (PhoneNumberUtils.isUriNumber(uriString)) {
1076                     Log.w(this, "Invalid deflect address. Not a legal PSTN number.");
1077                     return;
1078                 }
1079                 deflectNumber = PhoneNumberUtils.convertAndStrip(uriString);
1080                 if (TextUtils.isEmpty(deflectNumber)) {
1081                     Log.w(this, "Empty deflect number obtained from address uri");
1082                     return;
1083                 }
1084             } else {
1085                 Log.w(this, "Cannot deflect to voicemail uri");
1086                 return;
1087             }
1088 
1089             try {
1090                 mOriginalConnection.deflect(deflectNumber);
1091             } catch (CallStateException e) {
1092                 Log.e(this, e, "Failed to deflect call.");
1093             }
1094         }
1095     }
1096 
1097     @Override
onReject()1098     public void onReject() {
1099         performReject(android.telecom.Call.REJECT_REASON_DECLINED);
1100     }
1101 
1102     @Override
onReject(@ndroid.telecom.Call.RejectReason int rejectReason)1103     public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
1104         performReject(rejectReason);
1105     }
1106 
performReject(int rejectReason)1107     public void performReject(int rejectReason) {
1108         Log.v(this, "performReject");
1109         if (isValidRingingCall()) {
1110             mHandler.obtainMessage(MSG_REJECT, rejectReason)
1111                     .sendToTarget();
1112         }
1113         super.onReject();
1114     }
1115 
1116     @Override
onTransfer(Uri number, boolean isConfirmationRequired)1117     public void onTransfer(Uri number, boolean isConfirmationRequired) {
1118         Log.v(this, "onTransfer");
1119         if (mOriginalConnection != null) {
1120             if (number == null) {
1121                 Log.w(this, "call transfer uri is null");
1122                 return;
1123             }
1124             String scheme = number.getScheme();
1125             String transferNumber = "";
1126             String uriString = number.getSchemeSpecificPart();
1127             if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
1128                 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
1129                     Log.w(this, "onTransfer, number scheme is not of type tel instead: "
1130                             + scheme);
1131                     return;
1132                 }
1133                 if (PhoneNumberUtils.isUriNumber(uriString)) {
1134                     Log.w(this, "Invalid transfer address. Not a legal PSTN number.");
1135                     return;
1136                 }
1137                 transferNumber = PhoneNumberUtils.convertAndStrip(uriString);
1138                 if (TextUtils.isEmpty(transferNumber)) {
1139                     Log.w(this, "Empty transfer number obtained from uri");
1140                     return;
1141                 }
1142             } else {
1143                 Log.w(this, "Cannot transfer to voicemail uri");
1144                 return;
1145             }
1146 
1147             try {
1148                 mOriginalConnection.transfer(transferNumber, isConfirmationRequired);
1149             } catch (CallStateException e) {
1150                 Log.e(this, e, "Failed to transfer call.");
1151             }
1152         }
1153     }
1154 
1155     @Override
onTransfer(Connection otherConnection)1156     public void onTransfer(Connection otherConnection) {
1157         Log.v(this, "onConsultativeTransfer");
1158         if (mOriginalConnection != null && (otherConnection instanceof TelephonyConnection)) {
1159             try {
1160                 mOriginalConnection.consultativeTransfer(
1161                         ((TelephonyConnection) otherConnection).getOriginalConnection());
1162             } catch (CallStateException e) {
1163                 Log.e(this, e, "Failed to transfer call.");
1164             }
1165         }
1166     }
1167 
1168     @Override
onPostDialContinue(boolean proceed)1169     public void onPostDialContinue(boolean proceed) {
1170         Log.v(this, "onPostDialContinue, proceed: " + proceed);
1171         if (mOriginalConnection != null) {
1172             if (proceed) {
1173                 mOriginalConnection.proceedAfterWaitChar();
1174             } else {
1175                 mOriginalConnection.cancelPostDial();
1176             }
1177         }
1178     }
1179 
1180     /**
1181      * Handles requests to pull an external call.
1182      */
1183     @Override
onPullExternalCall()1184     public void onPullExternalCall() {
1185         if ((getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) !=
1186                 Connection.PROPERTY_IS_EXTERNAL_CALL) {
1187             Log.w(this, "onPullExternalCall - cannot pull non-external call");
1188             return;
1189         }
1190 
1191         if (mOriginalConnection != null) {
1192             mOriginalConnection.pullExternalCall();
1193         }
1194     }
1195 
1196     @Override
onStartRtt(RttTextStream textStream)1197     public void onStartRtt(RttTextStream textStream) {
1198         if (isImsConnection()) {
1199             ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
1200             if (originalConnection.isRttEnabledForCall()) {
1201                 originalConnection.setCurrentRttTextStream(textStream);
1202             } else {
1203                 originalConnection.startRtt(textStream);
1204             }
1205         } else {
1206             Log.w(this, "onStartRtt - not in IMS, so RTT cannot be enabled.");
1207         }
1208     }
1209 
1210     @Override
onStopRtt()1211     public void onStopRtt() {
1212         if (isImsConnection()) {
1213             ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
1214             if (originalConnection.isRttEnabledForCall()) {
1215                 originalConnection.stopRtt();
1216             } else {
1217                 Log.w(this, "onStopRtt - not in RTT call, ignoring");
1218             }
1219         } else {
1220             Log.w(this, "onStopRtt - not in IMS, ignoring");
1221         }
1222     }
1223 
1224     @Override
onCallFilteringCompleted(CallFilteringCompletionInfo callFilteringCompletionInfo)1225     public void onCallFilteringCompleted(CallFilteringCompletionInfo callFilteringCompletionInfo) {
1226         // Check what the call screening service has to say, if it's a system dialer.
1227         boolean isAllowedToDisplayPicture;
1228         String callScreeningPackage =
1229                 callFilteringCompletionInfo.getCallScreeningComponent() == null
1230                         ? null
1231                         : callFilteringCompletionInfo.getCallScreeningComponent().getPackageName();
1232         boolean isResponseFromSystemDialer =
1233                 Objects.equals(getPhone().getContext()
1234                         .getSystemService(TelecomManager.class).getSystemDialerPackage(),
1235                         callScreeningPackage);
1236         CallScreeningService.CallResponse callScreeningResponse =
1237                 callFilteringCompletionInfo.getCallResponse();
1238 
1239         if (isResponseFromSystemDialer && callScreeningResponse != null
1240                 && callScreeningResponse.getCallComposerAttachmentsToShow() >= 0) {
1241             isAllowedToDisplayPicture = (callScreeningResponse.getCallComposerAttachmentsToShow()
1242                     & CallScreeningService.CallResponse.CALL_COMPOSER_ATTACHMENT_PICTURE) != 0;
1243         } else {
1244             isAllowedToDisplayPicture = callFilteringCompletionInfo.isInContacts();
1245         }
1246 
1247         if (isImsConnection()) {
1248             ImsPhone imsPhone = (getPhone() instanceof ImsPhone) ? (ImsPhone) getPhone() : null;
1249             if (imsPhone != null
1250                     && imsPhone.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON
1251                     && !callFilteringCompletionInfo.isBlocked() && isAllowedToDisplayPicture) {
1252                 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
1253                 ImsCallProfile profile = originalConnection.getImsCall().getCallProfile();
1254                 String serverUrl = CallComposerPictureManager.sTestMode
1255                         ? CallComposerPictureManager.FAKE_SERVER_URL
1256                         : profile.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL);
1257                 if (profile != null
1258                         && !TextUtils.isEmpty(serverUrl)) {
1259                     CallComposerPictureManager manager = CallComposerPictureManager
1260                             .getInstance(getPhone().getContext(), getPhone().getSubId());
1261                     manager.handleDownloadFromServer(new CallComposerPictureTransfer.Factory() {},
1262                             serverUrl,
1263                             (result) -> {
1264                                 if (result.first != null) {
1265                                     Bundle newExtras = new Bundle();
1266                                     newExtras.putParcelable(TelecomManager.EXTRA_PICTURE_URI,
1267                                             result.first);
1268                                     putTelephonyExtras(newExtras);
1269                                 } else {
1270                                     Log.i(this, "Call composer picture download:"
1271                                             + " error=" + result.second);
1272                                     Bundle newExtras = new Bundle();
1273                                     newExtras.putBoolean(TelecomManager.EXTRA_HAS_PICTURE, false);
1274                                     putTelephonyExtras(newExtras);
1275                                 }
1276                             });
1277                 }
1278             }
1279         }
1280     }
1281 
1282     @Override
handleRttUpgradeResponse(RttTextStream textStream)1283     public void handleRttUpgradeResponse(RttTextStream textStream) {
1284         if (!isImsConnection()) {
1285             Log.w(this, "handleRttUpgradeResponse - not in IMS, so RTT cannot be enabled.");
1286             return;
1287         }
1288         ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
1289         originalConnection.sendRttModifyResponse(textStream);
1290     }
1291 
answeringDropsFgCalls()1292     private boolean answeringDropsFgCalls() {
1293         if (Flags.callExtraForNonHoldSupportedCarriers()) {
1294             Bundle extras = getExtras();
1295             if (extras != null) {
1296                 return extras.getBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
1297             }
1298         }
1299         return false;
1300     }
1301 
performAnswer(int videoState)1302     public void performAnswer(int videoState) {
1303         Log.v(this, "performAnswer");
1304         if (isValidRingingCall() && getPhone() != null) {
1305             try {
1306                 mTelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
1307                         getPhoneAccountHandle(), answeringDropsFgCalls());
1308                 getPhone().acceptCall(videoState);
1309             } catch (CallStateException e) {
1310                 Log.e(this, e, "Failed to accept call.");
1311             }
1312         }
1313     }
1314 
performHold()1315     public void performHold() {
1316         Log.v(this, "performHold");
1317         // TODO: Can dialing calls be put on hold as well since they take up the
1318         // foreground call slot?
1319         if (Call.State.ACTIVE == mConnectionState) {
1320             Log.v(this, "Holding active call");
1321             try {
1322                 Phone phone = mOriginalConnection.getCall().getPhone();
1323 
1324                 Call ringingCall = phone.getRingingCall();
1325 
1326                 // Although the method says switchHoldingAndActive, it eventually calls a RIL method
1327                 // called switchWaitingOrHoldingAndActive. What this means is that if we try to put
1328                 // a call on hold while a call-waiting call exists, it'll end up accepting the
1329                 // call-waiting call, which is bad if that was not the user's intention. We are
1330                 // cheating here and simply skipping it because we know any attempt to hold a call
1331                 // while a call-waiting call is happening is likely a request from Telecom prior to
1332                 // accepting the call-waiting call.
1333                 // TODO: Investigate a better solution. It would be great here if we
1334                 // could "fake" hold by silencing the audio and microphone streams for this call
1335                 // instead of actually putting it on hold.
1336                 if (ringingCall.getState() != Call.State.WAITING) {
1337                     // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
1338                     if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
1339                         ImsPhone imsPhone = (ImsPhone) phone;
1340                         imsPhone.holdActiveCall();
1341                         mTelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
1342                                 getPhoneAccountHandle());
1343                         return;
1344                     }
1345                     phone.switchHoldingAndActive();
1346                 }
1347 
1348                 // TODO: Cdma calls are slightly different.
1349             } catch (CallStateException e) {
1350                 Log.e(this, e, "Exception occurred while trying to put call on hold.");
1351             }
1352         } else {
1353             Log.w(this, "Cannot put a call that is not currently active on hold.");
1354         }
1355     }
1356 
performUnhold()1357     public void performUnhold() {
1358         Log.v(this, "performUnhold");
1359         if (Call.State.HOLDING == mConnectionState) {
1360             try {
1361                 Phone phone = mOriginalConnection.getCall().getPhone();
1362                 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
1363                 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
1364                     ImsPhone imsPhone = (ImsPhone) phone;
1365                     imsPhone.unholdHeldCall();
1366                     return;
1367                 }
1368                 // Here's the deal--Telephony hold/unhold is weird because whenever there exists
1369                 // more than one call, one of them must always be active. In other words, if you
1370                 // have an active call and holding call, and you put the active call on hold, it
1371                 // will automatically activate the holding call. This is weird with how Telecom
1372                 // sends its commands. When a user opts to "unhold" a background call, telecom
1373                 // issues hold commands to all active calls, and then the unhold command to the
1374                 // background call. This means that we get two commands...each of which reduces to
1375                 // switchHoldingAndActive(). The result is that they simply cancel each other out.
1376                 // To fix this so that it works well with telecom we add a minor hack. If we
1377                 // have one telephony call, everything works as normally expected. But if we have
1378                 // two or more calls, we will ignore all requests to "unhold" knowing that the hold
1379                 // requests already do what we want. If you've read up to this point, I'm very sorry
1380                 // that we are doing this. I didn't think of a better solution that wouldn't also
1381                 // make the Telecom APIs very ugly.
1382 
1383                 if (!hasMultipleTopLevelCalls()) {
1384                     mOriginalConnection.getCall().getPhone().switchHoldingAndActive();
1385                 } else {
1386                     Log.i(this, "Skipping unhold command for %s", this);
1387                 }
1388             } catch (CallStateException e) {
1389                 Log.e(this, e, "Exception occurred while trying to release call from hold.");
1390             }
1391         } else {
1392             Log.w(this, "Cannot release a call that is not already on hold from hold.");
1393         }
1394     }
1395 
performConference(Connection otherConnection)1396     public void performConference(Connection otherConnection) {
1397         Log.d(this, "performConference - %s", this);
1398         if (getPhone() != null) {
1399             try {
1400                 // We dont use the "other" connection because there is no concept of that in the
1401                 // implementation of calls inside telephony. Basically, you can "conference" and it
1402                 // will conference with the background call.  We know that otherConnection is the
1403                 // background call because it would never have called setConferenceableConnections()
1404                 // otherwise.
1405                 getPhone().conference();
1406             } catch (CallStateException e) {
1407                 Log.e(this, e, "Failed to conference call.");
1408             }
1409         }
1410     }
1411 
getAddConferenceParticipants(List<Uri> participants)1412     private String[] getAddConferenceParticipants(List<Uri> participants) {
1413         String[] addConfParticipants = new String[participants.size()];
1414         int i = 0;
1415         for (Uri participant : participants) {
1416            addConfParticipants[i] = participant.getSchemeSpecificPart();
1417            i++;
1418         }
1419         return addConfParticipants;
1420     }
1421 
performAddConferenceParticipants(List<Uri> participants)1422     public void performAddConferenceParticipants(List<Uri> participants) {
1423         Log.v(this, "performAddConferenceParticipants");
1424         if (mOriginalConnection.getCall() instanceof ImsPhoneCall) {
1425             ImsPhoneCall imsPhoneCall = (ImsPhoneCall)mOriginalConnection.getCall();
1426             try {
1427                 imsPhoneCall.getImsCall().inviteParticipants(
1428                         getAddConferenceParticipants(participants));
1429             } catch(ImsException e) {
1430                 Log.e(this, e, "failed to add conference participants");
1431             }
1432         }
1433     }
1434 
1435     /**
1436      * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
1437      * capabilities.
1438      */
buildConnectionCapabilities()1439     protected int buildConnectionCapabilities() {
1440         int callCapabilities = 0;
1441         if (mOriginalConnection != null && mOriginalConnection.isIncoming()) {
1442             callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO;
1443         }
1444         if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) {
1445             callCapabilities |= CAPABILITY_SUPPORT_HOLD;
1446             if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) {
1447                 callCapabilities |= CAPABILITY_HOLD;
1448             }
1449         }
1450 
1451         Log.d(this, "buildConnectionCapabilities: isHoldable = "
1452                 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities);
1453 
1454         return callCapabilities;
1455     }
1456 
updateConnectionCapabilities()1457     protected final void updateConnectionCapabilities() {
1458         int newCapabilities = buildConnectionCapabilities();
1459 
1460         newCapabilities = applyOriginalConnectionCapabilities(newCapabilities);
1461         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO,
1462                 mIsVideoPauseSupported && isVideoCapable());
1463         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
1464                 isExternalConnection() && isPullable());
1465         newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
1466         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
1467                 isImsConnection() && canDeflectImsCalls());
1468 
1469         newCapabilities = applyAddParticipantCapabilities(newCapabilities);
1470         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER_CONSULTATIVE,
1471                 isImsConnection() && canConsultativeTransfer());
1472         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER,
1473                 isImsConnection() && canTransferToNumber());
1474 
1475         if (getConnectionCapabilities() != newCapabilities) {
1476             setConnectionCapabilities(newCapabilities);
1477             notifyConnectionCapabilitiesChanged(newCapabilities);
1478         }
1479     }
1480 
buildConnectionProperties()1481     protected int buildConnectionProperties() {
1482         int connectionProperties = 0;
1483 
1484         // If the phone is in ECM mode, mark the call to indicate that the callback number should be
1485         // shown.
1486         Phone phone = getPhone();
1487         if (phone != null && phone.isInEcm()) {
1488             connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE;
1489         }
1490 
1491         return connectionProperties;
1492     }
1493 
1494     /**
1495      * Updates the properties of the connection.
1496      */
updateConnectionProperties()1497     protected final void updateConnectionProperties() {
1498         int newProperties = buildConnectionProperties();
1499 
1500         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
1501                 hasHighDefAudioProperty());
1502         newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi() && !isCrossSimCall());
1503         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
1504                 isExternalConnection());
1505         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
1506                 mIsCdmaVoicePrivacyEnabled);
1507         newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING,
1508                 mIsUsingAssistedDialing);
1509         newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
1510         newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
1511                 isNetworkIdentifiedEmergencyCall());
1512         newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE,
1513                 isAdhocConferenceCall());
1514         newProperties = changeBitmask(newProperties, PROPERTY_CROSS_SIM,
1515                 isCrossSimCall());
1516 
1517         if (getConnectionProperties() != newProperties) {
1518             setTelephonyConnectionProperties(newProperties);
1519         }
1520     }
1521 
setTelephonyConnectionProperties(int newProperties)1522     public void setTelephonyConnectionProperties(int newProperties) {
1523         setConnectionProperties(newProperties);
1524         notifyConnectionPropertiesChanged(newProperties);
1525     }
1526 
updateAddress()1527     protected final void updateAddress() {
1528         updateConnectionCapabilities();
1529         updateConnectionProperties();
1530         if (mOriginalConnection != null) {
1531             Uri address;
1532             if (isShowingOriginalDialString()
1533                     && mOriginalConnection.getOrigDialString() != null) {
1534                 address = getAddressFromNumber(mOriginalConnection.getOrigDialString());
1535             } else if (isNeededToFormatIncomingNumberForJp()) {
1536                 address = getAddressFromNumber(
1537                         formatIncomingNumberForJp(mOriginalConnection.getAddress()));
1538             } else {
1539                 address = getAddressFromNumber(mOriginalConnection.getAddress());
1540             }
1541             int presentation = mOriginalConnection.getNumberPresentation();
1542             if (!Objects.equals(address, getAddress()) ||
1543                     presentation != getAddressPresentation()) {
1544                 Log.v(this, "updateAddress, address changed");
1545                 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
1546                     address = null;
1547                 }
1548                 setAddress(address, presentation);
1549             }
1550 
1551             String name = filterCnapName(mOriginalConnection.getCnapName());
1552             int namePresentation = mOriginalConnection.getCnapNamePresentation();
1553             if (!Objects.equals(name, getCallerDisplayName()) ||
1554                     namePresentation != getCallerDisplayNamePresentation()) {
1555                 Log.v(this, "updateAddress, caller display name changed");
1556                 setCallerDisplayName(name, namePresentation);
1557             }
1558 
1559             TelephonyManager tm = (TelephonyManager) getPhone().getContext()
1560                     .getSystemService(Context.TELEPHONY_SERVICE);
1561             if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
1562                 mTreatAsEmergencyCall = true;
1563             }
1564 
1565             // Changing the address of the connection can change whether it is an emergency call or
1566             // not, which can impact whether it can be part of a conference.
1567             refreshConferenceSupported();
1568         }
1569     }
1570 
onRemovedFromCallService()1571     void onRemovedFromCallService() {
1572         // Subclass can override this to do cleanup.
1573     }
1574 
registerForCallEvents(Phone phone)1575     public void registerForCallEvents(Phone phone) {
1576         if (mPhoneForEvents == phone) {
1577             Log.i(this, "registerForCallEvents - same phone requested for"
1578                     + "registration, ignoring.");
1579             return;
1580         }
1581         Log.i(this, "registerForCallEvents; phone=%s", phone);
1582         // Only one Phone should be registered for events at a time.
1583         unregisterForCallEvents();
1584         phone.registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
1585         phone.registerForHandoverStateChanged(mHandler, MSG_HANDOVER_STATE_CHANGED, null);
1586         phone.registerForRedialConnectionChanged(mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
1587         phone.registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
1588         phone.registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
1589         phone.registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
1590         phone.registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
1591         phone.registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null);
1592         mPhoneForEvents = phone;
1593     }
1594 
setOriginalConnection(com.android.internal.telephony.Connection originalConnection)1595     void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
1596         Log.i(this, "setOriginalConnection: TelephonyConnection, originalConnection: "
1597                 + originalConnection);
1598         if (mOriginalConnection != null && originalConnection != null
1599                && !originalConnection.isIncoming()
1600                && originalConnection.getOrigDialString() == null
1601                && isShowingOriginalDialString()) {
1602             Log.i(this, "new original dial string is null, convert to: "
1603                    +  mOriginalConnection.getOrigDialString());
1604             originalConnection.restoreDialedNumberAfterConversion(
1605                     mOriginalConnection.getOrigDialString());
1606         }
1607 
1608         clearOriginalConnection();
1609         mOriginalConnectionExtras.clear();
1610         mOriginalConnection = originalConnection;
1611         mOriginalConnection.setTelecomCallId(getTelecomCallId());
1612         registerForCallEvents(getPhone());
1613 
1614         mOriginalConnection.addPostDialListener(mPostDialListener);
1615         mOriginalConnection.addListener(mOriginalConnectionListener);
1616 
1617         // Set video state and capabilities
1618         setTelephonyVideoState(mOriginalConnection.getVideoState());
1619         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
1620         setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
1621         setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference());
1622         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
1623         setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
1624         setAudioQuality(mOriginalConnection.getAudioQuality());
1625         setTechnologyTypeExtra();
1626 
1627         setCallRadioTech(mOriginalConnection.getCallRadioTech());
1628 
1629         // Post update of extras to the handler; extras are updated via the handler to ensure thread
1630         // safety. The Extras Bundle is cloned in case the original extras are modified while they
1631         // are being added to mOriginalConnectionExtras in updateExtras.
1632         Bundle connExtras = mOriginalConnection.getConnectionExtras();
1633             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null :
1634                     new Bundle(connExtras)).sendToTarget();
1635 
1636         TelephonyManager tm = (TelephonyManager) getPhone().getContext()
1637                 .getSystemService(Context.TELEPHONY_SERVICE);
1638         if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
1639             mTreatAsEmergencyCall = true;
1640         }
1641         // Propagate VERSTAT for IMS calls.
1642         setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus());
1643 
1644         if (isImsConnection()) {
1645             mWasImsConnection = true;
1646         }
1647         if (originalConnection instanceof ImsPhoneConnection) {
1648             maybeConfigureDeviceToDeviceCommunication();
1649         }
1650         mIsMultiParty = mOriginalConnection.isMultiparty();
1651 
1652         Bundle extrasToPut = new Bundle();
1653         // Also stash the number verification status in a hidden extra key in the connection.
1654         // We do this because a RemoteConnection DOES NOT include a getNumberVerificationStatus
1655         // method and we need to be able to pass the number verification status up to Telecom
1656         // despite the missing pathway in the RemoteConnectionService API surface.
1657         extrasToPut.putInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS,
1658                 mOriginalConnection.getNumberVerificationStatus());
1659         List<String> extrasToRemove = new ArrayList<>();
1660         if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
1661             extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
1662         } else {
1663             extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
1664         }
1665 
1666         if (shouldSetDisableAddCallExtra()) {
1667             extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
1668         } else {
1669             extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
1670         }
1671 
1672         if (mOriginalConnection != null) {
1673             ArrayList<String> forwardedNumber = mOriginalConnection.getForwardedNumber();
1674             if (forwardedNumber != null) {
1675                 extrasToPut.putStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER,
1676                         forwardedNumber);
1677             }
1678         }
1679 
1680         putTelephonyExtras(extrasToPut);
1681         removeTelephonyExtras(extrasToRemove);
1682 
1683         // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
1684         // should be executed *after* the above setters have run.
1685         updateState();
1686         if (mOriginalConnection == null) {
1687             Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " +
1688                     originalConnection);
1689         }
1690 
1691         fireOnOriginalConnectionConfigured();
1692     }
1693 
1694     /**
1695      * Filters the CNAP name to not include a list of names that are unhelpful to the user for
1696      * Caller ID purposes.
1697      */
filterCnapName(final String cnapName)1698     private String filterCnapName(final String cnapName) {
1699         if (cnapName == null) {
1700             return null;
1701         }
1702         PersistableBundle carrierConfig = getCarrierConfig();
1703         String[] filteredCnapNames = null;
1704         if (carrierConfig != null) {
1705             filteredCnapNames = carrierConfig.getStringArray(
1706                     CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY);
1707         }
1708         if (filteredCnapNames != null) {
1709             long cnapNameMatches = Arrays.asList(filteredCnapNames)
1710                     .stream()
1711                     .filter(filteredCnapName -> filteredCnapName.equals(
1712                             cnapName.toUpperCase(Locale.ROOT)))
1713                     .count();
1714             if (cnapNameMatches > 0) {
1715                 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName);
1716                 return "";
1717             }
1718         }
1719         return cnapName;
1720     }
1721 
1722     /**
1723      * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom.
1724      */
setTechnologyTypeExtra()1725     private void setTechnologyTypeExtra() {
1726         if (getPhone() != null) {
1727             Bundle newExtras = getExtras();
1728             if (newExtras == null) {
1729                 newExtras = new Bundle();
1730             }
1731             newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
1732             putTelephonyExtras(newExtras);
1733         }
1734     }
1735 
refreshHoldSupported()1736     private void refreshHoldSupported() {
1737        if (mOriginalConnection == null) {
1738            Log.w(this, "refreshHoldSupported org conn is null");
1739            return;
1740        }
1741 
1742        if (!mOriginalConnection.shouldAllowHoldingVideoCall() && canHoldImsCalls() !=
1743                ((getConnectionCapabilities() & (CAPABILITY_HOLD | CAPABILITY_SUPPORT_HOLD)) != 0)) {
1744            updateConnectionCapabilities();
1745        }
1746     }
1747 
refreshDisableAddCall()1748     private void refreshDisableAddCall() {
1749         if (shouldSetDisableAddCallExtra()) {
1750             Bundle newExtras = getExtras();
1751             if (newExtras == null) {
1752                 newExtras = new Bundle();
1753             }
1754             newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
1755             putTelephonyExtras(newExtras);
1756         } else {
1757             removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
1758         }
1759     }
1760 
refreshCodec()1761     private void refreshCodec() {
1762         boolean changed = false;
1763         Bundle newExtras = getExtras();
1764         if (newExtras == null) {
1765             newExtras = new Bundle();
1766         }
1767         int newCodecType;
1768         if (isImsConnection()) {
1769             newCodecType = transformCodec(getOriginalConnection().getAudioCodec());
1770         } else {
1771             // For SRVCC, report AUDIO_CODEC_NONE.
1772             newCodecType = Connection.AUDIO_CODEC_NONE;
1773         }
1774         int oldCodecType = newExtras.getInt(Connection.EXTRA_AUDIO_CODEC,
1775                 Connection.AUDIO_CODEC_NONE);
1776         if (newCodecType != oldCodecType) {
1777             newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
1778             Log.i(this, "refreshCodec: codec changed; old=%d, new=%d", oldCodecType, newCodecType);
1779             changed = true;
1780         }
1781         if (isImsConnection()) {
1782             float newBitrate = getOriginalConnection().getAudioCodecBitrateKbps();
1783             float oldBitrate = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f);
1784             if (Math.abs(newBitrate - oldBitrate) > THRESHOLD) {
1785                 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, newBitrate);
1786                 Log.i(this, "refreshCodec: bitrate changed; old=%f, new=%f", oldBitrate,
1787                         newBitrate);
1788                 changed = true;
1789             }
1790 
1791             float newBandwidth = getOriginalConnection().getAudioCodecBandwidthKhz();
1792             float oldBandwidth = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ,
1793                     0.0f);
1794             if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) {
1795                 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth);
1796                 Log.i(this, "refreshCodec: bandwidth changed; old=%f, new=%f", oldBandwidth,
1797                         newBandwidth);
1798                 changed = true;
1799             }
1800         } else {
1801             ArrayList<String> toRemove = new ArrayList<>();
1802             toRemove.add(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS);
1803             toRemove.add(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ);
1804             removeTelephonyExtras(toRemove);
1805         }
1806 
1807         if (changed) {
1808             Log.i(this, "refreshCodec: Codec:"
1809                     + newExtras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE)
1810                     + ", Bitrate:"
1811                     + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f)
1812                     + ", Bandwidth:"
1813                     + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, 0.0f));
1814             putTelephonyExtras(newExtras);
1815         }
1816     }
1817 
transformCodec(int codec)1818     private int transformCodec(int codec) {
1819         switch (codec) {
1820             case ImsStreamMediaProfile.AUDIO_QUALITY_NONE:
1821                 return Connection.AUDIO_CODEC_NONE;
1822             case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
1823                 return Connection.AUDIO_CODEC_AMR;
1824             case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB:
1825                 return Connection.AUDIO_CODEC_AMR_WB;
1826             case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K:
1827                 return Connection.AUDIO_CODEC_QCELP13K;
1828             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC:
1829                 return Connection.AUDIO_CODEC_EVRC;
1830             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B:
1831                 return Connection.AUDIO_CODEC_EVRC_B;
1832             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB:
1833                 return Connection.AUDIO_CODEC_EVRC_WB;
1834             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW:
1835                 return Connection.AUDIO_CODEC_EVRC_NW;
1836             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR:
1837                 return Connection.AUDIO_CODEC_GSM_EFR;
1838             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR:
1839                 return Connection.AUDIO_CODEC_GSM_FR;
1840             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR:
1841                 return Connection.AUDIO_CODEC_GSM_HR;
1842             case ImsStreamMediaProfile.AUDIO_QUALITY_G711U:
1843                 return Connection.AUDIO_CODEC_G711U;
1844             case ImsStreamMediaProfile.AUDIO_QUALITY_G723:
1845                 return Connection.AUDIO_CODEC_G723;
1846             case ImsStreamMediaProfile.AUDIO_QUALITY_G711A:
1847                 return Connection.AUDIO_CODEC_G711A;
1848             case ImsStreamMediaProfile.AUDIO_QUALITY_G722:
1849                 return Connection.AUDIO_CODEC_G722;
1850             case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB:
1851                 return Connection.AUDIO_CODEC_G711AB;
1852             case ImsStreamMediaProfile.AUDIO_QUALITY_G729:
1853                 return Connection.AUDIO_CODEC_G729;
1854             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB:
1855                 return Connection.AUDIO_CODEC_EVS_NB;
1856             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB:
1857                 return Connection.AUDIO_CODEC_EVS_WB;
1858             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB:
1859                 return Connection.AUDIO_CODEC_EVS_SWB;
1860             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB:
1861                 return Connection.AUDIO_CODEC_EVS_FB;
1862             default:
1863                 return Connection.AUDIO_CODEC_NONE;
1864         }
1865     }
1866 
shouldSetDisableAddCallExtra()1867     private boolean shouldSetDisableAddCallExtra() {
1868         if (mOriginalConnection == null) {
1869             return false;
1870         }
1871         boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall();
1872         if (carrierShouldAllowAddCall) {
1873             return false;
1874         }
1875         Phone phone = getPhone();
1876         if (phone == null) {
1877             return false;
1878         }
1879         boolean isCurrentVideoCall = false;
1880         boolean wasVideoCall = false;
1881         boolean isVowifiEnabled = false;
1882         if (phone instanceof ImsPhone) {
1883             ImsPhoneCall foregroundCall = ((ImsPhone) phone).getForegroundCall();
1884             if (foregroundCall != null) {
1885                 ImsCall call = foregroundCall.getImsCall();
1886                 if (call != null) {
1887                     isCurrentVideoCall = call.isVideoCall();
1888                     wasVideoCall = call.wasVideoCall();
1889                 }
1890             }
1891 
1892             isVowifiEnabled = isWfcEnabled(phone);
1893         }
1894 
1895         if (isCurrentVideoCall) {
1896             return true;
1897         } else if (wasVideoCall && isWifi() && !isVowifiEnabled) {
1898             return true;
1899         }
1900         return false;
1901     }
1902 
hasHighDefAudioProperty()1903     private boolean hasHighDefAudioProperty() {
1904         if (!mHasHighDefAudio) {
1905             return false;
1906         }
1907 
1908         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
1909 
1910         PersistableBundle b = getCarrierConfig();
1911         boolean canWifiCallsBeHdAudio =
1912                 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO);
1913         boolean canVideoCallsBeHdAudio =
1914                 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO);
1915         boolean canGsmCdmaCallsBeHdAudio =
1916                 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO);
1917         boolean shouldDisplayHdAudio =
1918                 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL);
1919 
1920         if (!shouldDisplayHdAudio) {
1921             return false;
1922         }
1923 
1924         if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) {
1925             return false;
1926         }
1927 
1928         if (isVideoCall && !canVideoCallsBeHdAudio) {
1929             return false;
1930         }
1931 
1932         if (isWifi() && !canWifiCallsBeHdAudio) {
1933             return false;
1934         }
1935 
1936         return true;
1937     }
1938 
1939     /**
1940      * @return The address's to which this Connection is currently communicating.
1941      */
getParticipants()1942     public final @Nullable List<Uri> getParticipants() {
1943         return mParticipants;
1944     }
1945 
1946     /**
1947      * Sets the value of the {@link #getParticipants()} property.
1948      *
1949      * @param address The participant address's.
1950      */
setParticipants(@ullable List<Uri> address)1951     public final void setParticipants(@Nullable List<Uri> address) {
1952         mParticipants = address;
1953     }
1954 
1955     /**
1956      * @return true if connection is adhocConference call else false.
1957      */
isAdhocConferenceCall()1958     public final boolean isAdhocConferenceCall() {
1959         return mIsAdhocConferenceCall;
1960     }
1961 
1962     /**
1963      * Sets the value of the {@link #isAdhocConferenceCall()} property.
1964      *
1965      * @param isAdhocConferenceCall represents if the call is adhoc conference call or not.
1966      */
setIsAdhocConferenceCall(boolean isAdhocConferenceCall)1967     public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) {
1968         mIsAdhocConferenceCall = isAdhocConferenceCall;
1969         updateConnectionProperties();
1970     }
1971 
canHoldImsCalls()1972     private boolean canHoldImsCalls() {
1973         PersistableBundle b = getCarrierConfig();
1974         // Return true if the CarrierConfig is unavailable
1975         return (!doesDeviceRespectHoldCarrierConfig() || b == null ||
1976                 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL)) &&
1977                 ((mOriginalConnection != null && mOriginalConnection.shouldAllowHoldingVideoCall())
1978                 || !VideoProfile.isVideo(getVideoState()));
1979     }
1980 
isConferenceHosted()1981     private boolean isConferenceHosted() {
1982         boolean isHosted = false;
1983         if (getTelephonyConnectionService() != null) {
1984             for (Conference current : getTelephonyConnectionService().getAllConferences()) {
1985                 if (current instanceof ImsConference) {
1986                     ImsConference other = (ImsConference) current;
1987                     if (getState() == current.getState()) {
1988                         continue;
1989                     }
1990                     if (other.isConferenceHost()) {
1991                         isHosted = true;
1992                         break;
1993                     }
1994                 }
1995             }
1996         }
1997         return isHosted;
1998     }
1999 
isAddParticipantCapable()2000     private boolean isAddParticipantCapable() {
2001         // not add participant capable for non ims phones
2002         if (getPhone() == null || getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
2003             return false;
2004         }
2005 
2006         if (!getCarrierConfig()
2007                 .getBoolean(CarrierConfigManager.KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL)) {
2008             return false;
2009         }
2010 
2011         boolean isCapable = !mTreatAsEmergencyCall && (mConnectionState == Call.State.ACTIVE ||
2012                 mConnectionState == Call.State.HOLDING);
2013 
2014         // add participant capable if current connection is a host connection or
2015         // if conference is not hosted on the device
2016         isCapable = isCapable && ((mOriginalConnection != null &&
2017                 mOriginalConnection.isConferenceHost()) ||
2018                 !isConferenceHosted());
2019 
2020         /**
2021           * For individual IMS calls, if the extra for remote conference support is
2022           *     - indicated, then consider the same for add participant capability
2023           *     - not indicated, then the add participant capability is same as before.
2024           */
2025         if (isCapable && (mOriginalConnection != null) && !mIsMultiParty) {
2026             // In case OEMs are still using deprecated value, read it and use it as default value.
2027             boolean isCapableFromDeprecatedExtra = mOriginalConnectionExtras.getBoolean(
2028                     ImsCallProfile.EXTRA_CONFERENCE_AVAIL, isCapable);
2029             isCapable = mOriginalConnectionExtras.getBoolean(
2030                     ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED,
2031                     isCapableFromDeprecatedExtra);
2032         }
2033         return isCapable;
2034     }
2035 
2036     /**
2037      * Applies the add participant capabilities to the {@code CallCapabilities} bit-mask.
2038      *
2039      * @param callCapabilities The {@code CallCapabilities} bit-mask.
2040      * @return The capabilities with the add participant capabilities applied.
2041      */
applyAddParticipantCapabilities(int callCapabilities)2042     private int applyAddParticipantCapabilities(int callCapabilities) {
2043         int currentCapabilities = callCapabilities;
2044         if (isAddParticipantCapable()) {
2045             currentCapabilities = changeBitmask(currentCapabilities,
2046                     Connection.CAPABILITY_ADD_PARTICIPANT, true);
2047         } else {
2048             currentCapabilities = changeBitmask(currentCapabilities,
2049                     Connection.CAPABILITY_ADD_PARTICIPANT, false);
2050         }
2051         return currentCapabilities;
2052     }
2053 
2054     @VisibleForTesting
getCarrierConfig()2055     public @NonNull PersistableBundle getCarrierConfig() {
2056         Phone phone = getPhone();
2057         if (phone == null) {
2058             Log.w(this,
2059                     "getCarrierConfig: phone is null. Returning CarrierConfigManager"
2060                             + ".getDefaultConfig()");
2061             return CarrierConfigManager.getDefaultConfig();
2062         }
2063 
2064         // potential null returned from .getCarrierConfigForSubId() and method guarantees non-null.
2065         // hence, need for try/finally block
2066         PersistableBundle pb = null;
2067         try {
2068             pb = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
2069         } catch (Exception e) {
2070             Log.e(this, e,
2071                     "getCarrierConfig: caught Exception when calling "
2072                             + "PhoneGlobals.getCarrierConfigForSubId(phone.getSubId()). Returning "
2073                             + "CarrierConfigManager.getDefaultConfig()");
2074         } finally {
2075             if (pb == null) {
2076                 pb = CarrierConfigManager.getDefaultConfig();
2077             }
2078         }
2079         return pb;
2080     }
2081 
2082     @VisibleForTesting
isRttMergeSupported(@onNull PersistableBundle pb)2083     public boolean isRttMergeSupported(@NonNull PersistableBundle pb) {
2084         return pb.getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
2085     }
2086 
canDeflectImsCalls()2087     private boolean canDeflectImsCalls() {
2088         return getCarrierConfig().getBoolean(
2089                 CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL)
2090                 && isValidRingingCall();
2091     }
2092 
isCallTransferSupported()2093     private boolean isCallTransferSupported() {
2094         return getCarrierConfig().getBoolean(
2095                 CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
2096     }
2097 
canTransfer(TelephonyConnection c)2098     private boolean canTransfer(TelephonyConnection c) {
2099         com.android.internal.telephony.Connection connection = c.getOriginalConnection();
2100         return (connection != null && !connection.isMultiparty()
2101                 && (c.getState() == STATE_ACTIVE || c.getState() == STATE_HOLDING));
2102     }
2103 
canTransferToNumber()2104     private boolean canTransferToNumber() {
2105         if (!isCallTransferSupported()) {
2106             return false;
2107         }
2108         return canTransfer(this);
2109     }
2110 
canConsultativeTransfer()2111     private boolean canConsultativeTransfer() {
2112         if (!isCallTransferSupported()) {
2113             return false;
2114         }
2115         if (!canTransfer(this)) {
2116             return false;
2117         }
2118         boolean canConsultativeTransfer = false;
2119         if (getTelephonyConnectionService() != null) {
2120             for (Connection current : getTelephonyConnectionService().getAllConnections()) {
2121                 if (current != this && current instanceof TelephonyConnection) {
2122                     TelephonyConnection other = (TelephonyConnection) current;
2123                     if (canTransfer(other)) {
2124                         canConsultativeTransfer = true;
2125                         break;
2126                     }
2127                 }
2128             }
2129         }
2130         return canConsultativeTransfer;
2131     }
2132 
2133     /**
2134      * Determines if the device will respect the value of the
2135      * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
2136      *
2137      * @return {@code false} if the device always supports holding IMS calls, {@code true} if it
2138      *      will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if
2139      *      hold is supported.
2140      */
doesDeviceRespectHoldCarrierConfig()2141     private boolean doesDeviceRespectHoldCarrierConfig() {
2142         Phone phone = getPhone();
2143         if (phone == null) {
2144             return true;
2145         }
2146         return phone.getContext().getResources().getBoolean(
2147                 com.android.internal.R.bool.config_device_respects_hold_carrier_config);
2148     }
2149 
2150     /**
2151      * Whether the connection should be treated as an emergency.
2152      * @return {@code true} if the connection should be treated as an emergency call based
2153      * on the number dialed, {@code false} otherwise.
2154      */
shouldTreatAsEmergencyCall()2155     protected boolean shouldTreatAsEmergencyCall() {
2156         return mTreatAsEmergencyCall;
2157     }
2158 
2159     /**
2160      * Sets whether to treat this call as an emergency call or not.
2161      * @param shouldTreatAsEmergencyCall
2162      */
2163     @VisibleForTesting
setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall)2164     public void setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall) {
2165         mTreatAsEmergencyCall = shouldTreatAsEmergencyCall;
2166     }
2167 
2168     /**
2169      * Un-sets the underlying radio connection.
2170      */
clearOriginalConnection()2171     void clearOriginalConnection() {
2172         if (mOriginalConnection != null) {
2173             Log.i(this, "clearOriginalConnection; clearing=%s", mOriginalConnection);
2174             unregisterForCallEvents();
2175             mOriginalConnection.removePostDialListener(mPostDialListener);
2176             mOriginalConnection.removeListener(mOriginalConnectionListener);
2177             mOriginalConnection = null;
2178         }
2179     }
2180 
unregisterForCallEvents()2181     public void unregisterForCallEvents() {
2182         if (mPhoneForEvents == null) return;
2183         mPhoneForEvents.unregisterForPreciseCallStateChanged(mHandler);
2184         mPhoneForEvents.unregisterForRingbackTone(mHandler);
2185         mPhoneForEvents.unregisterForHandoverStateChanged(mHandler);
2186         mPhoneForEvents.unregisterForRedialConnectionChanged(mHandler);
2187         mPhoneForEvents.unregisterForDisconnect(mHandler);
2188         mPhoneForEvents.unregisterForSuppServiceNotification(mHandler);
2189         mPhoneForEvents.unregisterForOnHoldTone(mHandler);
2190         mPhoneForEvents.unregisterForInCallVoicePrivacyOn(mHandler);
2191         mPhoneForEvents.unregisterForInCallVoicePrivacyOff(mHandler);
2192         mPhoneForEvents = null;
2193     }
2194 
2195     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
hangup(int telephonyDisconnectCode)2196     public void hangup(int telephonyDisconnectCode) {
2197         if (mOriginalConnection != null) {
2198             mHangupDisconnectCause = telephonyDisconnectCode;
2199             try {
2200                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
2201                 // connection.hangup(). Without this change, the party originating the call
2202                 // will not get sent to voicemail if the user opts to reject the call.
2203                 if (isValidRingingCall()) {
2204                     Call call = getCall();
2205                     if (call != null) {
2206                         call.hangup();
2207                     } else {
2208                         Log.w(this, "Attempting to hangup a connection without backing call.");
2209                     }
2210                 } else {
2211                     // We still prefer to call connection.hangup() for non-ringing calls
2212                     // in order to support hanging-up specific calls within a conference call.
2213                     // If we invoked call.hangup() while in a conference, we would end up
2214                     // hanging up the entire conference call instead of the specific connection.
2215                     mOriginalConnection.hangup();
2216                 }
2217             } catch (CallStateException e) {
2218                 Log.e(this, e, "Call to Connection.hangup failed with exception");
2219             }
2220         } else {
2221             mTelephonyConnectionService.onLocalHangup(this);
2222             if (getState() == STATE_DISCONNECTED) {
2223                 Log.i(this, "hangup called on an already disconnected call!");
2224                 close();
2225             } else {
2226                 // There are a few cases where mOriginalConnection has not been set yet. For
2227                 // example, when the radio has to be turned on to make an emergency call,
2228                 // mOriginalConnection could not be set for many seconds.
2229                 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
2230                         android.telephony.DisconnectCause.LOCAL,
2231                         "Local Disconnect before connection established."));
2232                 close();
2233             }
2234         }
2235     }
2236 
reject(@ndroid.telecom.Call.RejectReason int rejectReason)2237     protected void reject(@android.telecom.Call.RejectReason int rejectReason) {
2238         if (mOriginalConnection != null) {
2239             mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED;
2240             try {
2241                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
2242                 // connection.hangup(). Without this change, the party originating the call
2243                 // will not get sent to voicemail if the user opts to reject the call.
2244                 if (isValidRingingCall()) {
2245                     Call call = getCall();
2246                     if (call != null) {
2247                         call.hangup(rejectReason);
2248                     } else {
2249                         Log.w(this, "Attempting to hangup a connection without backing call.");
2250                     }
2251                 } else {
2252                     // We still prefer to call connection.hangup() for non-ringing calls
2253                     // in order to support hanging-up specific calls within a conference call.
2254                     // If we invoked call.hangup() while in a conference, we would end up
2255                     // hanging up the entire conference call instead of the specific connection.
2256                     mOriginalConnection.hangup();
2257                 }
2258             } catch (CallStateException e) {
2259                 Log.e(this, e, "Call to Connection.hangup failed with exception");
2260             }
2261         } else {
2262             if (getState() == STATE_DISCONNECTED) {
2263                 Log.i(this, "hangup called on an already disconnected call!");
2264                 close();
2265             } else {
2266                 // There are a few cases where mOriginalConnection has not been set yet. For
2267                 // example, when the radio has to be turned on to make an emergency call,
2268                 // mOriginalConnection could not be set for many seconds.
2269                 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
2270                         android.telephony.DisconnectCause.LOCAL,
2271                         "Local Disconnect before connection established."));
2272                 close();
2273             }
2274         }
2275     }
2276 
getOriginalConnection()2277     com.android.internal.telephony.Connection getOriginalConnection() {
2278         return mOriginalConnection;
2279     }
2280 
getCall()2281     protected Call getCall() {
2282         if (mOriginalConnection != null) {
2283             return mOriginalConnection.getCall();
2284         }
2285         return null;
2286     }
2287 
getPhone()2288     Phone getPhone() {
2289         Call call = getCall();
2290         if (call != null) {
2291             return call.getPhone();
2292         }
2293         return null;
2294     }
2295 
hasMultipleTopLevelCalls()2296     private boolean hasMultipleTopLevelCalls() {
2297         int numCalls = 0;
2298         Phone phone = getPhone();
2299         if (phone != null) {
2300             if (!phone.getRingingCall().isIdle()) {
2301                 numCalls++;
2302             }
2303             if (!phone.getForegroundCall().isIdle()) {
2304                 numCalls++;
2305             }
2306             if (!phone.getBackgroundCall().isIdle()) {
2307                 numCalls++;
2308             }
2309         }
2310         return numCalls > 1;
2311     }
2312 
getForegroundConnection()2313     private com.android.internal.telephony.Connection getForegroundConnection() {
2314         if (getPhone() != null) {
2315             return getPhone().getForegroundCall().getEarliestConnection();
2316         }
2317         return null;
2318     }
2319 
2320      /**
2321      * Checks for and returns the list of conference participants
2322      * associated with this connection.
2323      */
getConferenceParticipants()2324     public List<ConferenceParticipant> getConferenceParticipants() {
2325         if (mOriginalConnection == null) {
2326             Log.w(this, "Null mOriginalConnection, cannot get conf participants.");
2327             return null;
2328         }
2329         return mOriginalConnection.getConferenceParticipants();
2330     }
2331 
2332     /**
2333      * Checks to see the original connection corresponds to an active incoming call. Returns false
2334      * if there is no such actual call, or if the associated call is not incoming (See
2335      * {@link Call.State#isRinging}).
2336      */
isValidRingingCall()2337     private boolean isValidRingingCall() {
2338         if (getPhone() == null) {
2339             Log.v(this, "isValidRingingCall, phone is null");
2340             return false;
2341         }
2342 
2343         Call ringingCall = getPhone().getRingingCall();
2344         if (!ringingCall.getState().isRinging()) {
2345             Log.v(this, "isValidRingingCall, ringing call is not in ringing state");
2346             return false;
2347         }
2348 
2349         if (ringingCall.getEarliestConnection() != mOriginalConnection) {
2350             Log.v(this, "isValidRingingCall, ringing call connection does not match");
2351             return false;
2352         }
2353 
2354         Log.v(this, "isValidRingingCall, returning true");
2355         return true;
2356     }
2357 
2358     // Make sure the extras being passed into this method is a COPY of the original extras Bundle.
2359     // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll
2360     // below.
updateExtras(Bundle extras)2361     protected void updateExtras(Bundle extras) {
2362         if (mOriginalConnection != null) {
2363             if (extras != null) {
2364                 // Check if extras have changed and need updating.
2365                 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) {
2366                     if (Log.DEBUG) {
2367                         Log.d(TelephonyConnection.this, "Updating extras:");
2368                         for (String key : extras.keySet()) {
2369                             Object value = extras.get(key);
2370                             if (value instanceof String) {
2371                                 Log.d(this, "updateExtras Key=" + Rlog.pii(LOG_TAG, key)
2372                                         + " value=" + Rlog.pii(LOG_TAG, value));
2373                             }
2374                         }
2375                     }
2376                     mOriginalConnectionExtras.clear();
2377 
2378                     mOriginalConnectionExtras.putAll(extras);
2379 
2380                     // Remap any string extras that have a remapping defined.
2381                     for (String key : mOriginalConnectionExtras.keySet()) {
2382                         if (sExtrasMap.containsKey(key)) {
2383                             String newKey = sExtrasMap.get(key);
2384                             mOriginalConnectionExtras.putString(newKey, extras.getString(key));
2385                             mOriginalConnectionExtras.remove(key);
2386                         }
2387                     }
2388 
2389                     // Ensure extras are propagated to Telecom.
2390                     putTelephonyExtras(mOriginalConnectionExtras);
2391                     // If extras contain Conference support information,
2392                     // then ensure capabilities are updated.
2393                     if (mOriginalConnectionExtras.containsKey(
2394                             ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED)
2395                             || mOriginalConnectionExtras.containsKey(
2396                                 ImsCallProfile.EXTRA_CONFERENCE_AVAIL)) {
2397                         updateConnectionCapabilities();
2398                     }
2399                     // If extras contain or contained Cross Sim information,
2400                     // then ensure connection properties are updated and propagated to Telecom.
2401                     // Also, update the status hints in the case the call has
2402                     // has moved from cross sim call back to wifi
2403                     mWasCrossSim |= mOriginalConnectionExtras.containsKey(
2404                                 ImsCallProfile.EXTRA_IS_CROSS_SIM_CALL);
2405                     if (mWasCrossSim) {
2406                         updateStatusHints();
2407                         updateConnectionProperties();
2408                     }
2409                 } else {
2410                     Log.d(this, "Extras update not required");
2411                 }
2412             } else {
2413                 Log.d(this, "updateExtras extras: " + Rlog.pii(LOG_TAG, extras));
2414             }
2415         }
2416     }
2417 
areBundlesEqual(Bundle extras, Bundle newExtras)2418     private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
2419         if (extras == null || newExtras == null) {
2420             return extras == newExtras;
2421         }
2422 
2423         if (extras.size() != newExtras.size()) {
2424             return false;
2425         }
2426 
2427         for(String key : extras.keySet()) {
2428             if (key != null) {
2429                 final Object value = extras.get(key);
2430                 final Object newValue = newExtras.get(key);
2431                 if (!Objects.equals(value, newValue)) {
2432                     return false;
2433                 }
2434             }
2435         }
2436         return true;
2437     }
2438 
setStateOverride(Call.State state)2439     void setStateOverride(Call.State state) {
2440         mIsStateOverridden = true;
2441         mConnectionOverriddenState = state;
2442         // Need to keep track of the original connection's state before override.
2443         mOriginalConnectionState = mOriginalConnection.getState();
2444         updateStateInternal();
2445     }
2446 
resetStateOverride()2447     void resetStateOverride() {
2448         mIsStateOverridden = false;
2449         updateStateInternal();
2450     }
2451 
updateStateInternal()2452     void updateStateInternal() {
2453         if (mOriginalConnection == null) {
2454             return;
2455         }
2456         Call.State newState;
2457         // If the state is overridden and the state of the original connection hasn't changed since,
2458         // then we continue in the overridden state, else we go to the original connection's state.
2459         if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) {
2460             newState = mConnectionOverriddenState;
2461         } else {
2462             newState = mOriginalConnection.getState();
2463         }
2464         int cause = mOriginalConnection.getDisconnectCause();
2465         Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState,
2466                 getTelecomCallId());
2467 
2468         if (mConnectionState != newState) {
2469             mConnectionState = newState;
2470             switch (newState) {
2471                 case IDLE:
2472                     break;
2473                 case ACTIVE:
2474                     setActiveInternal();
2475                     break;
2476                 case HOLDING:
2477                     setTelephonyConnectionOnHold();
2478                     break;
2479                 case DIALING:
2480                 case ALERTING:
2481                     if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
2482                         setTelephonyConnectionPulling();
2483                     } else {
2484                         setTelephonyConnectionDialing();
2485                     }
2486                     break;
2487                 case INCOMING:
2488                 case WAITING:
2489                     setTelephonyConnectionRinging();
2490                     break;
2491                 case DISCONNECTED:
2492                     if (mTelephonyConnectionService != null) {
2493                         ImsReasonInfo reasonInfo = null;
2494                         if (isImsConnection()) {
2495                             ImsPhoneConnection imsPhoneConnection =
2496                                     (ImsPhoneConnection) mOriginalConnection;
2497                             reasonInfo = imsPhoneConnection.getImsReasonInfo();
2498                             if (reasonInfo != null) {
2499                                 int reasonCode = reasonInfo.getCode();
2500                                 int extraCode = reasonInfo.getExtraCode();
2501                                 if ((reasonCode == CODE_SIP_ALTERNATE_EMERGENCY_CALL)
2502                                         || (reasonCode == CODE_LOCAL_CALL_CS_RETRY_REQUIRED
2503                                                 && extraCode == EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
2504                                     EmergencyNumber numberInfo =
2505                                             imsPhoneConnection.getEmergencyNumberInfo();
2506                                     if (numberInfo != null) {
2507                                         mEmergencyServiceCategory =
2508                                                 numberInfo.getEmergencyServiceCategoryBitmask();
2509                                         mEmergencyUrns = numberInfo.getEmergencyUrns();
2510                                     } else {
2511                                         Log.i(this, "mEmergencyServiceCategory no EmergencyNumber");
2512                                     }
2513 
2514                                     if (mEmergencyServiceCategory != null) {
2515                                         Log.i(this, "mEmergencyServiceCategory="
2516                                                 + mEmergencyServiceCategory);
2517                                     }
2518                                     if (mEmergencyUrns != null) {
2519                                         Log.i(this, "mEmergencyUrns=" + mEmergencyUrns);
2520                                     }
2521                                 }
2522                             }
2523                         }
2524 
2525                         if (mTelephonyConnectionService.maybeReselectDomain(this, reasonInfo,
2526                                 mShowPreciseFailedCause, mHangupDisconnectCause)) {
2527                             clearOriginalConnection();
2528                             break;
2529                         }
2530                     }
2531 
2532                     if (shouldTreatAsEmergencyCall()
2533                             && (cause
2534                             == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
2535                             || cause
2536                             == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) {
2537                         // We can get into a situation where the radio wants us to redial the
2538                         // same emergency call on the other available slot. This will not set
2539                         // the state to disconnected and will instead tell the
2540                         // TelephonyConnectionService to
2541                         // create a new originalConnection using the new Slot.
2542                         fireOnOriginalConnectionRetryDial(cause
2543                                 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE);
2544                     } else {
2545                         int preciseDisconnectCause = CallFailCause.NOT_VALID;
2546                         if (mShowPreciseFailedCause) {
2547                             preciseDisconnectCause =
2548                                     mOriginalConnection.getPreciseDisconnectCause();
2549                         }
2550                         int disconnectCause = mOriginalConnection.getDisconnectCause();
2551                         if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID)
2552                                 && (mHangupDisconnectCause != disconnectCause)) {
2553                             Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause
2554                                     + " -> " + mHangupDisconnectCause);
2555                             disconnectCause = mHangupDisconnectCause;
2556                         }
2557                         ImsReasonInfo imsReasonInfo = null;
2558                         if (isImsConnection()) {
2559                             ImsPhoneConnection imsPhoneConnection =
2560                                     (ImsPhoneConnection) mOriginalConnection;
2561                             imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
2562                         }
2563                         setTelephonyConnectionDisconnected(
2564                                 DisconnectCauseUtil.toTelecomDisconnectCause(
2565                                         disconnectCause,
2566                                         preciseDisconnectCause,
2567                                         mOriginalConnection.getVendorDisconnectCause(),
2568                                         getPhone().getPhoneId(), imsReasonInfo,
2569                                         new FlagsAdapterImpl(),
2570                                         shouldTreatAsEmergencyCall()));
2571                         close();
2572                     }
2573                     break;
2574                 case DISCONNECTING:
2575                     break;
2576             }
2577 
2578             if (mCommunicator != null) {
2579                 mCommunicator.onStateChanged(getTelecomCallId(), getState());
2580             }
2581         }
2582     }
2583 
updateState()2584     void updateState() {
2585         if (mOriginalConnection == null) {
2586             return;
2587         }
2588 
2589         updateStateInternal();
2590         updateStatusHints();
2591         updateConnectionCapabilities();
2592         updateConnectionProperties();
2593         updateAddress();
2594         updateMultiparty();
2595         refreshDisableAddCall();
2596         refreshCodec();
2597     }
2598 
2599     /**
2600      * Checks for changes to the multiparty bit.  If a conference has started, informs listeners.
2601      */
updateMultiparty()2602     private void updateMultiparty() {
2603         if (mOriginalConnection == null) {
2604             return;
2605         }
2606 
2607         if (mIsMultiParty != mOriginalConnection.isMultiparty()) {
2608             mIsMultiParty = mOriginalConnection.isMultiparty();
2609 
2610             if (mIsMultiParty) {
2611                 notifyConferenceStarted();
2612             }
2613         }
2614     }
2615 
2616     /**
2617      * Handles a failure when merging calls into a conference.
2618      * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()}
2619      * listener.
2620      */
handleConferenceMergeFailed()2621     private void handleConferenceMergeFailed(){
2622         mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget();
2623     }
2624 
2625     /**
2626      * Handles requests to update the multiparty state received via the
2627      * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)}
2628      * listener.
2629      * <p>
2630      * Note: We post this to the mHandler to ensure that if a conference must be created as a
2631      * result of the multiparty state change, the conference creation happens on the correct
2632      * thread.  This ensures that the thread check in
2633      * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)}
2634      * does not fire.
2635      *
2636      * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise.
2637      */
handleMultipartyStateChange(boolean isMultiParty)2638     private void handleMultipartyStateChange(boolean isMultiParty) {
2639         Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
2640         mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget();
2641     }
2642 
setActiveInternal()2643     private void setActiveInternal() {
2644         if (getState() == STATE_ACTIVE) {
2645             Log.w(this, "Should not be called if this is already ACTIVE");
2646             return;
2647         }
2648 
2649         // When we set a call to active, we need to make sure that there are no other active
2650         // calls. However, the ordering of state updates to connections can be non-deterministic
2651         // since all connections register for state changes on the phone independently.
2652         // To "optimize", we check here to see if there already exists any active calls.  If so,
2653         // we issue an update for those calls first to make sure we only have one top-level
2654         // active call.
2655         if (getTelephonyConnectionService() != null) {
2656             for (Connection current : getTelephonyConnectionService().getAllConnections()) {
2657                 if (current != this && current instanceof TelephonyConnection) {
2658                     TelephonyConnection other = (TelephonyConnection) current;
2659                     if (other.getState() == STATE_ACTIVE) {
2660                         other.updateState();
2661                     }
2662                 }
2663             }
2664         }
2665         setTelephonyConnectionActive();
2666     }
2667 
close()2668     public void close() {
2669         Log.v(this, "close");
2670         clearOriginalConnection();
2671         destroy();
2672         if (mTelephonyConnectionService != null) {
2673             removeTelephonyConnectionListener(
2674                     mTelephonyConnectionService.getTelephonyConnectionListener());
2675         }
2676         notifyDestroyed();
2677     }
2678 
2679     /**
2680      * Determines if the current connection is video capable.
2681      *
2682      * A connection is deemed to be video capable if the original connection capabilities state that
2683      * both local and remote video is supported.
2684      *
2685      * @return {@code true} if the connection is video capable, {@code false} otherwise.
2686      */
isVideoCapable()2687     private boolean isVideoCapable() {
2688         return (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
2689                 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL
2690                 && (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
2691                 == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL;
2692     }
2693 
2694     /**
2695      * Determines if the current connection is an external connection.
2696      *
2697      * A connection is deemed to be external if the original connection capabilities state that it
2698      * is.
2699      *
2700      * @return {@code true} if the connection is external, {@code false} otherwise.
2701      */
isExternalConnection()2702     private boolean isExternalConnection() {
2703         return (mOriginalConnectionCapabilities
2704                 & Capability.IS_EXTERNAL_CONNECTION) == Capability.IS_EXTERNAL_CONNECTION;
2705     }
2706 
2707     /**
2708      * Determines if the current connection has RTT enabled.
2709      */
isRtt()2710     private boolean isRtt() {
2711         return mOriginalConnection != null
2712                 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
2713                 && mOriginalConnection instanceof ImsPhoneConnection
2714                 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall();
2715     }
2716 
2717     /**
2718      * Determines if the current connection is cross sim calling
2719      */
isCrossSimCall()2720     private boolean isCrossSimCall() {
2721         return mOriginalConnection != null
2722                 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
2723                 && mOriginalConnection instanceof ImsPhoneConnection
2724                 && ((ImsPhoneConnection) mOriginalConnection).isCrossSimCall();
2725     }
2726 
2727     /**
2728      * Determines if the current connection is pullable.
2729      *
2730      * A connection is deemed to be pullable if the original connection capabilities state that it
2731      * is.
2732      *
2733      * @return {@code true} if the connection is pullable, {@code false} otherwise.
2734      */
isPullable()2735     private boolean isPullable() {
2736         return (mOriginalConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION)
2737                 == Capability.IS_EXTERNAL_CONNECTION
2738                 && (mOriginalConnectionCapabilities & Capability.IS_PULLABLE)
2739                 == Capability.IS_PULLABLE;
2740     }
2741 
2742     /**
2743      * Sets whether or not CDMA enhanced call privacy is enabled for this connection.
2744      */
setCdmaVoicePrivacy(boolean isEnabled)2745     private void setCdmaVoicePrivacy(boolean isEnabled) {
2746         if(mIsCdmaVoicePrivacyEnabled != isEnabled) {
2747             mIsCdmaVoicePrivacyEnabled = isEnabled;
2748             updateConnectionProperties();
2749         }
2750     }
2751 
2752     /**
2753      * Applies capabilities specific to conferences termination to the
2754      * {@code ConnectionCapabilities} bit-mask.
2755      *
2756      * @param capabilities The {@code ConnectionCapabilities} bit-mask.
2757      * @return The capabilities with the IMS conference capabilities applied.
2758      */
applyConferenceTerminationCapabilities(int capabilities)2759     private int applyConferenceTerminationCapabilities(int capabilities) {
2760         int currentCapabilities = capabilities;
2761 
2762         // An IMS call cannot be individually disconnected or separated from its parent conference.
2763         // If the call was IMS, even if it hands over to GMS, these capabilities are not supported.
2764         if (!mWasImsConnection) {
2765             currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE;
2766             currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE;
2767         }
2768 
2769         return currentCapabilities;
2770     }
2771 
2772     /**
2773      * Stores the new original connection capabilities, and applies them to the current connection,
2774      * notifying any listeners as necessary.
2775      *
2776      * @param connectionCapabilities The original connection capabilties.
2777      */
setOriginalConnectionCapabilities(int connectionCapabilities)2778     public void setOriginalConnectionCapabilities(int connectionCapabilities) {
2779         mOriginalConnectionCapabilities = connectionCapabilities;
2780         updateConnectionCapabilities();
2781         updateConnectionProperties();
2782     }
2783 
2784     /**
2785      * Called to apply the capabilities present in the {@link #mOriginalConnection} to this
2786      * {@link Connection}.  Provides a mapping between the capabilities present in the original
2787      * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in
2788      * this {@link Connection}.
2789      *
2790      * @param capabilities The capabilities bitmask from the {@link Connection}.
2791      * @return the capabilities bitmask with the original connection capabilities remapped and
2792      *      applied.
2793      */
applyOriginalConnectionCapabilities(int capabilities)2794     public int applyOriginalConnectionCapabilities(int capabilities) {
2795         // We only support downgrading to audio if both the remote and local side support
2796         // downgrading to audio.
2797         int supportsDowngrade = Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL
2798                 | Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE;
2799         boolean supportsDowngradeToAudio =
2800                 (mOriginalConnectionCapabilities & supportsDowngrade) == supportsDowngrade;
2801         capabilities = changeBitmask(capabilities,
2802                 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
2803 
2804         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
2805                 (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
2806                         == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
2807 
2808         boolean isLocalVideoSupported = (mOriginalConnectionCapabilities
2809                 & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
2810                 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled;
2811         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
2812                 isLocalVideoSupported);
2813 
2814         capabilities = changeBitmask(capabilities, CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT,
2815                 (mOriginalConnectionCapabilities & Capability.SUPPORTS_RTT_REMOTE)
2816                 == Capability.SUPPORTS_RTT_REMOTE);
2817 
2818         return capabilities;
2819     }
2820 
2821     /**
2822      * Whether the call is using wifi.
2823      */
isWifi()2824     boolean isWifi() {
2825         return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
2826     }
2827 
2828     /**
2829      * Sets whether this call has been identified by the network as an emergency call.
2830      * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call
2831      * as an emergency call, {@code false} otherwise.
2832      */
setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)2833     public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) {
2834         Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, "
2835                 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(),
2836                 isNetworkIdentifiedEmergencyCall);
2837         mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall;
2838         updateConnectionProperties();
2839     }
2840 
2841     /**
2842      * @return {@code true} if the network has identified this call as an emergency call,
2843      * {@code false} otherwise.
2844      */
isNetworkIdentifiedEmergencyCall()2845     public boolean isNetworkIdentifiedEmergencyCall() {
2846         return mIsNetworkIdentifiedEmergencyCall;
2847     }
2848 
2849     /**
2850      * @return {@code true} if this is an outgoing call, {@code false} otherwise.
2851      */
isOutgoingCall()2852     public boolean isOutgoingCall() {
2853         return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING;
2854     }
2855 
2856     /**
2857      * Sets the current call audio quality. Used during rebuild of the properties
2858      * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
2859      *
2860      * @param audioQuality The audio quality.
2861      */
setAudioQuality(int audioQuality)2862     public void setAudioQuality(int audioQuality) {
2863         mHasHighDefAudio = audioQuality ==
2864                 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION;
2865         updateConnectionProperties();
2866     }
2867 
resetStateForConference()2868     void resetStateForConference() {
2869         if (getState() == Connection.STATE_HOLDING) {
2870             resetStateOverride();
2871         }
2872     }
2873 
setHoldingForConference()2874     boolean setHoldingForConference() {
2875         if (getState() == Connection.STATE_ACTIVE) {
2876             setStateOverride(Call.State.HOLDING);
2877             return true;
2878         }
2879         return false;
2880     }
2881 
setRttTextStream(RttTextStream s)2882     public void setRttTextStream(RttTextStream s) {
2883         mRttTextStream = s;
2884     }
2885 
getRttTextStream()2886     public RttTextStream getRttTextStream() {
2887         return mRttTextStream;
2888     }
2889 
2890     /**
2891      * For video calls, sets whether this connection supports pausing the outgoing video for the
2892      * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
2893      *
2894      * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise.
2895      */
setVideoPauseSupported(boolean isVideoPauseSupported)2896     public void setVideoPauseSupported(boolean isVideoPauseSupported) {
2897         mIsVideoPauseSupported = isVideoPauseSupported;
2898     }
2899 
2900     /**
2901      * @return {@code true} if this connection supports pausing the outgoing video using the
2902      * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
2903      */
getVideoPauseSupported()2904     public boolean getVideoPauseSupported() {
2905         return mIsVideoPauseSupported;
2906     }
2907 
2908     /**
2909      * Sets whether this connection supports conference calling.
2910      * @param isConferenceSupported {@code true} if conference calling is supported by this
2911      *                                         connection, {@code false} otherwise.
2912      */
setConferenceSupported(boolean isConferenceSupported)2913     public void setConferenceSupported(boolean isConferenceSupported) {
2914         mIsConferenceSupported = isConferenceSupported;
2915     }
2916 
2917     /**
2918      * @return {@code true} if this connection supports merging calls into a conference.
2919      */
isConferenceSupported()2920     public boolean isConferenceSupported() {
2921         return mIsConferenceSupported;
2922     }
2923 
2924     /**
2925      * Sets whether managing conference call is supported after this connection being a part of a
2926      * Ims conference.
2927      *
2928      * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is
2929      *        supported after this connection being a part of a IMS conference,
2930      *        {@code false} otherwise.
2931      */
setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)2932     public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) {
2933         mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported;
2934     }
2935 
2936     /**
2937      * @return {@code true} if manage conference calling is supported after this connection being a
2938      * part of a IMS conference.
2939      */
isManageImsConferenceCallSupported()2940     public boolean isManageImsConferenceCallSupported() {
2941         return mIsManageImsConferenceCallSupported;
2942     }
2943 
2944     /**
2945      * Sets whether this connection supports showing precise call disconnect cause.
2946      * @param showPreciseFailedCause  {@code true} if showing precise call
2947      * disconnect cause is supported by this connection, {@code false} otherwise.
2948      */
setShowPreciseFailedCause(boolean showPreciseFailedCause)2949     public void setShowPreciseFailedCause(boolean showPreciseFailedCause) {
2950         mShowPreciseFailedCause = showPreciseFailedCause;
2951     }
2952 
2953     /**
2954      * Sets whether TTY is enabled or not.
2955      * @param isTtyEnabled
2956      */
setTtyEnabled(boolean isTtyEnabled)2957     public void setTtyEnabled(boolean isTtyEnabled) {
2958         mIsTtyEnabled = isTtyEnabled;
2959         updateConnectionCapabilities();
2960     }
2961 
2962     /**
2963      * Whether the original connection is an IMS connection.
2964      * @return {@code True} if the original connection is an IMS connection, {@code false}
2965      *     otherwise.
2966      */
isImsConnection()2967     protected boolean isImsConnection() {
2968         com.android.internal.telephony.Connection originalConnection = getOriginalConnection();
2969 
2970         return originalConnection != null
2971                 && originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
2972                 && originalConnection instanceof ImsPhoneConnection;
2973     }
2974 
2975     /**
2976      * Whether the original connection is an GSM/CDMA connection.
2977      * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false}
2978      *     otherwise.
2979      */
isGsmCdmaConnection()2980     protected boolean isGsmCdmaConnection() {
2981         Phone phone = getPhone();
2982         if (phone != null) {
2983             switch (phone.getPhoneType()) {
2984                 case PhoneConstants.PHONE_TYPE_GSM:
2985                 case PhoneConstants.PHONE_TYPE_CDMA:
2986                     return true;
2987                 default:
2988                     return false;
2989             }
2990         }
2991         return false;
2992     }
2993 
2994     /**
2995      * Whether the original connection was ever an IMS connection, either before or now.
2996      * @return {@code True} if the original connection was ever an IMS connection, {@code false}
2997      *     otherwise.
2998      */
wasImsConnection()2999     public boolean wasImsConnection() {
3000         return mWasImsConnection;
3001     }
3002 
getIsUsingAssistedDialing()3003     boolean getIsUsingAssistedDialing() {
3004         return mIsUsingAssistedDialing;
3005     }
3006 
setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)3007     void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) {
3008         mIsUsingAssistedDialing = isUsingAssistedDialing;
3009         updateConnectionProperties();
3010     }
3011 
getAddressFromNumber(String number)3012     private static Uri getAddressFromNumber(String number) {
3013         // Address can be null for blocked calls.
3014         if (number == null) {
3015             number = "";
3016         }
3017         return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
3018     }
3019 
3020     /**
3021      * Changes a capabilities bit-mask to add or remove a capability.
3022      *
3023      * @param bitmask The bit-mask.
3024      * @param bitfield The bit-field to change.
3025      * @param enabled Whether the bit-field should be set or removed.
3026      * @return The bit-mask with the bit-field changed.
3027      */
changeBitmask(int bitmask, int bitfield, boolean enabled)3028     private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
3029         if (enabled) {
3030             return bitmask | bitfield;
3031         } else {
3032             return bitmask & ~bitfield;
3033         }
3034     }
3035 
updateStatusHints()3036     private void updateStatusHints() {
3037         if (isWifi() && !isCrossSimCall() && getPhone() != null) {
3038             int labelId = isValidRingingCall()
3039                     ? R.string.status_hint_label_incoming_wifi_call
3040                     : R.string.status_hint_label_wifi_call;
3041 
3042             Context context = getPhone().getContext();
3043             setTelephonyStatusHints(new StatusHints(
3044                     getResourceString(labelId),
3045                     Icon.createWithResource(
3046                             context, R.drawable.ic_signal_wifi_4_bar_24dp),
3047                     null /* extras */));
3048         } else {
3049             setTelephonyStatusHints(null);
3050         }
3051     }
3052 
3053     /**
3054      * Register a listener for {@link TelephonyConnection} specific triggers.
3055      * @param l The instance of the listener to add
3056      * @return The connection being listened to
3057      */
addTelephonyConnectionListener(TelephonyConnectionListener l)3058     public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) {
3059         mTelephonyListeners.add(l);
3060         // If we already have an original connection, let's call back immediately.
3061         // This would be the case for incoming calls.
3062         if (mOriginalConnection != null) {
3063             fireOnOriginalConnectionConfigured();
3064         }
3065         return this;
3066     }
3067 
3068     /**
3069      * Remove a listener for {@link TelephonyConnection} specific triggers.
3070      * @param l The instance of the listener to remove
3071      * @return The connection being listened to
3072      */
removeTelephonyConnectionListener( TelephonyConnectionListener l)3073     public final TelephonyConnection removeTelephonyConnectionListener(
3074             TelephonyConnectionListener l) {
3075         if (l != null) {
3076             mTelephonyListeners.remove(l);
3077         }
3078         return this;
3079     }
3080 
3081     @Override
setHoldable(boolean isHoldable)3082     public void setHoldable(boolean isHoldable) {
3083         mIsHoldable = isHoldable;
3084         updateConnectionCapabilities();
3085     }
3086 
3087     @Override
isChildHoldable()3088     public boolean isChildHoldable() {
3089         return getConference() != null;
3090     }
3091 
isHoldable()3092     public boolean isHoldable() {
3093         return mIsHoldable;
3094     }
3095 
3096     /**
3097      * Fire a callback to the various listeners for when the original connection is
3098      * set in this {@link TelephonyConnection}
3099      */
fireOnOriginalConnectionConfigured()3100     private final void fireOnOriginalConnectionConfigured() {
3101         for (TelephonyConnectionListener l : mTelephonyListeners) {
3102             l.onOriginalConnectionConfigured(this);
3103         }
3104     }
3105 
fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)3106     private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) {
3107         for (TelephonyConnectionListener l : mTelephonyListeners) {
3108             l.onOriginalConnectionRetry(this, isPermanentFailure);
3109         }
3110     }
3111 
3112     /**
3113      * Handles exiting ECM mode.
3114      */
handleExitedEcmMode()3115     protected void handleExitedEcmMode() {
3116         updateConnectionProperties();
3117     }
3118 
3119     /**
3120      * Determines whether the connection supports conference calling.  A connection supports
3121      * conference calling if it:
3122      * 1. Is not an emergency call.
3123      * 2. Carrier supports conference calls.
3124      * 3. If call is a video call, carrier supports video conference calls.
3125      * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
3126      */
3127     @VisibleForTesting
refreshConferenceSupported()3128     void refreshConferenceSupported() {
3129         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
3130         Phone phone = getPhone();
3131         if (phone == null) {
3132             Log.w(this, "refreshConferenceSupported = false; phone is null");
3133             if (isConferenceSupported()) {
3134                 setConferenceSupported(false);
3135                 notifyConferenceSupportedChanged(false);
3136             }
3137             return;
3138         }
3139 
3140         boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
3141         boolean isVoWifiEnabled = false;
3142         if (isIms) {
3143             isVoWifiEnabled = isWfcEnabled(phone);
3144         }
3145         PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
3146                 .makePstnPhoneAccountHandle(phone.getDefaultPhone())
3147                 : PhoneUtils.makePstnPhoneAccountHandle(phone);
3148         TelecomAccountRegistry telecomAccountRegistry = getTelecomAccountRegistry(
3149                 getPhone().getContext());
3150         boolean isConferencingSupported = telecomAccountRegistry
3151                 .isMergeCallSupported(phoneAccountHandle);
3152         boolean isImsConferencingSupported = telecomAccountRegistry
3153                 .isMergeImsCallSupported(phoneAccountHandle);
3154         mIsCarrierVideoConferencingSupported = telecomAccountRegistry
3155                 .isVideoConferencingSupported(phoneAccountHandle);
3156         boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry
3157                 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle);
3158         ImsCall imsCall = isImsConnection()
3159                 ? ((ImsPhoneConnection) getOriginalConnection()).getImsCall()
3160                 : null;
3161         CarrierConfigManager configManager = (CarrierConfigManager) phone.getContext()
3162                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
3163         boolean downGradedVideoCall = false;
3164         if (configManager != null) {
3165             PersistableBundle config = configManager.getConfigForSubId(phone.getSubId());
3166             if (config != null) {
3167                 downGradedVideoCall = config.getBoolean(
3168                         CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL);
3169             }
3170         }
3171 
3172         Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " +
3173                 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " +
3174                 "isWifi=%b, isVoWifiEnabled=%b",
3175                 isConferencingSupported, isImsConferencingSupported,
3176                 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff,
3177                 isWifi(), isVoWifiEnabled);
3178         boolean isConferenceSupported = true;
3179         if (mTreatAsEmergencyCall) {
3180             isConferenceSupported = false;
3181             Log.d(this, "refreshConferenceSupported = false; emergency call");
3182         } else if (isRtt() && !isRttMergeSupported(getCarrierConfig())) {
3183             isConferenceSupported = false;
3184             Log.d(this, "refreshConferenceSupported = false; rtt call");
3185         } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) {
3186             isConferenceSupported = false;
3187             Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf.");
3188         } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) {
3189             isConferenceSupported = false;
3190             Log.d(this, "refreshConferenceSupported = false; video conf not supported.");
3191         } else if ((imsCall != null) && (imsCall.wasVideoCall() && downGradedVideoCall)
3192                 && !mIsCarrierVideoConferencingSupported) {
3193             isConferenceSupported = false;
3194             Log.d(this,
3195                     "refreshConferenceSupported = false;"
3196                             + " video conf not supported for downgraded audio call.");
3197         } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) {
3198             isConferenceSupported = false;
3199             Log.d(this,
3200                     "refreshConferenceSupported = false; can't merge wifi calls when voWifi off.");
3201         } else {
3202             Log.d(this, "refreshConferenceSupported = true.");
3203         }
3204 
3205         if (isConferenceSupported != isConferenceSupported()) {
3206             setConferenceSupported(isConferenceSupported);
3207             notifyConferenceSupportedChanged(isConferenceSupported);
3208         }
3209     }
3210 
3211     @VisibleForTesting
isWfcEnabled(Phone phone)3212     boolean isWfcEnabled(Phone phone) {
3213         return ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
3214     }
3215 
3216     /**
3217      * Provides a mapping from extras keys which may be found in the
3218      * {@link com.android.internal.telephony.Connection} to their equivalents defined in
3219      * {@link android.telecom.Connection}.
3220      *
3221      * @return Map containing key mappings.
3222      */
createExtrasMap()3223     private static Map<String, String> createExtrasMap() {
3224         Map<String, String> result = new HashMap<String, String>();
3225         result.put(ImsCallProfile.EXTRA_CHILD_NUMBER,
3226                 android.telecom.Connection.EXTRA_CHILD_ADDRESS);
3227         result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT,
3228                 android.telecom.Connection.EXTRA_CALL_SUBJECT);
3229         result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS,
3230                 android.telecom.Connection.EXTRA_SIP_INVITE);
3231         return Collections.unmodifiableMap(result);
3232     }
3233 
isShowingOriginalDialString()3234     private boolean isShowingOriginalDialString() {
3235         boolean showOrigDialString = false;
3236         Phone phone = getPhone();
3237         if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)
3238                 && !mOriginalConnection.isIncoming()) {
3239             showOrigDialString = getCarrierConfig().getBoolean(CarrierConfigManager
3240                     .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
3241             Log.d(this, "showOrigDialString: " + showOrigDialString);
3242         }
3243         return showOrigDialString;
3244     }
3245 
3246     /**
3247      * Creates a string representation of this {@link TelephonyConnection}.  Primarily intended for
3248      * use in log statements.
3249      *
3250      * @return String representation of the connection.
3251      */
3252     @Override
toString()3253     public String toString() {
3254         StringBuilder sb = new StringBuilder();
3255         sb.append("[TelephonyConnection objId:");
3256         sb.append(System.identityHashCode(this));
3257         sb.append(" telecomCallID:");
3258         sb.append(getTelecomCallId());
3259         sb.append(" type:");
3260         if (isImsConnection()) {
3261             sb.append("ims");
3262         } else if (this instanceof com.android.services.telephony.GsmConnection) {
3263             sb.append("gsm");
3264         } else if (this instanceof CdmaConnection) {
3265             sb.append("cdma");
3266         }
3267         sb.append(" state:");
3268         sb.append(Connection.stateToString(getState()));
3269         sb.append(" capabilities:");
3270         sb.append(capabilitiesToString(getConnectionCapabilities()));
3271         sb.append(" properties:");
3272         sb.append(propertiesToString(getConnectionProperties()));
3273         sb.append(" address:");
3274         sb.append(Rlog.pii(LOG_TAG, getAddress()));
3275         sb.append(" originalConnection:");
3276         sb.append(mOriginalConnection);
3277         sb.append(" partOfConf:");
3278         if (getConference() == null) {
3279             sb.append("N");
3280         } else {
3281             sb.append("Y");
3282         }
3283         sb.append(" confSupported:");
3284         sb.append(mIsConferenceSupported ? "Y" : "N");
3285         sb.append(" isAdhocConf:");
3286         sb.append(isAdhocConferenceCall() ? "Y" : "N");
3287         sb.append("]");
3288         return sb.toString();
3289     }
3290 
setTelephonyConnectionService(TelephonyConnectionService connectionService)3291     public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) {
3292         mTelephonyConnectionService = connectionService;
3293     }
3294 
getTelephonyConnectionService()3295     public final TelephonyConnectionService getTelephonyConnectionService() {
3296         return mTelephonyConnectionService;
3297     }
3298 
3299     /**
3300      * Set this {@link TelephonyConnection} to an active state.
3301      * <p>
3302      * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified.
3303      */
setTelephonyConnectionActive()3304     public void setTelephonyConnectionActive() {
3305         setActive();
3306         notifyStateChanged(getState());
3307     }
3308 
3309     /**
3310      * Set this {@link TelephonyConnection} to a ringing state.
3311      * <p>
3312      * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified.
3313      */
setTelephonyConnectionRinging()3314     public void setTelephonyConnectionRinging() {
3315         setRinging();
3316         notifyStateChanged(getState());
3317     }
3318 
3319     /**
3320      * Set this {@link TelephonyConnection} to an initializing state.
3321      * <p>
3322      * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are
3323      * notified.
3324      */
setTelephonyConnectionInitializing()3325     public void setTelephonyConnectionInitializing() {
3326         setInitializing();
3327         notifyStateChanged(getState());
3328     }
3329 
3330     /**
3331      * Set this {@link TelephonyConnection} to a dialing state.
3332      * <p>
3333      * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified.
3334      */
setTelephonyConnectionDialing()3335     public void setTelephonyConnectionDialing() {
3336         setDialing();
3337         notifyStateChanged(getState());
3338     }
3339 
3340     /**
3341      * Set this {@link TelephonyConnection} to a pulling state.
3342      * <p>
3343      * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified.
3344      */
setTelephonyConnectionPulling()3345     public void setTelephonyConnectionPulling() {
3346         setPulling();
3347         notifyStateChanged(getState());
3348     }
3349 
3350     /**
3351      * Set this {@link TelephonyConnection} to a held state.
3352      * <p>
3353      * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified.
3354      */
setTelephonyConnectionOnHold()3355     public void setTelephonyConnectionOnHold() {
3356         setOnHold();
3357         notifyStateChanged(getState());
3358     }
3359 
3360     /**
3361      * Set this {@link TelephonyConnection} to a disconnected state.
3362      * <p>
3363      * Note: This should be used instead of
3364      * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
3365      */
setTelephonyConnectionDisconnected(@onNull android.telecom.DisconnectCause disconnectCause)3366     public void setTelephonyConnectionDisconnected(@NonNull
3367             android.telecom.DisconnectCause disconnectCause) {
3368         setDisconnected(disconnectCause);
3369         notifyDisconnected(disconnectCause);
3370         notifyStateChanged(getState());
3371     }
3372 
3373     /**
3374      * Sends a connection event for this {@link TelephonyConnection}.
3375      * <p>
3376      * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure
3377      * listeners are notified.
3378      */
sendTelephonyConnectionEvent(@onNull String event, @Nullable Bundle extras)3379     public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) {
3380         sendConnectionEvent(event, extras);
3381         notifyTelephonyConnectionEvent(event, extras);
3382     }
3383 
3384     /**
3385      * Sets the extras associated with this {@link TelephonyConnection}.
3386      * <p>
3387      * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are
3388      * notified.
3389      */
putTelephonyExtras(@onNull Bundle extras)3390     public void putTelephonyExtras(@NonNull Bundle extras) {
3391         putExtras(extras);
3392         notifyPutExtras(extras);
3393     }
3394 
3395     /**
3396      * Removes the specified extras associated with this {@link TelephonyConnection}.
3397      * <p>
3398      * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are
3399      * notified.
3400      */
removeTelephonyExtras(@onNull List<String> keys)3401     public void removeTelephonyExtras(@NonNull List<String> keys) {
3402         removeExtras(keys);
3403         notifyRemoveExtras(keys);
3404     }
3405 
3406     /**
3407      * Sets the video state associated with this {@link TelephonyConnection}.
3408      * <p>
3409      * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are
3410      * notified.
3411      * @param videoState The new video state. Valid values:
3412      *                   {@link VideoProfile#STATE_AUDIO_ONLY},
3413      *                   {@link VideoProfile#STATE_BIDIRECTIONAL},
3414      *                   {@link VideoProfile#STATE_TX_ENABLED},
3415      *                   {@link VideoProfile#STATE_RX_ENABLED}.
3416      */
setTelephonyVideoState(int videoState)3417     public void setTelephonyVideoState(int videoState) {
3418         setVideoState(videoState);
3419         notifyVideoStateChanged(videoState);
3420     }
3421 
3422     /**
3423      * Sets the video provider associated with this {@link TelephonyConnection}.
3424      * <p>
3425      * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure
3426      * listeners are notified.
3427      */
setTelephonyVideoProvider(@ullable VideoProvider videoProvider)3428     public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) {
3429         setVideoProvider(videoProvider);
3430         notifyVideoProviderChanged(videoProvider);
3431     }
3432 
3433     /**
3434      * Sets the status hints associated with this {@link TelephonyConnection}.
3435      * <p>
3436      * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners
3437      * are notified.
3438      */
setTelephonyStatusHints(@ullable StatusHints statusHints)3439     public void setTelephonyStatusHints(@Nullable StatusHints statusHints) {
3440         setStatusHints(statusHints);
3441         notifyStatusHintsChanged(statusHints);
3442     }
3443 
3444     /**
3445      * Sets RIL voice radio technology used for current connection.
3446      * <p>
3447      * This property is set by the Telephony {@link ConnectionService}.
3448      *
3449      * @param vrat the RIL Voice Radio Technology used for current connection,
3450      *             see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
3451      */
setCallRadioTech(@ilRadioTechnology int vrat)3452     public final void setCallRadioTech(@RilRadioTechnology int vrat) {
3453         Bundle extras = getExtras();
3454         if (extras == null) {
3455             extras = new Bundle();
3456         }
3457         extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
3458                 ServiceState.rilRadioTechnologyToNetworkType(vrat));
3459         putExtras(extras);
3460         // Propagates the call radio technology to its parent {@link android.telecom.Conference}
3461         // This action only covers non-IMS CS conference calls.
3462         // For IMS PS call conference call, it can be updated via its host connection
3463         // {@link #Listener.onExtrasChanged} event.
3464         if (getConference() != null) {
3465             Bundle newExtras = new Bundle();
3466             newExtras.putInt(
3467                     TelecomManager.EXTRA_CALL_NETWORK_TYPE,
3468                     ServiceState.rilRadioTechnologyToNetworkType(vrat));
3469             getConference().putExtras(newExtras);
3470         }
3471     }
3472 
3473     /**
3474      * Returns RIL voice radio technology used for current connection.
3475      * <p>
3476      * Used by the Telephony {@link ConnectionService}.
3477      *
3478      * @return the RIL voice radio technology used for current connection,
3479      *         see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
3480      */
getCallRadioTech()3481     public final @RilRadioTechnology int getCallRadioTech() {
3482         int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
3483         Bundle extras = getExtras();
3484         if (extras != null) {
3485             voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
3486                     TelephonyManager.NETWORK_TYPE_UNKNOWN);
3487         }
3488         return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
3489     }
3490 
3491     /**
3492      * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data
3493      * received via the {@link ImsConference} (i.e. conference event package).
3494      *
3495      * @param conferenceParticipants The participants.
3496      */
updateConferenceParticipants( @onNull List<ConferenceParticipant> conferenceParticipants)3497     private void updateConferenceParticipants(
3498             @NonNull List<ConferenceParticipant> conferenceParticipants) {
3499         for (TelephonyConnectionListener l : mTelephonyListeners) {
3500             l.onConferenceParticipantsChanged(this, conferenceParticipants);
3501         }
3502     }
3503 
3504     /**
3505      * Where device to device communication is available and this is an IMS call, configures the
3506      * D2D communication infrastructure for operation.
3507      */
maybeConfigureDeviceToDeviceCommunication()3508     private void maybeConfigureDeviceToDeviceCommunication() {
3509         if (!getPhone().getContext().getResources().getBoolean(
3510                 R.bool.config_use_device_to_device_communication)) {
3511             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D.");
3512             notifyD2DAvailabilityChanged(false);
3513             return;
3514         }
3515         if (!isImsConnection()) {
3516             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection.");
3517             if (mCommunicator != null) {
3518                 mCommunicator = null;
3519             }
3520             notifyD2DAvailabilityChanged(false);
3521             return;
3522         }
3523         if (mTreatAsEmergencyCall || mIsNetworkIdentifiedEmergencyCall) {
3524             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: emergency call; no D2D");
3525             notifyD2DAvailabilityChanged(false);
3526             return;
3527         }
3528 
3529         ArrayList<TransportProtocol> supportedTransports = new ArrayList<>(2);
3530 
3531         if (supportsD2DUsingRtp()) {
3532             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports RTP.");
3533             // Implement abstracted out RTP functionality the RTP transport depends on.
3534             RtpAdapter rtpAdapter = new RtpAdapter() {
3535                 @Override
3536                 public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() {
3537                     ImsPhoneConnection originalConnection =
3538                             (ImsPhoneConnection) mOriginalConnection;
3539                     return originalConnection.getAcceptedRtpHeaderExtensions();
3540                 }
3541 
3542                 @Override
3543                 public void sendRtpHeaderExtensions(
3544                         @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
3545                     Log.i(TelephonyConnection.this, "sendRtpHeaderExtensions: sending: %s",
3546                             rtpHeaderExtensions.stream()
3547                                     .map(r -> r.toString())
3548                                     .collect(Collectors.joining(",")));
3549                     ImsPhoneConnection originalConnection =
3550                             (ImsPhoneConnection) mOriginalConnection;
3551                     originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions);
3552                 }
3553             };
3554             mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler,
3555                     supportsSdpNegotiationOfRtpHeaderExtensions());
3556             supportedTransports.add(mRtpTransport);
3557         }
3558         if (supportsD2DUsingDtmf()) {
3559             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports DTMF.");
3560             DtmfAdapter dtmfAdapter = digit -> {
3561                 Log.i(TelephonyConnection.this, "sendDtmf: send digit %c", digit);
3562                 ImsPhoneConnection originalConnection =
3563                         (ImsPhoneConnection) mOriginalConnection;
3564                 Message dtmfComplete = mHandler.obtainMessage(MSG_DTMF_DONE);
3565                 dtmfComplete.replyTo = mHandlerMessenger;
3566                 originalConnection.getImsCall().sendDtmf(digit, dtmfComplete);
3567             };
3568             ContentResolver cr = getPhone().getContext().getContentResolver();
3569             mDtmfTransport = new DtmfTransport(dtmfAdapter, new Timeouts.Adapter(cr),
3570                     Executors.newSingleThreadScheduledExecutor());
3571             supportedTransports.add(mDtmfTransport);
3572         }
3573         if (supportedTransports.size() > 0) {
3574             mCommunicator = new Communicator(supportedTransports, this);
3575             mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator);
3576             addTelephonyConnectionListener(mD2DCallStateAdapter);
3577         } else {
3578             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: no transports; disabled.");
3579             notifyD2DAvailabilityChanged(false);
3580         }
3581     }
3582 
3583     /**
3584      * Notifies upper layers of the availability of D2D communication.
3585      * @param isAvailable {@code true} if D2D is available, {@code false} otherwise.
3586      */
notifyD2DAvailabilityChanged(boolean isAvailable)3587     private void notifyD2DAvailabilityChanged(boolean isAvailable) {
3588         Bundle extras = new Bundle();
3589         extras.putBoolean(Connection.EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE,
3590                 isAvailable);
3591         putTelephonyExtras(extras);
3592     }
3593 
3594     /**
3595      * @return The D2D communication class, or {@code null} if not set up.
3596      */
getCommunicator()3597     public @Nullable Communicator getCommunicator() {
3598         return mCommunicator;
3599     }
3600 
3601     /**
3602      * Called by {@link Communicator} associated with this {@link TelephonyConnection} when there
3603      * are incoming device-to-device messages received.
3604      * @param messages the incoming messages.
3605      */
3606     @Override
onMessagesReceived(@onNull Set<Communicator.Message> messages)3607     public void onMessagesReceived(@NonNull Set<Communicator.Message> messages) {
3608         Log.i(this, "onMessagesReceived: got d2d messages: %s", messages);
3609         // Send connection events up to Telecom so that we can relay the messages to a valid
3610         // CallDiagnosticService which is bound.
3611         for (Communicator.Message msg : messages) {
3612             Integer dcMsgType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getValue(
3613                     msg.getType());
3614             if (dcMsgType == null) {
3615                 // Invalid msg type, skip.
3616                 continue;
3617             }
3618 
3619             Integer dcMsgValue;
3620             switch (msg.getType()) {
3621                 case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC:
3622                     dcMsgValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getValue(
3623                             msg.getValue());
3624                     break;
3625                 case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE:
3626                     dcMsgValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getValue(
3627                             msg.getValue());
3628                     break;
3629                 case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE:
3630                     dcMsgValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE
3631                             .getValue(msg.getValue());
3632                     break;
3633                 case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE:
3634                     dcMsgValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE
3635                             .getValue(msg.getValue());
3636                     break;
3637                 default:
3638                     Log.w(this, "onMessagesReceived: msg=%d - invalid msg", msg.getValue());
3639                     continue;
3640             }
3641             if (dcMsgValue == null) {
3642                 Log.w(this, "onMessagesReceived: msg=%d/%d - invalid msg value", msg.getType(),
3643                         msg.getValue());
3644                 continue;
3645             }
3646             Bundle extras = new Bundle();
3647             extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, dcMsgType);
3648             extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, dcMsgValue);
3649             sendConnectionEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras);
3650         }
3651     }
3652 
3653     /**
3654      * Handles report from {@link Communicator} when the availability of D2D changes.
3655      * @param isAvailable {@code true} if D2D is available, {@code false} if unavailable.
3656      */
3657     @Override
onD2DAvailabilitychanged(boolean isAvailable)3658     public void onD2DAvailabilitychanged(boolean isAvailable) {
3659         notifyD2DAvailabilityChanged(isAvailable);
3660     }
3661 
3662     /**
3663      * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
3664      * operation has started.
3665      */
notifyConferenceStarted()3666     protected void notifyConferenceStarted() {
3667         for (TelephonyConnectionListener l : mTelephonyListeners) {
3668             l.onConferenceStarted();
3669         }
3670     }
3671 
3672     /**
3673      * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection
3674      * which impacts its ability to be a part of a conference call.
3675      * @param isConferenceSupported {@code true} if the connection supports being part of a
3676      *      conference call, {@code false} otherwise.
3677      */
notifyConferenceSupportedChanged(boolean isConferenceSupported)3678     private void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
3679         for (TelephonyConnectionListener l : mTelephonyListeners) {
3680             l.onConferenceSupportedChanged(this, isConferenceSupported);
3681         }
3682     }
3683 
3684     /**
3685      * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities.
3686      * @param newCapabilities the new capabilities.
3687      */
notifyConnectionCapabilitiesChanged(int newCapabilities)3688     private void notifyConnectionCapabilitiesChanged(int newCapabilities) {
3689         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3690             listener.onConnectionCapabilitiesChanged(this, newCapabilities);
3691         }
3692     }
3693 
3694     /**
3695      * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties.
3696      * @param newProperties the new properties.
3697      */
notifyConnectionPropertiesChanged(int newProperties)3698     private void notifyConnectionPropertiesChanged(int newProperties) {
3699         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3700             listener.onConnectionPropertiesChanged(this, newProperties);
3701         }
3702     }
3703 
3704     /**
3705      * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed.
3706      */
notifyDestroyed()3707     private void notifyDestroyed() {
3708         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3709             listener.onDestroyed(this);
3710         }
3711     }
3712 
3713     /**
3714      * Notifies {@link TelephonyConnectionListener}s when a connection disconnects.
3715      * @param cause The disconnect cause.
3716      */
notifyDisconnected(android.telecom.DisconnectCause cause)3717     private void notifyDisconnected(android.telecom.DisconnectCause cause) {
3718         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3719             listener.onDisconnected(this, cause);
3720         }
3721     }
3722 
3723     /**
3724      * Notifies {@link TelephonyConnectionListener}s of connection state changes.
3725      * @param newState The new state.
3726      */
notifyStateChanged(int newState)3727     private void notifyStateChanged(int newState) {
3728         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3729             listener.onStateChanged(this, newState);
3730         }
3731     }
3732 
3733     /**
3734      * Notifies {@link TelephonyConnectionListener}s of telephony connection events.
3735      * @param event The event.
3736      * @param extras Any extras.
3737      */
notifyTelephonyConnectionEvent(String event, Bundle extras)3738     private void notifyTelephonyConnectionEvent(String event, Bundle extras) {
3739         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3740             listener.onConnectionEvent(this, event, extras);
3741         }
3742     }
3743 
3744     /**
3745      * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection.
3746      * @param extras The new extras.
3747      */
notifyPutExtras(Bundle extras)3748     private void notifyPutExtras(Bundle extras) {
3749         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3750             listener.onExtrasChanged(this, extras);
3751         }
3752     }
3753 
3754     /**
3755      * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection.
3756      * @param keys The removed keys.
3757      */
notifyRemoveExtras(List<String> keys)3758     private void notifyRemoveExtras(List<String> keys) {
3759         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3760             listener.onExtrasRemoved(this, keys);
3761         }
3762     }
3763 
3764     /**
3765      * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection.
3766      * @param videoState The new video state. Valid values:
3767      *                   {@link VideoProfile#STATE_AUDIO_ONLY},
3768      *                   {@link VideoProfile#STATE_BIDIRECTIONAL},
3769      *                   {@link VideoProfile#STATE_TX_ENABLED},
3770      *                   {@link VideoProfile#STATE_RX_ENABLED}.
3771      */
notifyVideoStateChanged(int videoState)3772     private void notifyVideoStateChanged(int videoState) {
3773         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3774             listener.onVideoStateChanged(this, videoState);
3775         }
3776     }
3777 
3778     /**
3779      * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not.
3780      * @param ringback Whether the ringback tone is to be played
3781      */
notifyRingbackRequested(boolean ringback)3782     private void notifyRingbackRequested(boolean ringback) {
3783         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3784             listener.onRingbackRequested(this, ringback);
3785         }
3786     }
3787 
3788     /**
3789      * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
3790      * connection.
3791      * @param videoProvider The new video provider.
3792      */
notifyVideoProviderChanged(VideoProvider videoProvider)3793     private void notifyVideoProviderChanged(VideoProvider videoProvider) {
3794         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3795             listener.onVideoProviderChanged(this, videoProvider);
3796         }
3797     }
3798 
3799     /**
3800      * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a
3801      * connection.
3802      * @param statusHints The new status hints.
3803      */
notifyStatusHintsChanged(StatusHints statusHints)3804     private void notifyStatusHintsChanged(StatusHints statusHints) {
3805         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3806             listener.onStatusHintsChanged(this, statusHints);
3807         }
3808     }
3809 
3810     /**
3811      * Whether the incoming call number should be formatted to national number for Japan.
3812      * @return {@code true} should be convert to the national format, {@code false} otherwise.
3813      */
isNeededToFormatIncomingNumberForJp()3814     private boolean isNeededToFormatIncomingNumberForJp() {
3815         if (mOriginalConnection.isIncoming()
3816                 && !TextUtils.isEmpty(mOriginalConnection.getAddress())
3817                 && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) {
3818             return getCarrierConfig().getBoolean(
3819                     CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL);
3820         }
3821         return false;
3822     }
3823 
3824     /**
3825      * Format the incoming call number to national number for Japan.
3826      * @param number
3827      * @return the formatted phone number (e.g, "+819012345678" -> "09012345678")
3828      */
formatIncomingNumberForJp(String number)3829     private String formatIncomingNumberForJp(String number) {
3830         return PhoneNumberUtils.stripSeparators(
3831                 PhoneNumberUtils.formatNumber(number, JAPAN_ISO_COUNTRY_CODE));
3832     }
3833 
getTelecomAccountRegistry(Context context)3834     public TelecomAccountRegistry getTelecomAccountRegistry(Context context) {
3835         return TelecomAccountRegistry.getInstance(context);
3836     }
3837 
3838     /**
3839      * @return {@code true} if the carrier supports D2D using RTP header extensions, {@code false}
3840      * otherwise.
3841      */
supportsD2DUsingRtp()3842     private boolean supportsD2DUsingRtp() {
3843         return getCarrierConfig().getBoolean(
3844                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
3845     }
3846 
3847     /**
3848      * @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise.
3849      */
supportsD2DUsingDtmf()3850     private boolean supportsD2DUsingDtmf() {
3851         return getCarrierConfig().getBoolean(
3852                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL);
3853     }
3854 
3855     /**
3856      * @return {@code true} if the carrier supports using SDP negotiation for the RTP header
3857      * extensions used in D2D comms, {@code false} otherwise.
3858      */
supportsSdpNegotiationOfRtpHeaderExtensions()3859     private boolean supportsSdpNegotiationOfRtpHeaderExtensions() {
3860         return getCarrierConfig().getBoolean(
3861                 CarrierConfigManager
3862                         .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
3863     }
3864 
3865     /**
3866      * Handles a device to device message which a {@link CallDiagnostics} wishes to send.
3867      * @param extras the call event extras bundle.
3868      */
handleOutgoingDeviceToDeviceMessage(Bundle extras)3869     private void handleOutgoingDeviceToDeviceMessage(Bundle extras) {
3870         int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE);
3871         int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE);
3872 
3873         Integer internalMessageValue;
3874         switch (messageType) {
3875             case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC:
3876                 internalMessageValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getKey(
3877                         messageValue);
3878                 break;
3879             case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE:
3880                 internalMessageValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getKey(
3881                         messageValue);
3882                 break;
3883             case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE:
3884                 internalMessageValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE
3885                         .getKey(messageValue);
3886                 break;
3887             case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE:
3888                 internalMessageValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE
3889                         .getKey(messageValue);
3890                 break;
3891             default:
3892                 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d - invalid msg",
3893                         messageType);
3894                 return;
3895         }
3896         Integer internalMessageType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getKey(
3897                 messageType);
3898         if (internalMessageValue == null) {
3899             Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - invalid value",
3900                     messageType, messageValue);
3901             return;
3902         }
3903 
3904         if (mCommunicator != null) {
3905             Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - sending",
3906                     internalMessageType, internalMessageValue);
3907             Set<Communicator.Message> set = new ArraySet<>();
3908             set.add(new Communicator.Message(internalMessageType, internalMessageValue));
3909             mCommunicator.sendMessages(set);
3910         }
3911     }
3912 
3913     /**
3914      * Returns the current telephony connection listeners for test purposes.
3915      * @return list of telephony connection listeners.
3916      */
3917     @VisibleForTesting
getTelephonyConnectionListeners()3918     public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
3919         return new ArrayList<>(mTelephonyListeners);
3920     }
3921 
3922     /**
3923      * @return An {@link Integer} instance of the emergency service category.
3924      */
getEmergencyServiceCategory()3925     public @Nullable Integer getEmergencyServiceCategory() {
3926         return mEmergencyServiceCategory;
3927     }
3928 
3929     /**
3930      * Sets the emergency service category.
3931      *
3932      * @param eccCategory The emergency service category.
3933      */
3934     @VisibleForTesting
setEmergencyServiceCategory(int eccCategory)3935     public void setEmergencyServiceCategory(int eccCategory) {
3936         mEmergencyServiceCategory = eccCategory;
3937     }
3938 
3939     /**
3940      * @return a {@link List} of {@link String}s that are the emrgency URNs.
3941      */
getEmergencyUrns()3942     public @Nullable List<String> getEmergencyUrns() {
3943         return mEmergencyUrns;
3944     }
3945 
3946     /**
3947      * Set the emergency URNs.
3948      *
3949      * @param emergencyUrns The emergency URNs.
3950      */
3951     @VisibleForTesting
setEmergencyUrns(@ullable List<String> emergencyUrns)3952     public void setEmergencyUrns(@Nullable List<String> emergencyUrns) {
3953         mEmergencyUrns = emergencyUrns;
3954     }
3955 }
3956