• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                         if (!com.android.server.telecom.flags.Flags.enableCallSequencing()) {
1342                             mTelephonyConnectionService.maybeUnholdCallsOnOtherSubs(
1343                                     getPhoneAccountHandle());
1344                         }
1345                         return;
1346                     }
1347                     phone.switchHoldingAndActive();
1348                 }
1349 
1350                 // TODO: Cdma calls are slightly different.
1351             } catch (CallStateException e) {
1352                 Log.e(this, e, "Exception occurred while trying to put call on hold.");
1353             }
1354         } else {
1355             Log.w(this, "Cannot put a call that is not currently active on hold.");
1356         }
1357     }
1358 
performUnhold()1359     public void performUnhold() {
1360         Log.v(this, "performUnhold");
1361         if (Call.State.HOLDING == mConnectionState) {
1362             try {
1363                 Phone phone = mOriginalConnection.getCall().getPhone();
1364                 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
1365                 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
1366                     ImsPhone imsPhone = (ImsPhone) phone;
1367                     imsPhone.unholdHeldCall();
1368                     return;
1369                 }
1370                 // Here's the deal--Telephony hold/unhold is weird because whenever there exists
1371                 // more than one call, one of them must always be active. In other words, if you
1372                 // have an active call and holding call, and you put the active call on hold, it
1373                 // will automatically activate the holding call. This is weird with how Telecom
1374                 // sends its commands. When a user opts to "unhold" a background call, telecom
1375                 // issues hold commands to all active calls, and then the unhold command to the
1376                 // background call. This means that we get two commands...each of which reduces to
1377                 // switchHoldingAndActive(). The result is that they simply cancel each other out.
1378                 // To fix this so that it works well with telecom we add a minor hack. If we
1379                 // have one telephony call, everything works as normally expected. But if we have
1380                 // two or more calls, we will ignore all requests to "unhold" knowing that the hold
1381                 // requests already do what we want. If you've read up to this point, I'm very sorry
1382                 // that we are doing this. I didn't think of a better solution that wouldn't also
1383                 // make the Telecom APIs very ugly.
1384 
1385                 if (!hasMultipleTopLevelCalls()) {
1386                     mOriginalConnection.getCall().getPhone().switchHoldingAndActive();
1387                 } else {
1388                     Log.i(this, "Skipping unhold command for %s", this);
1389                 }
1390             } catch (CallStateException e) {
1391                 Log.e(this, e, "Exception occurred while trying to release call from hold.");
1392             }
1393         } else {
1394             Log.w(this, "Cannot release a call that is not already on hold from hold.");
1395         }
1396     }
1397 
performConference(Connection otherConnection)1398     public void performConference(Connection otherConnection) {
1399         Log.d(this, "performConference - %s", this);
1400         if (getPhone() != null) {
1401             try {
1402                 // We dont use the "other" connection because there is no concept of that in the
1403                 // implementation of calls inside telephony. Basically, you can "conference" and it
1404                 // will conference with the background call.  We know that otherConnection is the
1405                 // background call because it would never have called setConferenceableConnections()
1406                 // otherwise.
1407                 getPhone().conference();
1408             } catch (CallStateException e) {
1409                 Log.e(this, e, "Failed to conference call.");
1410             }
1411         }
1412     }
1413 
getAddConferenceParticipants(List<Uri> participants)1414     private String[] getAddConferenceParticipants(List<Uri> participants) {
1415         String[] addConfParticipants = new String[participants.size()];
1416         int i = 0;
1417         for (Uri participant : participants) {
1418            addConfParticipants[i] = participant.getSchemeSpecificPart();
1419            i++;
1420         }
1421         return addConfParticipants;
1422     }
1423 
performAddConferenceParticipants(List<Uri> participants)1424     public void performAddConferenceParticipants(List<Uri> participants) {
1425         Log.v(this, "performAddConferenceParticipants");
1426         if (mOriginalConnection.getCall() instanceof ImsPhoneCall) {
1427             ImsPhoneCall imsPhoneCall = (ImsPhoneCall)mOriginalConnection.getCall();
1428             try {
1429                 imsPhoneCall.getImsCall().inviteParticipants(
1430                         getAddConferenceParticipants(participants));
1431             } catch(ImsException e) {
1432                 Log.e(this, e, "failed to add conference participants");
1433             }
1434         }
1435     }
1436 
1437     /**
1438      * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
1439      * capabilities.
1440      */
buildConnectionCapabilities()1441     protected int buildConnectionCapabilities() {
1442         int callCapabilities = 0;
1443         if (mOriginalConnection != null && mOriginalConnection.isIncoming()) {
1444             callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO;
1445         }
1446         if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) {
1447             callCapabilities |= CAPABILITY_SUPPORT_HOLD;
1448             if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) {
1449                 callCapabilities |= CAPABILITY_HOLD;
1450             }
1451         }
1452 
1453         Log.d(this, "buildConnectionCapabilities: isHoldable = "
1454                 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities);
1455 
1456         return callCapabilities;
1457     }
1458 
updateConnectionCapabilities()1459     protected final void updateConnectionCapabilities() {
1460         int newCapabilities = buildConnectionCapabilities();
1461 
1462         newCapabilities = applyOriginalConnectionCapabilities(newCapabilities);
1463         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO,
1464                 mIsVideoPauseSupported && isVideoCapable());
1465         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
1466                 isExternalConnection() && isPullable());
1467         newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
1468         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
1469                 isImsConnection() && canDeflectImsCalls());
1470 
1471         newCapabilities = applyAddParticipantCapabilities(newCapabilities);
1472         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER_CONSULTATIVE,
1473                 isImsConnection() && canConsultativeTransfer());
1474         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER,
1475                 isImsConnection() && canTransferToNumber());
1476 
1477         if (getConnectionCapabilities() != newCapabilities) {
1478             setConnectionCapabilities(newCapabilities);
1479             notifyConnectionCapabilitiesChanged(newCapabilities);
1480         }
1481     }
1482 
buildConnectionProperties()1483     protected int buildConnectionProperties() {
1484         int connectionProperties = 0;
1485 
1486         // If the phone is in ECM mode, mark the call to indicate that the callback number should be
1487         // shown.
1488         Phone phone = getPhone();
1489         if (phone != null && phone.isInEcm()) {
1490             connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE;
1491         }
1492 
1493         return connectionProperties;
1494     }
1495 
1496     /**
1497      * Updates the properties of the connection.
1498      */
updateConnectionProperties()1499     protected final void updateConnectionProperties() {
1500         int newProperties = buildConnectionProperties();
1501 
1502         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
1503                 hasHighDefAudioProperty());
1504         newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi() && !isCrossSimCall());
1505         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
1506                 isExternalConnection());
1507         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
1508                 mIsCdmaVoicePrivacyEnabled);
1509         newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING,
1510                 mIsUsingAssistedDialing);
1511         newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
1512         newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
1513                 isNetworkIdentifiedEmergencyCall());
1514         newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE,
1515                 isAdhocConferenceCall());
1516         newProperties = changeBitmask(newProperties, PROPERTY_CROSS_SIM,
1517                 isCrossSimCall());
1518 
1519         if (getConnectionProperties() != newProperties) {
1520             setTelephonyConnectionProperties(newProperties);
1521         }
1522     }
1523 
setTelephonyConnectionProperties(int newProperties)1524     public void setTelephonyConnectionProperties(int newProperties) {
1525         setConnectionProperties(newProperties);
1526         notifyConnectionPropertiesChanged(newProperties);
1527     }
1528 
updateAddress()1529     protected final void updateAddress() {
1530         updateConnectionCapabilities();
1531         updateConnectionProperties();
1532         if (mOriginalConnection != null) {
1533             Uri address;
1534             if (isShowingOriginalDialString()
1535                     && mOriginalConnection.getOrigDialString() != null) {
1536                 address = getAddressFromNumber(mOriginalConnection.getOrigDialString());
1537             } else if (isNeededToFormatIncomingNumberForJp()) {
1538                 address = getAddressFromNumber(
1539                         formatIncomingNumberForJp(mOriginalConnection.getAddress()));
1540             } else {
1541                 address = getAddressFromNumber(mOriginalConnection.getAddress());
1542             }
1543             int presentation = mOriginalConnection.getNumberPresentation();
1544             if (!Objects.equals(address, getAddress()) ||
1545                     presentation != getAddressPresentation()) {
1546                 Log.v(this, "updateAddress, address changed");
1547                 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
1548                     address = null;
1549                 }
1550                 setAddress(address, presentation);
1551             }
1552 
1553             String name = filterCnapName(mOriginalConnection.getCnapName());
1554             int namePresentation = mOriginalConnection.getCnapNamePresentation();
1555             if (!Objects.equals(name, getCallerDisplayName()) ||
1556                     namePresentation != getCallerDisplayNamePresentation()) {
1557                 Log.v(this, "updateAddress, caller display name changed");
1558                 setCallerDisplayName(name, namePresentation);
1559             }
1560 
1561             TelephonyManager tm = (TelephonyManager) getPhone().getContext()
1562                     .getSystemService(Context.TELEPHONY_SERVICE);
1563             if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
1564                 mTreatAsEmergencyCall = true;
1565             }
1566 
1567             // Changing the address of the connection can change whether it is an emergency call or
1568             // not, which can impact whether it can be part of a conference.
1569             refreshConferenceSupported();
1570         }
1571     }
1572 
onRemovedFromCallService()1573     void onRemovedFromCallService() {
1574         // Subclass can override this to do cleanup.
1575     }
1576 
registerForCallEvents(Phone phone)1577     public void registerForCallEvents(Phone phone) {
1578         if (mPhoneForEvents == phone) {
1579             Log.i(this, "registerForCallEvents - same phone requested for"
1580                     + "registration, ignoring.");
1581             return;
1582         }
1583         Log.i(this, "registerForCallEvents; phone=%s", phone);
1584         // Only one Phone should be registered for events at a time.
1585         unregisterForCallEvents();
1586         phone.registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
1587         phone.registerForHandoverStateChanged(mHandler, MSG_HANDOVER_STATE_CHANGED, null);
1588         phone.registerForRedialConnectionChanged(mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
1589         phone.registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
1590         phone.registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
1591         phone.registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
1592         phone.registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
1593         phone.registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null);
1594         mPhoneForEvents = phone;
1595     }
1596 
setOriginalConnection(com.android.internal.telephony.Connection originalConnection)1597     void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
1598         Log.i(this, "setOriginalConnection: TelephonyConnection, originalConnection: "
1599                 + originalConnection);
1600         if (mOriginalConnection != null && originalConnection != null
1601                && !originalConnection.isIncoming()
1602                && originalConnection.getOrigDialString() == null
1603                && isShowingOriginalDialString()) {
1604             Log.i(this, "new original dial string is null, convert to: "
1605                    +  mOriginalConnection.getOrigDialString());
1606             originalConnection.restoreDialedNumberAfterConversion(
1607                     mOriginalConnection.getOrigDialString());
1608         }
1609 
1610         clearOriginalConnection();
1611         mOriginalConnectionExtras.clear();
1612         mOriginalConnection = originalConnection;
1613         mOriginalConnection.setTelecomCallId(getTelecomCallId());
1614         registerForCallEvents(getPhone());
1615 
1616         mOriginalConnection.addPostDialListener(mPostDialListener);
1617         mOriginalConnection.addListener(mOriginalConnectionListener);
1618 
1619         // Set video state and capabilities
1620         setTelephonyVideoState(mOriginalConnection.getVideoState());
1621         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
1622         setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
1623         setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference());
1624         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
1625         setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
1626         setAudioQuality(mOriginalConnection.getAudioQuality());
1627         setTechnologyTypeExtra();
1628 
1629         setCallRadioTech(mOriginalConnection.getCallRadioTech());
1630 
1631         // Post update of extras to the handler; extras are updated via the handler to ensure thread
1632         // safety. The Extras Bundle is cloned in case the original extras are modified while they
1633         // are being added to mOriginalConnectionExtras in updateExtras.
1634         Bundle connExtras = mOriginalConnection.getConnectionExtras();
1635             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null :
1636                     new Bundle(connExtras)).sendToTarget();
1637 
1638         TelephonyManager tm = (TelephonyManager) getPhone().getContext()
1639                 .getSystemService(Context.TELEPHONY_SERVICE);
1640         if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
1641             mTreatAsEmergencyCall = true;
1642         }
1643         // Propagate VERSTAT for IMS calls.
1644         setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus());
1645 
1646         if (isImsConnection()) {
1647             mWasImsConnection = true;
1648         }
1649         if (originalConnection instanceof ImsPhoneConnection) {
1650             maybeConfigureDeviceToDeviceCommunication();
1651         }
1652         mIsMultiParty = mOriginalConnection.isMultiparty();
1653 
1654         Bundle extrasToPut = new Bundle();
1655         // Also stash the number verification status in a hidden extra key in the connection.
1656         // We do this because a RemoteConnection DOES NOT include a getNumberVerificationStatus
1657         // method and we need to be able to pass the number verification status up to Telecom
1658         // despite the missing pathway in the RemoteConnectionService API surface.
1659         extrasToPut.putInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS,
1660                 mOriginalConnection.getNumberVerificationStatus());
1661         List<String> extrasToRemove = new ArrayList<>();
1662         if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
1663             extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
1664         } else {
1665             extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
1666         }
1667 
1668         if (shouldSetDisableAddCallExtra()) {
1669             extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
1670         } else {
1671             extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
1672         }
1673 
1674         if (mOriginalConnection != null) {
1675             ArrayList<String> forwardedNumber = mOriginalConnection.getForwardedNumber();
1676             if (forwardedNumber != null) {
1677                 extrasToPut.putStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER,
1678                         forwardedNumber);
1679             }
1680         }
1681 
1682         putTelephonyExtras(extrasToPut);
1683         removeTelephonyExtras(extrasToRemove);
1684 
1685         // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
1686         // should be executed *after* the above setters have run.
1687         updateState();
1688         if (mOriginalConnection == null) {
1689             Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " +
1690                     originalConnection);
1691         }
1692 
1693         fireOnOriginalConnectionConfigured();
1694     }
1695 
1696     /**
1697      * Filters the CNAP name to not include a list of names that are unhelpful to the user for
1698      * Caller ID purposes.
1699      */
filterCnapName(final String cnapName)1700     private String filterCnapName(final String cnapName) {
1701         if (cnapName == null) {
1702             return null;
1703         }
1704         PersistableBundle carrierConfig = getCarrierConfig();
1705         String[] filteredCnapNames = null;
1706         if (carrierConfig != null) {
1707             filteredCnapNames = carrierConfig.getStringArray(
1708                     CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY);
1709         }
1710         if (filteredCnapNames != null) {
1711             long cnapNameMatches = Arrays.asList(filteredCnapNames)
1712                     .stream()
1713                     .filter(filteredCnapName -> filteredCnapName.equals(
1714                             cnapName.toUpperCase(Locale.ROOT)))
1715                     .count();
1716             if (cnapNameMatches > 0) {
1717                 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName);
1718                 return "";
1719             }
1720         }
1721         return cnapName;
1722     }
1723 
1724     /**
1725      * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom.
1726      */
setTechnologyTypeExtra()1727     private void setTechnologyTypeExtra() {
1728         if (getPhone() != null) {
1729             Bundle newExtras = getExtras();
1730             if (newExtras == null) {
1731                 newExtras = new Bundle();
1732             }
1733             newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
1734             putTelephonyExtras(newExtras);
1735         }
1736     }
1737 
refreshHoldSupported()1738     private void refreshHoldSupported() {
1739        if (mOriginalConnection == null) {
1740            Log.w(this, "refreshHoldSupported org conn is null");
1741            return;
1742        }
1743 
1744        if (!mOriginalConnection.shouldAllowHoldingVideoCall() && canHoldImsCalls() !=
1745                ((getConnectionCapabilities() & (CAPABILITY_HOLD | CAPABILITY_SUPPORT_HOLD)) != 0)) {
1746            updateConnectionCapabilities();
1747        }
1748     }
1749 
refreshDisableAddCall()1750     private void refreshDisableAddCall() {
1751         if (shouldSetDisableAddCallExtra()) {
1752             Bundle newExtras = getExtras();
1753             if (newExtras == null) {
1754                 newExtras = new Bundle();
1755             }
1756             newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
1757             putTelephonyExtras(newExtras);
1758         } else {
1759             removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
1760         }
1761     }
1762 
refreshCodec()1763     private void refreshCodec() {
1764         boolean changed = false;
1765         Bundle newExtras = getExtras();
1766         if (newExtras == null) {
1767             newExtras = new Bundle();
1768         }
1769         int newCodecType;
1770         if (isImsConnection()) {
1771             newCodecType = transformCodec(getOriginalConnection().getAudioCodec());
1772         } else {
1773             // For SRVCC, report AUDIO_CODEC_NONE.
1774             newCodecType = Connection.AUDIO_CODEC_NONE;
1775         }
1776         int oldCodecType = newExtras.getInt(Connection.EXTRA_AUDIO_CODEC,
1777                 Connection.AUDIO_CODEC_NONE);
1778         if (newCodecType != oldCodecType) {
1779             newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
1780             Log.i(this, "refreshCodec: codec changed; old=%d, new=%d", oldCodecType, newCodecType);
1781             changed = true;
1782         }
1783         if (isImsConnection()) {
1784             float newBitrate = getOriginalConnection().getAudioCodecBitrateKbps();
1785             float oldBitrate = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f);
1786             if (Math.abs(newBitrate - oldBitrate) > THRESHOLD) {
1787                 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, newBitrate);
1788                 Log.i(this, "refreshCodec: bitrate changed; old=%f, new=%f", oldBitrate,
1789                         newBitrate);
1790                 changed = true;
1791             }
1792 
1793             float newBandwidth = getOriginalConnection().getAudioCodecBandwidthKhz();
1794             float oldBandwidth = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ,
1795                     0.0f);
1796             if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) {
1797                 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth);
1798                 Log.i(this, "refreshCodec: bandwidth changed; old=%f, new=%f", oldBandwidth,
1799                         newBandwidth);
1800                 changed = true;
1801             }
1802         } else {
1803             ArrayList<String> toRemove = new ArrayList<>();
1804             toRemove.add(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS);
1805             toRemove.add(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ);
1806             removeTelephonyExtras(toRemove);
1807         }
1808 
1809         if (changed) {
1810             Log.i(this, "refreshCodec: Codec:"
1811                     + newExtras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE)
1812                     + ", Bitrate:"
1813                     + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f)
1814                     + ", Bandwidth:"
1815                     + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, 0.0f));
1816             putTelephonyExtras(newExtras);
1817         }
1818     }
1819 
transformCodec(int codec)1820     private int transformCodec(int codec) {
1821         switch (codec) {
1822             case ImsStreamMediaProfile.AUDIO_QUALITY_NONE:
1823                 return Connection.AUDIO_CODEC_NONE;
1824             case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
1825                 return Connection.AUDIO_CODEC_AMR;
1826             case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB:
1827                 return Connection.AUDIO_CODEC_AMR_WB;
1828             case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K:
1829                 return Connection.AUDIO_CODEC_QCELP13K;
1830             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC:
1831                 return Connection.AUDIO_CODEC_EVRC;
1832             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B:
1833                 return Connection.AUDIO_CODEC_EVRC_B;
1834             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB:
1835                 return Connection.AUDIO_CODEC_EVRC_WB;
1836             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW:
1837                 return Connection.AUDIO_CODEC_EVRC_NW;
1838             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR:
1839                 return Connection.AUDIO_CODEC_GSM_EFR;
1840             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR:
1841                 return Connection.AUDIO_CODEC_GSM_FR;
1842             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR:
1843                 return Connection.AUDIO_CODEC_GSM_HR;
1844             case ImsStreamMediaProfile.AUDIO_QUALITY_G711U:
1845                 return Connection.AUDIO_CODEC_G711U;
1846             case ImsStreamMediaProfile.AUDIO_QUALITY_G723:
1847                 return Connection.AUDIO_CODEC_G723;
1848             case ImsStreamMediaProfile.AUDIO_QUALITY_G711A:
1849                 return Connection.AUDIO_CODEC_G711A;
1850             case ImsStreamMediaProfile.AUDIO_QUALITY_G722:
1851                 return Connection.AUDIO_CODEC_G722;
1852             case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB:
1853                 return Connection.AUDIO_CODEC_G711AB;
1854             case ImsStreamMediaProfile.AUDIO_QUALITY_G729:
1855                 return Connection.AUDIO_CODEC_G729;
1856             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB:
1857                 return Connection.AUDIO_CODEC_EVS_NB;
1858             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB:
1859                 return Connection.AUDIO_CODEC_EVS_WB;
1860             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB:
1861                 return Connection.AUDIO_CODEC_EVS_SWB;
1862             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB:
1863                 return Connection.AUDIO_CODEC_EVS_FB;
1864             default:
1865                 return Connection.AUDIO_CODEC_NONE;
1866         }
1867     }
1868 
shouldSetDisableAddCallExtra()1869     private boolean shouldSetDisableAddCallExtra() {
1870         if (mOriginalConnection == null) {
1871             return false;
1872         }
1873         boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall();
1874         if (carrierShouldAllowAddCall) {
1875             return false;
1876         }
1877         Phone phone = getPhone();
1878         if (phone == null) {
1879             return false;
1880         }
1881         boolean isCurrentVideoCall = false;
1882         boolean wasVideoCall = false;
1883         boolean isVowifiEnabled = false;
1884         if (phone instanceof ImsPhone) {
1885             ImsPhoneCall foregroundCall = ((ImsPhone) phone).getForegroundCall();
1886             if (foregroundCall != null) {
1887                 ImsCall call = foregroundCall.getImsCall();
1888                 if (call != null) {
1889                     isCurrentVideoCall = call.isVideoCall();
1890                     wasVideoCall = call.wasVideoCall();
1891                 }
1892             }
1893 
1894             isVowifiEnabled = isWfcEnabled(phone);
1895         }
1896 
1897         if (isCurrentVideoCall) {
1898             return true;
1899         } else if (wasVideoCall && isWifi() && !isVowifiEnabled) {
1900             return true;
1901         }
1902         return false;
1903     }
1904 
hasHighDefAudioProperty()1905     private boolean hasHighDefAudioProperty() {
1906         if (!mHasHighDefAudio) {
1907             return false;
1908         }
1909 
1910         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
1911 
1912         PersistableBundle b = getCarrierConfig();
1913         boolean canWifiCallsBeHdAudio =
1914                 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO);
1915         boolean canVideoCallsBeHdAudio =
1916                 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO);
1917         boolean canGsmCdmaCallsBeHdAudio =
1918                 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO);
1919         boolean shouldDisplayHdAudio =
1920                 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL);
1921 
1922         if (!shouldDisplayHdAudio) {
1923             return false;
1924         }
1925 
1926         if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) {
1927             return false;
1928         }
1929 
1930         if (isVideoCall && !canVideoCallsBeHdAudio) {
1931             return false;
1932         }
1933 
1934         if (isWifi() && !canWifiCallsBeHdAudio) {
1935             return false;
1936         }
1937 
1938         return true;
1939     }
1940 
1941     /**
1942      * @return The address's to which this Connection is currently communicating.
1943      */
getParticipants()1944     public final @Nullable List<Uri> getParticipants() {
1945         return mParticipants;
1946     }
1947 
1948     /**
1949      * Sets the value of the {@link #getParticipants()} property.
1950      *
1951      * @param address The participant address's.
1952      */
setParticipants(@ullable List<Uri> address)1953     public final void setParticipants(@Nullable List<Uri> address) {
1954         mParticipants = address;
1955     }
1956 
1957     /**
1958      * @return true if connection is adhocConference call else false.
1959      */
isAdhocConferenceCall()1960     public final boolean isAdhocConferenceCall() {
1961         return mIsAdhocConferenceCall;
1962     }
1963 
1964     /**
1965      * Sets the value of the {@link #isAdhocConferenceCall()} property.
1966      *
1967      * @param isAdhocConferenceCall represents if the call is adhoc conference call or not.
1968      */
setIsAdhocConferenceCall(boolean isAdhocConferenceCall)1969     public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) {
1970         mIsAdhocConferenceCall = isAdhocConferenceCall;
1971         updateConnectionProperties();
1972     }
1973 
canHoldImsCalls()1974     private boolean canHoldImsCalls() {
1975         PersistableBundle b = getCarrierConfig();
1976         // Return true if the CarrierConfig is unavailable
1977         return (!doesDeviceRespectHoldCarrierConfig() || b == null ||
1978                 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL)) &&
1979                 ((mOriginalConnection != null && mOriginalConnection.shouldAllowHoldingVideoCall())
1980                 || !VideoProfile.isVideo(getVideoState()));
1981     }
1982 
isConferenceHosted()1983     private boolean isConferenceHosted() {
1984         boolean isHosted = false;
1985         if (getTelephonyConnectionService() != null) {
1986             for (Conference current : getTelephonyConnectionService().getAllConferences()) {
1987                 if (current instanceof ImsConference) {
1988                     ImsConference other = (ImsConference) current;
1989                     if (getState() == current.getState()) {
1990                         continue;
1991                     }
1992                     if (other.isConferenceHost()) {
1993                         isHosted = true;
1994                         break;
1995                     }
1996                 }
1997             }
1998         }
1999         return isHosted;
2000     }
2001 
isAddParticipantCapable()2002     private boolean isAddParticipantCapable() {
2003         // not add participant capable for non ims phones
2004         if (getPhone() == null || getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
2005             return false;
2006         }
2007 
2008         if (!getCarrierConfig()
2009                 .getBoolean(CarrierConfigManager.KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL)) {
2010             return false;
2011         }
2012 
2013         boolean isCapable = !mTreatAsEmergencyCall && (mConnectionState == Call.State.ACTIVE ||
2014                 mConnectionState == Call.State.HOLDING);
2015 
2016         // add participant capable if current connection is a host connection or
2017         // if conference is not hosted on the device
2018         isCapable = isCapable && ((mOriginalConnection != null &&
2019                 mOriginalConnection.isConferenceHost()) ||
2020                 !isConferenceHosted());
2021 
2022         /**
2023           * For individual IMS calls, if the extra for remote conference support is
2024           *     - indicated, then consider the same for add participant capability
2025           *     - not indicated, then the add participant capability is same as before.
2026           */
2027         if (isCapable && (mOriginalConnection != null) && !mIsMultiParty) {
2028             // In case OEMs are still using deprecated value, read it and use it as default value.
2029             boolean isCapableFromDeprecatedExtra = mOriginalConnectionExtras.getBoolean(
2030                     ImsCallProfile.EXTRA_CONFERENCE_AVAIL, isCapable);
2031             isCapable = mOriginalConnectionExtras.getBoolean(
2032                     ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED,
2033                     isCapableFromDeprecatedExtra);
2034         }
2035         return isCapable;
2036     }
2037 
2038     /**
2039      * Applies the add participant capabilities to the {@code CallCapabilities} bit-mask.
2040      *
2041      * @param callCapabilities The {@code CallCapabilities} bit-mask.
2042      * @return The capabilities with the add participant capabilities applied.
2043      */
applyAddParticipantCapabilities(int callCapabilities)2044     private int applyAddParticipantCapabilities(int callCapabilities) {
2045         int currentCapabilities = callCapabilities;
2046         if (isAddParticipantCapable()) {
2047             currentCapabilities = changeBitmask(currentCapabilities,
2048                     Connection.CAPABILITY_ADD_PARTICIPANT, true);
2049         } else {
2050             currentCapabilities = changeBitmask(currentCapabilities,
2051                     Connection.CAPABILITY_ADD_PARTICIPANT, false);
2052         }
2053         return currentCapabilities;
2054     }
2055 
2056     @VisibleForTesting
getCarrierConfig()2057     public @NonNull PersistableBundle getCarrierConfig() {
2058         Phone phone = getPhone();
2059         if (phone == null) {
2060             Log.w(this,
2061                     "getCarrierConfig: phone is null. Returning CarrierConfigManager"
2062                             + ".getDefaultConfig()");
2063             return CarrierConfigManager.getDefaultConfig();
2064         }
2065 
2066         // potential null returned from .getCarrierConfigForSubId() and method guarantees non-null.
2067         // hence, need for try/finally block
2068         PersistableBundle pb = null;
2069         try {
2070             pb = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
2071         } catch (Exception e) {
2072             Log.e(this, e,
2073                     "getCarrierConfig: caught Exception when calling "
2074                             + "PhoneGlobals.getCarrierConfigForSubId(phone.getSubId()). Returning "
2075                             + "CarrierConfigManager.getDefaultConfig()");
2076         } finally {
2077             if (pb == null) {
2078                 pb = CarrierConfigManager.getDefaultConfig();
2079             }
2080         }
2081         return pb;
2082     }
2083 
2084     @VisibleForTesting
isRttMergeSupported(@onNull PersistableBundle pb)2085     public boolean isRttMergeSupported(@NonNull PersistableBundle pb) {
2086         return pb.getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
2087     }
2088 
canDeflectImsCalls()2089     private boolean canDeflectImsCalls() {
2090         return getCarrierConfig().getBoolean(
2091                 CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL)
2092                 && isValidRingingCall();
2093     }
2094 
isCallTransferSupported()2095     private boolean isCallTransferSupported() {
2096         return getCarrierConfig().getBoolean(
2097                 CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
2098     }
2099 
canTransfer(TelephonyConnection c)2100     private boolean canTransfer(TelephonyConnection c) {
2101         com.android.internal.telephony.Connection connection = c.getOriginalConnection();
2102         return (connection != null && !connection.isMultiparty()
2103                 && (c.getState() == STATE_ACTIVE || c.getState() == STATE_HOLDING));
2104     }
2105 
canTransferToNumber()2106     private boolean canTransferToNumber() {
2107         if (!isCallTransferSupported()) {
2108             return false;
2109         }
2110         return canTransfer(this);
2111     }
2112 
canConsultativeTransfer()2113     private boolean canConsultativeTransfer() {
2114         if (!isCallTransferSupported()) {
2115             return false;
2116         }
2117         if (!canTransfer(this)) {
2118             return false;
2119         }
2120         boolean canConsultativeTransfer = false;
2121         if (getTelephonyConnectionService() != null) {
2122             for (Connection current : getTelephonyConnectionService().getAllConnections()) {
2123                 if (current != this && current instanceof TelephonyConnection) {
2124                     TelephonyConnection other = (TelephonyConnection) current;
2125                     if (canTransfer(other)) {
2126                         canConsultativeTransfer = true;
2127                         break;
2128                     }
2129                 }
2130             }
2131         }
2132         return canConsultativeTransfer;
2133     }
2134 
2135     /**
2136      * Determines if the device will respect the value of the
2137      * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
2138      *
2139      * @return {@code false} if the device always supports holding IMS calls, {@code true} if it
2140      *      will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if
2141      *      hold is supported.
2142      */
doesDeviceRespectHoldCarrierConfig()2143     private boolean doesDeviceRespectHoldCarrierConfig() {
2144         Phone phone = getPhone();
2145         if (phone == null) {
2146             return true;
2147         }
2148         return phone.getContext().getResources().getBoolean(
2149                 com.android.internal.R.bool.config_device_respects_hold_carrier_config);
2150     }
2151 
2152     /**
2153      * Whether the connection should be treated as an emergency.
2154      * @return {@code true} if the connection should be treated as an emergency call based
2155      * on the number dialed, {@code false} otherwise.
2156      */
shouldTreatAsEmergencyCall()2157     protected boolean shouldTreatAsEmergencyCall() {
2158         return mTreatAsEmergencyCall;
2159     }
2160 
2161     /**
2162      * Sets whether to treat this call as an emergency call or not.
2163      * @param shouldTreatAsEmergencyCall
2164      */
2165     @VisibleForTesting
setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall)2166     public void setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall) {
2167         mTreatAsEmergencyCall = shouldTreatAsEmergencyCall;
2168     }
2169 
2170     /**
2171      * Un-sets the underlying radio connection.
2172      */
clearOriginalConnection()2173     void clearOriginalConnection() {
2174         if (mOriginalConnection != null) {
2175             Log.i(this, "clearOriginalConnection; clearing=%s", mOriginalConnection);
2176             unregisterForCallEvents();
2177             mOriginalConnection.removePostDialListener(mPostDialListener);
2178             mOriginalConnection.removeListener(mOriginalConnectionListener);
2179             mOriginalConnection = null;
2180         }
2181     }
2182 
unregisterForCallEvents()2183     public void unregisterForCallEvents() {
2184         if (mPhoneForEvents == null) return;
2185         mPhoneForEvents.unregisterForPreciseCallStateChanged(mHandler);
2186         mPhoneForEvents.unregisterForRingbackTone(mHandler);
2187         mPhoneForEvents.unregisterForHandoverStateChanged(mHandler);
2188         mPhoneForEvents.unregisterForRedialConnectionChanged(mHandler);
2189         mPhoneForEvents.unregisterForDisconnect(mHandler);
2190         mPhoneForEvents.unregisterForSuppServiceNotification(mHandler);
2191         mPhoneForEvents.unregisterForOnHoldTone(mHandler);
2192         mPhoneForEvents.unregisterForInCallVoicePrivacyOn(mHandler);
2193         mPhoneForEvents.unregisterForInCallVoicePrivacyOff(mHandler);
2194         mPhoneForEvents = null;
2195     }
2196 
2197     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
hangup(int telephonyDisconnectCode)2198     public void hangup(int telephonyDisconnectCode) {
2199         if (mOriginalConnection != null) {
2200             mHangupDisconnectCause = telephonyDisconnectCode;
2201             try {
2202                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
2203                 // connection.hangup(). Without this change, the party originating the call
2204                 // will not get sent to voicemail if the user opts to reject the call.
2205                 if (isValidRingingCall()) {
2206                     Call call = getCall();
2207                     if (call != null) {
2208                         call.hangup();
2209                     } else {
2210                         Log.w(this, "Attempting to hangup a connection without backing call.");
2211                     }
2212                 } else {
2213                     // We still prefer to call connection.hangup() for non-ringing calls
2214                     // in order to support hanging-up specific calls within a conference call.
2215                     // If we invoked call.hangup() while in a conference, we would end up
2216                     // hanging up the entire conference call instead of the specific connection.
2217                     mOriginalConnection.hangup();
2218                 }
2219             } catch (CallStateException e) {
2220                 Log.e(this, e, "Call to Connection.hangup failed with exception");
2221             }
2222         } else {
2223             mTelephonyConnectionService.onLocalHangup(this);
2224             if (getState() == STATE_DISCONNECTED) {
2225                 Log.i(this, "hangup called on an already disconnected call!");
2226                 close();
2227             } else {
2228                 // There are a few cases where mOriginalConnection has not been set yet. For
2229                 // example, when the radio has to be turned on to make an emergency call,
2230                 // mOriginalConnection could not be set for many seconds.
2231                 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
2232                         android.telephony.DisconnectCause.LOCAL,
2233                         "Local Disconnect before connection established."));
2234                 close();
2235             }
2236         }
2237     }
2238 
reject(@ndroid.telecom.Call.RejectReason int rejectReason)2239     protected void reject(@android.telecom.Call.RejectReason int rejectReason) {
2240         if (mOriginalConnection != null) {
2241             mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED;
2242             try {
2243                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
2244                 // connection.hangup(). Without this change, the party originating the call
2245                 // will not get sent to voicemail if the user opts to reject the call.
2246                 if (isValidRingingCall()) {
2247                     Call call = getCall();
2248                     if (call != null) {
2249                         call.hangup(rejectReason);
2250                     } else {
2251                         Log.w(this, "Attempting to hangup a connection without backing call.");
2252                     }
2253                 } else {
2254                     // We still prefer to call connection.hangup() for non-ringing calls
2255                     // in order to support hanging-up specific calls within a conference call.
2256                     // If we invoked call.hangup() while in a conference, we would end up
2257                     // hanging up the entire conference call instead of the specific connection.
2258                     mOriginalConnection.hangup();
2259                 }
2260             } catch (CallStateException e) {
2261                 Log.e(this, e, "Call to Connection.hangup failed with exception");
2262             }
2263         } else {
2264             if (getState() == STATE_DISCONNECTED) {
2265                 Log.i(this, "hangup called on an already disconnected call!");
2266                 close();
2267             } else {
2268                 // There are a few cases where mOriginalConnection has not been set yet. For
2269                 // example, when the radio has to be turned on to make an emergency call,
2270                 // mOriginalConnection could not be set for many seconds.
2271                 setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
2272                         android.telephony.DisconnectCause.LOCAL,
2273                         "Local Disconnect before connection established."));
2274                 close();
2275             }
2276         }
2277     }
2278 
getOriginalConnection()2279     com.android.internal.telephony.Connection getOriginalConnection() {
2280         return mOriginalConnection;
2281     }
2282 
getCall()2283     protected Call getCall() {
2284         if (mOriginalConnection != null) {
2285             return mOriginalConnection.getCall();
2286         }
2287         return null;
2288     }
2289 
getPhone()2290     Phone getPhone() {
2291         Call call = getCall();
2292         if (call != null) {
2293             return call.getPhone();
2294         }
2295         return null;
2296     }
2297 
hasMultipleTopLevelCalls()2298     private boolean hasMultipleTopLevelCalls() {
2299         int numCalls = 0;
2300         Phone phone = getPhone();
2301         if (phone != null) {
2302             if (!phone.getRingingCall().isIdle()) {
2303                 numCalls++;
2304             }
2305             if (!phone.getForegroundCall().isIdle()) {
2306                 numCalls++;
2307             }
2308             if (!phone.getBackgroundCall().isIdle()) {
2309                 numCalls++;
2310             }
2311         }
2312         return numCalls > 1;
2313     }
2314 
getForegroundConnection()2315     private com.android.internal.telephony.Connection getForegroundConnection() {
2316         if (getPhone() != null) {
2317             return getPhone().getForegroundCall().getEarliestConnection();
2318         }
2319         return null;
2320     }
2321 
2322      /**
2323      * Checks for and returns the list of conference participants
2324      * associated with this connection.
2325      */
getConferenceParticipants()2326     public List<ConferenceParticipant> getConferenceParticipants() {
2327         if (mOriginalConnection == null) {
2328             Log.w(this, "Null mOriginalConnection, cannot get conf participants.");
2329             return null;
2330         }
2331         return mOriginalConnection.getConferenceParticipants();
2332     }
2333 
2334     /**
2335      * Checks to see the original connection corresponds to an active incoming call. Returns false
2336      * if there is no such actual call, or if the associated call is not incoming (See
2337      * {@link Call.State#isRinging}).
2338      */
isValidRingingCall()2339     private boolean isValidRingingCall() {
2340         if (getPhone() == null) {
2341             Log.v(this, "isValidRingingCall, phone is null");
2342             return false;
2343         }
2344 
2345         Call ringingCall = getPhone().getRingingCall();
2346         if (!ringingCall.getState().isRinging()) {
2347             Log.v(this, "isValidRingingCall, ringing call is not in ringing state");
2348             return false;
2349         }
2350 
2351         if (ringingCall.getEarliestConnection() != mOriginalConnection) {
2352             Log.v(this, "isValidRingingCall, ringing call connection does not match");
2353             return false;
2354         }
2355 
2356         Log.v(this, "isValidRingingCall, returning true");
2357         return true;
2358     }
2359 
2360     // Make sure the extras being passed into this method is a COPY of the original extras Bundle.
2361     // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll
2362     // below.
updateExtras(Bundle extras)2363     protected void updateExtras(Bundle extras) {
2364         if (mOriginalConnection != null) {
2365             if (extras != null) {
2366                 // Check if extras have changed and need updating.
2367                 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) {
2368                     if (Log.DEBUG) {
2369                         Log.d(TelephonyConnection.this, "Updating extras:");
2370                         for (String key : extras.keySet()) {
2371                             Object value = extras.get(key);
2372                             if (value instanceof String) {
2373                                 Log.d(this, "updateExtras Key=" + Rlog.pii(LOG_TAG, key)
2374                                         + " value=" + Rlog.pii(LOG_TAG, value));
2375                             }
2376                         }
2377                     }
2378                     mOriginalConnectionExtras.clear();
2379 
2380                     mOriginalConnectionExtras.putAll(extras);
2381 
2382                     // Remap any string extras that have a remapping defined.
2383                     for (String key : mOriginalConnectionExtras.keySet()) {
2384                         if (sExtrasMap.containsKey(key)) {
2385                             String newKey = sExtrasMap.get(key);
2386                             mOriginalConnectionExtras.putString(newKey, extras.getString(key));
2387                             mOriginalConnectionExtras.remove(key);
2388                         }
2389                     }
2390 
2391                     // Ensure extras are propagated to Telecom.
2392                     putTelephonyExtras(mOriginalConnectionExtras);
2393                     // If extras contain Conference support information,
2394                     // then ensure capabilities are updated.
2395                     if (mOriginalConnectionExtras.containsKey(
2396                             ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED)
2397                             || mOriginalConnectionExtras.containsKey(
2398                                 ImsCallProfile.EXTRA_CONFERENCE_AVAIL)) {
2399                         updateConnectionCapabilities();
2400                     }
2401                     // If extras contain or contained Cross Sim information,
2402                     // then ensure connection properties are updated and propagated to Telecom.
2403                     // Also, update the status hints in the case the call has
2404                     // has moved from cross sim call back to wifi
2405                     mWasCrossSim |= mOriginalConnectionExtras.containsKey(
2406                                 ImsCallProfile.EXTRA_IS_CROSS_SIM_CALL);
2407                     if (mWasCrossSim) {
2408                         updateStatusHints();
2409                         updateConnectionProperties();
2410                     }
2411                 } else {
2412                     Log.d(this, "Extras update not required");
2413                 }
2414             } else {
2415                 Log.d(this, "updateExtras extras: " + Rlog.pii(LOG_TAG, extras));
2416             }
2417         }
2418     }
2419 
areBundlesEqual(Bundle extras, Bundle newExtras)2420     private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
2421         if (extras == null || newExtras == null) {
2422             return extras == newExtras;
2423         }
2424 
2425         if (extras.size() != newExtras.size()) {
2426             return false;
2427         }
2428 
2429         for(String key : extras.keySet()) {
2430             if (key != null) {
2431                 final Object value = extras.get(key);
2432                 final Object newValue = newExtras.get(key);
2433                 if (!Objects.equals(value, newValue)) {
2434                     return false;
2435                 }
2436             }
2437         }
2438         return true;
2439     }
2440 
setStateOverride(Call.State state)2441     void setStateOverride(Call.State state) {
2442         mIsStateOverridden = true;
2443         mConnectionOverriddenState = state;
2444         // Need to keep track of the original connection's state before override.
2445         mOriginalConnectionState = mOriginalConnection.getState();
2446         updateStateInternal();
2447     }
2448 
resetStateOverride()2449     void resetStateOverride() {
2450         mIsStateOverridden = false;
2451         updateStateInternal();
2452     }
2453 
updateStateInternal()2454     void updateStateInternal() {
2455         if (mOriginalConnection == null) {
2456             return;
2457         }
2458         Call.State newState;
2459         // If the state is overridden and the state of the original connection hasn't changed since,
2460         // then we continue in the overridden state, else we go to the original connection's state.
2461         if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) {
2462             newState = mConnectionOverriddenState;
2463         } else {
2464             newState = mOriginalConnection.getState();
2465         }
2466         int cause = mOriginalConnection.getDisconnectCause();
2467         Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState,
2468                 getTelecomCallId());
2469 
2470         if (mConnectionState != newState) {
2471             mConnectionState = newState;
2472             switch (newState) {
2473                 case IDLE:
2474                     break;
2475                 case ACTIVE:
2476                     setActiveInternal();
2477                     break;
2478                 case HOLDING:
2479                     setTelephonyConnectionOnHold();
2480                     break;
2481                 case DIALING:
2482                 case ALERTING:
2483                     if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
2484                         setTelephonyConnectionPulling();
2485                     } else {
2486                         setTelephonyConnectionDialing();
2487                     }
2488                     break;
2489                 case INCOMING:
2490                 case WAITING:
2491                     setTelephonyConnectionRinging();
2492                     break;
2493                 case DISCONNECTED:
2494                     if (mTelephonyConnectionService != null) {
2495                         ImsReasonInfo reasonInfo = null;
2496                         if (isImsConnection()) {
2497                             ImsPhoneConnection imsPhoneConnection =
2498                                     (ImsPhoneConnection) mOriginalConnection;
2499                             reasonInfo = imsPhoneConnection.getImsReasonInfo();
2500                             if (reasonInfo != null) {
2501                                 int reasonCode = reasonInfo.getCode();
2502                                 int extraCode = reasonInfo.getExtraCode();
2503                                 if ((reasonCode == CODE_SIP_ALTERNATE_EMERGENCY_CALL)
2504                                         || (reasonCode == CODE_LOCAL_CALL_CS_RETRY_REQUIRED
2505                                                 && extraCode == EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
2506                                     EmergencyNumber numberInfo =
2507                                             imsPhoneConnection.getEmergencyNumberInfo();
2508                                     if (numberInfo != null) {
2509                                         mEmergencyServiceCategory =
2510                                                 numberInfo.getEmergencyServiceCategoryBitmask();
2511                                         mEmergencyUrns = numberInfo.getEmergencyUrns();
2512                                     } else {
2513                                         Log.i(this, "mEmergencyServiceCategory no EmergencyNumber");
2514                                     }
2515 
2516                                     if (mEmergencyServiceCategory != null) {
2517                                         Log.i(this, "mEmergencyServiceCategory="
2518                                                 + mEmergencyServiceCategory);
2519                                     }
2520                                     if (mEmergencyUrns != null) {
2521                                         Log.i(this, "mEmergencyUrns=" + mEmergencyUrns);
2522                                     }
2523                                 }
2524                             }
2525                         }
2526 
2527                         if (mTelephonyConnectionService.maybeReselectDomain(this, reasonInfo,
2528                                 mShowPreciseFailedCause, mHangupDisconnectCause)) {
2529                             clearOriginalConnection();
2530                             break;
2531                         }
2532                     }
2533 
2534                     if (shouldTreatAsEmergencyCall()
2535                             && (cause
2536                             == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
2537                             || cause
2538                             == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) {
2539                         // We can get into a situation where the radio wants us to redial the
2540                         // same emergency call on the other available slot. This will not set
2541                         // the state to disconnected and will instead tell the
2542                         // TelephonyConnectionService to
2543                         // create a new originalConnection using the new Slot.
2544                         fireOnOriginalConnectionRetryDial(cause
2545                                 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE);
2546                     } else {
2547                         int preciseDisconnectCause = CallFailCause.NOT_VALID;
2548                         if (mShowPreciseFailedCause) {
2549                             preciseDisconnectCause =
2550                                     mOriginalConnection.getPreciseDisconnectCause();
2551                         }
2552                         int disconnectCause = mOriginalConnection.getDisconnectCause();
2553                         if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID)
2554                                 && (mHangupDisconnectCause != disconnectCause)) {
2555                             Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause
2556                                     + " -> " + mHangupDisconnectCause);
2557                             disconnectCause = mHangupDisconnectCause;
2558                         }
2559                         ImsReasonInfo imsReasonInfo = null;
2560                         if (isImsConnection()) {
2561                             ImsPhoneConnection imsPhoneConnection =
2562                                     (ImsPhoneConnection) mOriginalConnection;
2563                             imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
2564                         }
2565                         setTelephonyConnectionDisconnected(
2566                                 DisconnectCauseUtil.toTelecomDisconnectCause(
2567                                         disconnectCause,
2568                                         preciseDisconnectCause,
2569                                         mOriginalConnection.getVendorDisconnectCause(),
2570                                         getPhone().getPhoneId(), imsReasonInfo,
2571                                         new FlagsAdapterImpl(),
2572                                         shouldTreatAsEmergencyCall()));
2573                         close();
2574                     }
2575                     break;
2576                 case DISCONNECTING:
2577                     break;
2578             }
2579 
2580             if (mCommunicator != null) {
2581                 mCommunicator.onStateChanged(getTelecomCallId(), getState());
2582             }
2583         }
2584     }
2585 
updateState()2586     void updateState() {
2587         if (mOriginalConnection == null) {
2588             return;
2589         }
2590 
2591         updateStateInternal();
2592         updateStatusHints();
2593         updateConnectionCapabilities();
2594         updateConnectionProperties();
2595         updateAddress();
2596         updateMultiparty();
2597         refreshDisableAddCall();
2598         refreshCodec();
2599     }
2600 
2601     /**
2602      * Checks for changes to the multiparty bit.  If a conference has started, informs listeners.
2603      */
updateMultiparty()2604     private void updateMultiparty() {
2605         if (mOriginalConnection == null) {
2606             return;
2607         }
2608 
2609         if (mIsMultiParty != mOriginalConnection.isMultiparty()) {
2610             mIsMultiParty = mOriginalConnection.isMultiparty();
2611 
2612             if (mIsMultiParty) {
2613                 notifyConferenceStarted();
2614             }
2615         }
2616     }
2617 
2618     /**
2619      * Handles a failure when merging calls into a conference.
2620      * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()}
2621      * listener.
2622      */
handleConferenceMergeFailed()2623     private void handleConferenceMergeFailed(){
2624         mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget();
2625     }
2626 
2627     /**
2628      * Handles requests to update the multiparty state received via the
2629      * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)}
2630      * listener.
2631      * <p>
2632      * Note: We post this to the mHandler to ensure that if a conference must be created as a
2633      * result of the multiparty state change, the conference creation happens on the correct
2634      * thread.  This ensures that the thread check in
2635      * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)}
2636      * does not fire.
2637      *
2638      * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise.
2639      */
handleMultipartyStateChange(boolean isMultiParty)2640     private void handleMultipartyStateChange(boolean isMultiParty) {
2641         Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
2642         mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget();
2643     }
2644 
setActiveInternal()2645     private void setActiveInternal() {
2646         if (getState() == STATE_ACTIVE) {
2647             Log.w(this, "Should not be called if this is already ACTIVE");
2648             return;
2649         }
2650 
2651         // When we set a call to active, we need to make sure that there are no other active
2652         // calls. However, the ordering of state updates to connections can be non-deterministic
2653         // since all connections register for state changes on the phone independently.
2654         // To "optimize", we check here to see if there already exists any active calls.  If so,
2655         // we issue an update for those calls first to make sure we only have one top-level
2656         // active call.
2657         if (getTelephonyConnectionService() != null) {
2658             for (Connection current : getTelephonyConnectionService().getAllConnections()) {
2659                 if (current != this && current instanceof TelephonyConnection) {
2660                     TelephonyConnection other = (TelephonyConnection) current;
2661                     if (other.getState() == STATE_ACTIVE) {
2662                         other.updateState();
2663                     }
2664                 }
2665             }
2666         }
2667         setTelephonyConnectionActive();
2668     }
2669 
close()2670     public void close() {
2671         Log.v(this, "close");
2672         clearOriginalConnection();
2673         destroy();
2674         if (mTelephonyConnectionService != null) {
2675             removeTelephonyConnectionListener(
2676                     mTelephonyConnectionService.getTelephonyConnectionListener());
2677         }
2678         notifyDestroyed();
2679     }
2680 
2681     /**
2682      * Determines if the current connection is video capable.
2683      *
2684      * A connection is deemed to be video capable if the original connection capabilities state that
2685      * both local and remote video is supported.
2686      *
2687      * @return {@code true} if the connection is video capable, {@code false} otherwise.
2688      */
isVideoCapable()2689     private boolean isVideoCapable() {
2690         return (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
2691                 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL
2692                 && (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
2693                 == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL;
2694     }
2695 
2696     /**
2697      * Determines if the current connection is an external connection.
2698      *
2699      * A connection is deemed to be external if the original connection capabilities state that it
2700      * is.
2701      *
2702      * @return {@code true} if the connection is external, {@code false} otherwise.
2703      */
isExternalConnection()2704     private boolean isExternalConnection() {
2705         return (mOriginalConnectionCapabilities
2706                 & Capability.IS_EXTERNAL_CONNECTION) == Capability.IS_EXTERNAL_CONNECTION;
2707     }
2708 
2709     /**
2710      * Determines if the current connection has RTT enabled.
2711      */
isRtt()2712     private boolean isRtt() {
2713         return mOriginalConnection != null
2714                 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
2715                 && mOriginalConnection instanceof ImsPhoneConnection
2716                 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall();
2717     }
2718 
2719     /**
2720      * Determines if the current connection is cross sim calling
2721      */
isCrossSimCall()2722     private boolean isCrossSimCall() {
2723         return mOriginalConnection != null
2724                 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
2725                 && mOriginalConnection instanceof ImsPhoneConnection
2726                 && ((ImsPhoneConnection) mOriginalConnection).isCrossSimCall();
2727     }
2728 
2729     /**
2730      * Determines if the current connection is pullable.
2731      *
2732      * A connection is deemed to be pullable if the original connection capabilities state that it
2733      * is.
2734      *
2735      * @return {@code true} if the connection is pullable, {@code false} otherwise.
2736      */
isPullable()2737     private boolean isPullable() {
2738         return (mOriginalConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION)
2739                 == Capability.IS_EXTERNAL_CONNECTION
2740                 && (mOriginalConnectionCapabilities & Capability.IS_PULLABLE)
2741                 == Capability.IS_PULLABLE;
2742     }
2743 
2744     /**
2745      * Sets whether or not CDMA enhanced call privacy is enabled for this connection.
2746      */
setCdmaVoicePrivacy(boolean isEnabled)2747     private void setCdmaVoicePrivacy(boolean isEnabled) {
2748         if(mIsCdmaVoicePrivacyEnabled != isEnabled) {
2749             mIsCdmaVoicePrivacyEnabled = isEnabled;
2750             updateConnectionProperties();
2751         }
2752     }
2753 
2754     /**
2755      * Applies capabilities specific to conferences termination to the
2756      * {@code ConnectionCapabilities} bit-mask.
2757      *
2758      * @param capabilities The {@code ConnectionCapabilities} bit-mask.
2759      * @return The capabilities with the IMS conference capabilities applied.
2760      */
applyConferenceTerminationCapabilities(int capabilities)2761     private int applyConferenceTerminationCapabilities(int capabilities) {
2762         int currentCapabilities = capabilities;
2763 
2764         // An IMS call cannot be individually disconnected or separated from its parent conference.
2765         // If the call was IMS, even if it hands over to GMS, these capabilities are not supported.
2766         if (!mWasImsConnection) {
2767             currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE;
2768             currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE;
2769         }
2770 
2771         return currentCapabilities;
2772     }
2773 
2774     /**
2775      * Stores the new original connection capabilities, and applies them to the current connection,
2776      * notifying any listeners as necessary.
2777      *
2778      * @param connectionCapabilities The original connection capabilties.
2779      */
setOriginalConnectionCapabilities(int connectionCapabilities)2780     public void setOriginalConnectionCapabilities(int connectionCapabilities) {
2781         mOriginalConnectionCapabilities = connectionCapabilities;
2782         updateConnectionCapabilities();
2783         updateConnectionProperties();
2784     }
2785 
2786     /**
2787      * Called to apply the capabilities present in the {@link #mOriginalConnection} to this
2788      * {@link Connection}.  Provides a mapping between the capabilities present in the original
2789      * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in
2790      * this {@link Connection}.
2791      *
2792      * @param capabilities The capabilities bitmask from the {@link Connection}.
2793      * @return the capabilities bitmask with the original connection capabilities remapped and
2794      *      applied.
2795      */
applyOriginalConnectionCapabilities(int capabilities)2796     public int applyOriginalConnectionCapabilities(int capabilities) {
2797         // We only support downgrading to audio if both the remote and local side support
2798         // downgrading to audio.
2799         int supportsDowngrade = Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL
2800                 | Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE;
2801         boolean supportsDowngradeToAudio =
2802                 (mOriginalConnectionCapabilities & supportsDowngrade) == supportsDowngrade;
2803         capabilities = changeBitmask(capabilities,
2804                 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
2805 
2806         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
2807                 (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
2808                         == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
2809 
2810         boolean isLocalVideoSupported = (mOriginalConnectionCapabilities
2811                 & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
2812                 == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled;
2813         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
2814                 isLocalVideoSupported);
2815 
2816         capabilities = changeBitmask(capabilities, CAPABILITY_REMOTE_PARTY_SUPPORTS_RTT,
2817                 (mOriginalConnectionCapabilities & Capability.SUPPORTS_RTT_REMOTE)
2818                 == Capability.SUPPORTS_RTT_REMOTE);
2819 
2820         return capabilities;
2821     }
2822 
2823     /**
2824      * Whether the call is using wifi.
2825      */
isWifi()2826     boolean isWifi() {
2827         return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
2828     }
2829 
2830     /**
2831      * Sets whether this call has been identified by the network as an emergency call.
2832      * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call
2833      * as an emergency call, {@code false} otherwise.
2834      */
setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)2835     public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) {
2836         Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, "
2837                 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(),
2838                 isNetworkIdentifiedEmergencyCall);
2839         mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall;
2840         updateConnectionProperties();
2841     }
2842 
2843     /**
2844      * @return {@code true} if the network has identified this call as an emergency call,
2845      * {@code false} otherwise.
2846      */
isNetworkIdentifiedEmergencyCall()2847     public boolean isNetworkIdentifiedEmergencyCall() {
2848         return mIsNetworkIdentifiedEmergencyCall;
2849     }
2850 
2851     /**
2852      * @return {@code true} if this is an outgoing call, {@code false} otherwise.
2853      */
isOutgoingCall()2854     public boolean isOutgoingCall() {
2855         return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING;
2856     }
2857 
2858     /**
2859      * Sets the current call audio quality. Used during rebuild of the properties
2860      * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
2861      *
2862      * @param audioQuality The audio quality.
2863      */
setAudioQuality(int audioQuality)2864     public void setAudioQuality(int audioQuality) {
2865         mHasHighDefAudio = audioQuality ==
2866                 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION;
2867         updateConnectionProperties();
2868     }
2869 
resetStateForConference()2870     void resetStateForConference() {
2871         if (getState() == Connection.STATE_HOLDING) {
2872             resetStateOverride();
2873         }
2874     }
2875 
setHoldingForConference()2876     boolean setHoldingForConference() {
2877         if (getState() == Connection.STATE_ACTIVE) {
2878             setStateOverride(Call.State.HOLDING);
2879             return true;
2880         }
2881         return false;
2882     }
2883 
setRttTextStream(RttTextStream s)2884     public void setRttTextStream(RttTextStream s) {
2885         mRttTextStream = s;
2886     }
2887 
getRttTextStream()2888     public RttTextStream getRttTextStream() {
2889         return mRttTextStream;
2890     }
2891 
2892     /**
2893      * For video calls, sets whether this connection supports pausing the outgoing video for the
2894      * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
2895      *
2896      * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise.
2897      */
setVideoPauseSupported(boolean isVideoPauseSupported)2898     public void setVideoPauseSupported(boolean isVideoPauseSupported) {
2899         mIsVideoPauseSupported = isVideoPauseSupported;
2900     }
2901 
2902     /**
2903      * @return {@code true} if this connection supports pausing the outgoing video using the
2904      * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
2905      */
getVideoPauseSupported()2906     public boolean getVideoPauseSupported() {
2907         return mIsVideoPauseSupported;
2908     }
2909 
2910     /**
2911      * Sets whether this connection supports conference calling.
2912      * @param isConferenceSupported {@code true} if conference calling is supported by this
2913      *                                         connection, {@code false} otherwise.
2914      */
setConferenceSupported(boolean isConferenceSupported)2915     public void setConferenceSupported(boolean isConferenceSupported) {
2916         mIsConferenceSupported = isConferenceSupported;
2917     }
2918 
2919     /**
2920      * @return {@code true} if this connection supports merging calls into a conference.
2921      */
isConferenceSupported()2922     public boolean isConferenceSupported() {
2923         return mIsConferenceSupported;
2924     }
2925 
2926     /**
2927      * Sets whether managing conference call is supported after this connection being a part of a
2928      * Ims conference.
2929      *
2930      * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is
2931      *        supported after this connection being a part of a IMS conference,
2932      *        {@code false} otherwise.
2933      */
setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)2934     public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) {
2935         mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported;
2936     }
2937 
2938     /**
2939      * @return {@code true} if manage conference calling is supported after this connection being a
2940      * part of a IMS conference.
2941      */
isManageImsConferenceCallSupported()2942     public boolean isManageImsConferenceCallSupported() {
2943         return mIsManageImsConferenceCallSupported;
2944     }
2945 
2946     /**
2947      * Sets whether this connection supports showing precise call disconnect cause.
2948      * @param showPreciseFailedCause  {@code true} if showing precise call
2949      * disconnect cause is supported by this connection, {@code false} otherwise.
2950      */
setShowPreciseFailedCause(boolean showPreciseFailedCause)2951     public void setShowPreciseFailedCause(boolean showPreciseFailedCause) {
2952         mShowPreciseFailedCause = showPreciseFailedCause;
2953     }
2954 
2955     /**
2956      * Sets whether TTY is enabled or not.
2957      * @param isTtyEnabled
2958      */
setTtyEnabled(boolean isTtyEnabled)2959     public void setTtyEnabled(boolean isTtyEnabled) {
2960         mIsTtyEnabled = isTtyEnabled;
2961         updateConnectionCapabilities();
2962     }
2963 
2964     /**
2965      * Whether the original connection is an IMS connection.
2966      * @return {@code True} if the original connection is an IMS connection, {@code false}
2967      *     otherwise.
2968      */
isImsConnection()2969     protected boolean isImsConnection() {
2970         com.android.internal.telephony.Connection originalConnection = getOriginalConnection();
2971 
2972         return originalConnection != null
2973                 && originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
2974                 && originalConnection instanceof ImsPhoneConnection;
2975     }
2976 
2977     /**
2978      * Whether the original connection is an GSM/CDMA connection.
2979      * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false}
2980      *     otherwise.
2981      */
isGsmCdmaConnection()2982     protected boolean isGsmCdmaConnection() {
2983         Phone phone = getPhone();
2984         if (phone != null) {
2985             switch (phone.getPhoneType()) {
2986                 case PhoneConstants.PHONE_TYPE_GSM:
2987                 case PhoneConstants.PHONE_TYPE_CDMA:
2988                     return true;
2989                 default:
2990                     return false;
2991             }
2992         }
2993         return false;
2994     }
2995 
2996     /**
2997      * Whether the original connection was ever an IMS connection, either before or now.
2998      * @return {@code True} if the original connection was ever an IMS connection, {@code false}
2999      *     otherwise.
3000      */
wasImsConnection()3001     public boolean wasImsConnection() {
3002         return mWasImsConnection;
3003     }
3004 
getIsUsingAssistedDialing()3005     boolean getIsUsingAssistedDialing() {
3006         return mIsUsingAssistedDialing;
3007     }
3008 
setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)3009     void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) {
3010         mIsUsingAssistedDialing = isUsingAssistedDialing;
3011         updateConnectionProperties();
3012     }
3013 
getAddressFromNumber(String number)3014     private static Uri getAddressFromNumber(String number) {
3015         // Address can be null for blocked calls.
3016         if (number == null) {
3017             number = "";
3018         }
3019         return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
3020     }
3021 
3022     /**
3023      * Changes a capabilities bit-mask to add or remove a capability.
3024      *
3025      * @param bitmask The bit-mask.
3026      * @param bitfield The bit-field to change.
3027      * @param enabled Whether the bit-field should be set or removed.
3028      * @return The bit-mask with the bit-field changed.
3029      */
changeBitmask(int bitmask, int bitfield, boolean enabled)3030     private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
3031         if (enabled) {
3032             return bitmask | bitfield;
3033         } else {
3034             return bitmask & ~bitfield;
3035         }
3036     }
3037 
updateStatusHints()3038     private void updateStatusHints() {
3039         if (isWifi() && !isCrossSimCall() && getPhone() != null) {
3040             int labelId = isValidRingingCall()
3041                     ? R.string.status_hint_label_incoming_wifi_call
3042                     : R.string.status_hint_label_wifi_call;
3043 
3044             Context context = getPhone().getContext();
3045             setTelephonyStatusHints(new StatusHints(
3046                     getResourceString(labelId),
3047                     Icon.createWithResource(
3048                             context, R.drawable.ic_signal_wifi_4_bar_24dp),
3049                     null /* extras */));
3050         } else {
3051             setTelephonyStatusHints(null);
3052         }
3053     }
3054 
3055     /**
3056      * Register a listener for {@link TelephonyConnection} specific triggers.
3057      * @param l The instance of the listener to add
3058      * @return The connection being listened to
3059      */
addTelephonyConnectionListener(TelephonyConnectionListener l)3060     public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) {
3061         mTelephonyListeners.add(l);
3062         // If we already have an original connection, let's call back immediately.
3063         // This would be the case for incoming calls.
3064         if (mOriginalConnection != null) {
3065             fireOnOriginalConnectionConfigured();
3066         }
3067         return this;
3068     }
3069 
3070     /**
3071      * Remove a listener for {@link TelephonyConnection} specific triggers.
3072      * @param l The instance of the listener to remove
3073      * @return The connection being listened to
3074      */
removeTelephonyConnectionListener( TelephonyConnectionListener l)3075     public final TelephonyConnection removeTelephonyConnectionListener(
3076             TelephonyConnectionListener l) {
3077         if (l != null) {
3078             mTelephonyListeners.remove(l);
3079         }
3080         return this;
3081     }
3082 
3083     @Override
setHoldable(boolean isHoldable)3084     public void setHoldable(boolean isHoldable) {
3085         mIsHoldable = isHoldable;
3086         updateConnectionCapabilities();
3087     }
3088 
3089     @Override
isChildHoldable()3090     public boolean isChildHoldable() {
3091         return getConference() != null;
3092     }
3093 
isHoldable()3094     public boolean isHoldable() {
3095         return mIsHoldable;
3096     }
3097 
3098     /**
3099      * Fire a callback to the various listeners for when the original connection is
3100      * set in this {@link TelephonyConnection}
3101      */
fireOnOriginalConnectionConfigured()3102     private final void fireOnOriginalConnectionConfigured() {
3103         for (TelephonyConnectionListener l : mTelephonyListeners) {
3104             l.onOriginalConnectionConfigured(this);
3105         }
3106     }
3107 
fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)3108     private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) {
3109         for (TelephonyConnectionListener l : mTelephonyListeners) {
3110             l.onOriginalConnectionRetry(this, isPermanentFailure);
3111         }
3112     }
3113 
3114     /**
3115      * Handles exiting ECM mode.
3116      */
handleExitedEcmMode()3117     protected void handleExitedEcmMode() {
3118         updateConnectionProperties();
3119     }
3120 
3121     /**
3122      * Determines whether the connection supports conference calling.  A connection supports
3123      * conference calling if it:
3124      * 1. Is not an emergency call.
3125      * 2. Carrier supports conference calls.
3126      * 3. If call is a video call, carrier supports video conference calls.
3127      * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
3128      */
3129     @VisibleForTesting
refreshConferenceSupported()3130     void refreshConferenceSupported() {
3131         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
3132         Phone phone = getPhone();
3133         if (phone == null) {
3134             Log.w(this, "refreshConferenceSupported = false; phone is null");
3135             if (isConferenceSupported()) {
3136                 setConferenceSupported(false);
3137                 notifyConferenceSupportedChanged(false);
3138             }
3139             return;
3140         }
3141 
3142         boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
3143         boolean isVoWifiEnabled = false;
3144         if (isIms) {
3145             isVoWifiEnabled = isWfcEnabled(phone);
3146         }
3147         PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
3148                 .makePstnPhoneAccountHandle(phone.getDefaultPhone())
3149                 : PhoneUtils.makePstnPhoneAccountHandle(phone);
3150         TelecomAccountRegistry telecomAccountRegistry = getTelecomAccountRegistry(
3151                 getPhone().getContext());
3152         boolean isConferencingSupported = telecomAccountRegistry
3153                 .isMergeCallSupported(phoneAccountHandle);
3154         boolean isImsConferencingSupported = telecomAccountRegistry
3155                 .isMergeImsCallSupported(phoneAccountHandle);
3156         mIsCarrierVideoConferencingSupported = telecomAccountRegistry
3157                 .isVideoConferencingSupported(phoneAccountHandle);
3158         boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry
3159                 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle);
3160         ImsCall imsCall = isImsConnection()
3161                 ? ((ImsPhoneConnection) getOriginalConnection()).getImsCall()
3162                 : null;
3163         CarrierConfigManager configManager = (CarrierConfigManager) phone.getContext()
3164                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
3165         boolean downGradedVideoCall = false;
3166         if (configManager != null) {
3167             PersistableBundle config = configManager.getConfigForSubId(phone.getSubId());
3168             if (config != null) {
3169                 downGradedVideoCall = config.getBoolean(
3170                         CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL);
3171             }
3172         }
3173 
3174         Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " +
3175                 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " +
3176                 "isWifi=%b, isVoWifiEnabled=%b",
3177                 isConferencingSupported, isImsConferencingSupported,
3178                 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff,
3179                 isWifi(), isVoWifiEnabled);
3180         boolean isConferenceSupported = true;
3181         if (mTreatAsEmergencyCall) {
3182             isConferenceSupported = false;
3183             Log.d(this, "refreshConferenceSupported = false; emergency call");
3184         } else if (isRtt() && !isRttMergeSupported(getCarrierConfig())) {
3185             isConferenceSupported = false;
3186             Log.d(this, "refreshConferenceSupported = false; rtt call");
3187         } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) {
3188             isConferenceSupported = false;
3189             Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf.");
3190         } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) {
3191             isConferenceSupported = false;
3192             Log.d(this, "refreshConferenceSupported = false; video conf not supported.");
3193         } else if ((imsCall != null) && (imsCall.wasVideoCall() && downGradedVideoCall)
3194                 && !mIsCarrierVideoConferencingSupported) {
3195             isConferenceSupported = false;
3196             Log.d(this,
3197                     "refreshConferenceSupported = false;"
3198                             + " video conf not supported for downgraded audio call.");
3199         } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) {
3200             isConferenceSupported = false;
3201             Log.d(this,
3202                     "refreshConferenceSupported = false; can't merge wifi calls when voWifi off.");
3203         } else {
3204             Log.d(this, "refreshConferenceSupported = true.");
3205         }
3206 
3207         if (isConferenceSupported != isConferenceSupported()) {
3208             setConferenceSupported(isConferenceSupported);
3209             notifyConferenceSupportedChanged(isConferenceSupported);
3210         }
3211     }
3212 
3213     @VisibleForTesting
isWfcEnabled(Phone phone)3214     boolean isWfcEnabled(Phone phone) {
3215         return ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
3216     }
3217 
3218     /**
3219      * Provides a mapping from extras keys which may be found in the
3220      * {@link com.android.internal.telephony.Connection} to their equivalents defined in
3221      * {@link android.telecom.Connection}.
3222      *
3223      * @return Map containing key mappings.
3224      */
createExtrasMap()3225     private static Map<String, String> createExtrasMap() {
3226         Map<String, String> result = new HashMap<String, String>();
3227         result.put(ImsCallProfile.EXTRA_CHILD_NUMBER,
3228                 android.telecom.Connection.EXTRA_CHILD_ADDRESS);
3229         result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT,
3230                 android.telecom.Connection.EXTRA_CALL_SUBJECT);
3231         result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS,
3232                 android.telecom.Connection.EXTRA_SIP_INVITE);
3233         return Collections.unmodifiableMap(result);
3234     }
3235 
isShowingOriginalDialString()3236     private boolean isShowingOriginalDialString() {
3237         boolean showOrigDialString = false;
3238         Phone phone = getPhone();
3239         if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)
3240                 && !mOriginalConnection.isIncoming()) {
3241             showOrigDialString = getCarrierConfig().getBoolean(CarrierConfigManager
3242                     .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
3243             Log.d(this, "showOrigDialString: " + showOrigDialString);
3244         }
3245         return showOrigDialString;
3246     }
3247 
3248     /**
3249      * Creates a string representation of this {@link TelephonyConnection}.  Primarily intended for
3250      * use in log statements.
3251      *
3252      * @return String representation of the connection.
3253      */
3254     @Override
toString()3255     public String toString() {
3256         StringBuilder sb = new StringBuilder();
3257         sb.append("[TelephonyConnection objId:");
3258         sb.append(System.identityHashCode(this));
3259         sb.append(" telecomCallID:");
3260         sb.append(getTelecomCallId());
3261         sb.append(" type:");
3262         if (isImsConnection()) {
3263             sb.append("ims");
3264         } else if (this instanceof com.android.services.telephony.GsmConnection) {
3265             sb.append("gsm");
3266         } else if (this instanceof CdmaConnection) {
3267             sb.append("cdma");
3268         }
3269         sb.append(" state:");
3270         sb.append(Connection.stateToString(getState()));
3271         sb.append(" capabilities:");
3272         sb.append(capabilitiesToString(getConnectionCapabilities()));
3273         sb.append(" properties:");
3274         sb.append(propertiesToString(getConnectionProperties()));
3275         sb.append(" address:");
3276         sb.append(Rlog.pii(LOG_TAG, getAddress()));
3277         sb.append(" originalConnection:");
3278         sb.append(mOriginalConnection);
3279         sb.append(" partOfConf:");
3280         if (getConference() == null) {
3281             sb.append("N");
3282         } else {
3283             sb.append("Y");
3284         }
3285         sb.append(" confSupported:");
3286         sb.append(mIsConferenceSupported ? "Y" : "N");
3287         sb.append(" isAdhocConf:");
3288         sb.append(isAdhocConferenceCall() ? "Y" : "N");
3289         sb.append("]");
3290         return sb.toString();
3291     }
3292 
setTelephonyConnectionService(TelephonyConnectionService connectionService)3293     public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) {
3294         mTelephonyConnectionService = connectionService;
3295     }
3296 
getTelephonyConnectionService()3297     public final TelephonyConnectionService getTelephonyConnectionService() {
3298         return mTelephonyConnectionService;
3299     }
3300 
3301     /**
3302      * Set this {@link TelephonyConnection} to an active state.
3303      * <p>
3304      * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified.
3305      */
setTelephonyConnectionActive()3306     public void setTelephonyConnectionActive() {
3307         setActive();
3308         notifyStateChanged(getState());
3309     }
3310 
3311     /**
3312      * Set this {@link TelephonyConnection} to a ringing state.
3313      * <p>
3314      * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified.
3315      */
setTelephonyConnectionRinging()3316     public void setTelephonyConnectionRinging() {
3317         setRinging();
3318         notifyStateChanged(getState());
3319     }
3320 
3321     /**
3322      * Set this {@link TelephonyConnection} to an initializing state.
3323      * <p>
3324      * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are
3325      * notified.
3326      */
setTelephonyConnectionInitializing()3327     public void setTelephonyConnectionInitializing() {
3328         setInitializing();
3329         notifyStateChanged(getState());
3330     }
3331 
3332     /**
3333      * Set this {@link TelephonyConnection} to a dialing state.
3334      * <p>
3335      * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified.
3336      */
setTelephonyConnectionDialing()3337     public void setTelephonyConnectionDialing() {
3338         setDialing();
3339         notifyStateChanged(getState());
3340     }
3341 
3342     /**
3343      * Set this {@link TelephonyConnection} to a pulling state.
3344      * <p>
3345      * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified.
3346      */
setTelephonyConnectionPulling()3347     public void setTelephonyConnectionPulling() {
3348         setPulling();
3349         notifyStateChanged(getState());
3350     }
3351 
3352     /**
3353      * Set this {@link TelephonyConnection} to a held state.
3354      * <p>
3355      * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified.
3356      */
setTelephonyConnectionOnHold()3357     public void setTelephonyConnectionOnHold() {
3358         setOnHold();
3359         notifyStateChanged(getState());
3360     }
3361 
3362     /**
3363      * Set this {@link TelephonyConnection} to a disconnected state.
3364      * <p>
3365      * Note: This should be used instead of
3366      * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
3367      */
setTelephonyConnectionDisconnected(@onNull android.telecom.DisconnectCause disconnectCause)3368     public void setTelephonyConnectionDisconnected(@NonNull
3369             android.telecom.DisconnectCause disconnectCause) {
3370         setDisconnected(disconnectCause);
3371         notifyDisconnected(disconnectCause);
3372         notifyStateChanged(getState());
3373     }
3374 
3375     /**
3376      * Sends a connection event for this {@link TelephonyConnection}.
3377      * <p>
3378      * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure
3379      * listeners are notified.
3380      */
sendTelephonyConnectionEvent(@onNull String event, @Nullable Bundle extras)3381     public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) {
3382         sendConnectionEvent(event, extras);
3383         notifyTelephonyConnectionEvent(event, extras);
3384     }
3385 
3386     /**
3387      * Sets the extras associated with this {@link TelephonyConnection}.
3388      * <p>
3389      * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are
3390      * notified.
3391      */
putTelephonyExtras(@onNull Bundle extras)3392     public void putTelephonyExtras(@NonNull Bundle extras) {
3393         putExtras(extras);
3394         notifyPutExtras(extras);
3395     }
3396 
3397     /**
3398      * Removes the specified extras associated with this {@link TelephonyConnection}.
3399      * <p>
3400      * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are
3401      * notified.
3402      */
removeTelephonyExtras(@onNull List<String> keys)3403     public void removeTelephonyExtras(@NonNull List<String> keys) {
3404         removeExtras(keys);
3405         notifyRemoveExtras(keys);
3406     }
3407 
3408     /**
3409      * Sets the video state associated with this {@link TelephonyConnection}.
3410      * <p>
3411      * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are
3412      * notified.
3413      * @param videoState The new video state. Valid values:
3414      *                   {@link VideoProfile#STATE_AUDIO_ONLY},
3415      *                   {@link VideoProfile#STATE_BIDIRECTIONAL},
3416      *                   {@link VideoProfile#STATE_TX_ENABLED},
3417      *                   {@link VideoProfile#STATE_RX_ENABLED}.
3418      */
setTelephonyVideoState(int videoState)3419     public void setTelephonyVideoState(int videoState) {
3420         setVideoState(videoState);
3421         notifyVideoStateChanged(videoState);
3422     }
3423 
3424     /**
3425      * Sets the video provider associated with this {@link TelephonyConnection}.
3426      * <p>
3427      * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure
3428      * listeners are notified.
3429      */
setTelephonyVideoProvider(@ullable VideoProvider videoProvider)3430     public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) {
3431         setVideoProvider(videoProvider);
3432         notifyVideoProviderChanged(videoProvider);
3433     }
3434 
3435     /**
3436      * Sets the status hints associated with this {@link TelephonyConnection}.
3437      * <p>
3438      * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners
3439      * are notified.
3440      */
setTelephonyStatusHints(@ullable StatusHints statusHints)3441     public void setTelephonyStatusHints(@Nullable StatusHints statusHints) {
3442         setStatusHints(statusHints);
3443         notifyStatusHintsChanged(statusHints);
3444     }
3445 
3446     /**
3447      * Sets RIL voice radio technology used for current connection.
3448      * <p>
3449      * This property is set by the Telephony {@link ConnectionService}.
3450      *
3451      * @param vrat the RIL Voice Radio Technology used for current connection,
3452      *             see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
3453      */
setCallRadioTech(@ilRadioTechnology int vrat)3454     public final void setCallRadioTech(@RilRadioTechnology int vrat) {
3455         Bundle extras = getExtras();
3456         if (extras == null) {
3457             extras = new Bundle();
3458         }
3459         extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
3460                 ServiceState.rilRadioTechnologyToNetworkType(vrat));
3461         putExtras(extras);
3462         // Propagates the call radio technology to its parent {@link android.telecom.Conference}
3463         // This action only covers non-IMS CS conference calls.
3464         // For IMS PS call conference call, it can be updated via its host connection
3465         // {@link #Listener.onExtrasChanged} event.
3466         if (getConference() != null) {
3467             Bundle newExtras = new Bundle();
3468             newExtras.putInt(
3469                     TelecomManager.EXTRA_CALL_NETWORK_TYPE,
3470                     ServiceState.rilRadioTechnologyToNetworkType(vrat));
3471             getConference().putExtras(newExtras);
3472         }
3473     }
3474 
3475     /**
3476      * Returns RIL voice radio technology used for current connection.
3477      * <p>
3478      * Used by the Telephony {@link ConnectionService}.
3479      *
3480      * @return the RIL voice radio technology used for current connection,
3481      *         see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
3482      */
getCallRadioTech()3483     public final @RilRadioTechnology int getCallRadioTech() {
3484         int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
3485         Bundle extras = getExtras();
3486         if (extras != null) {
3487             voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
3488                     TelephonyManager.NETWORK_TYPE_UNKNOWN);
3489         }
3490         return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
3491     }
3492 
3493     /**
3494      * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data
3495      * received via the {@link ImsConference} (i.e. conference event package).
3496      *
3497      * @param conferenceParticipants The participants.
3498      */
updateConferenceParticipants( @onNull List<ConferenceParticipant> conferenceParticipants)3499     private void updateConferenceParticipants(
3500             @NonNull List<ConferenceParticipant> conferenceParticipants) {
3501         for (TelephonyConnectionListener l : mTelephonyListeners) {
3502             l.onConferenceParticipantsChanged(this, conferenceParticipants);
3503         }
3504     }
3505 
3506     /**
3507      * Where device to device communication is available and this is an IMS call, configures the
3508      * D2D communication infrastructure for operation.
3509      */
maybeConfigureDeviceToDeviceCommunication()3510     private void maybeConfigureDeviceToDeviceCommunication() {
3511         if (!getPhone().getContext().getResources().getBoolean(
3512                 R.bool.config_use_device_to_device_communication)) {
3513             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D.");
3514             notifyD2DAvailabilityChanged(false);
3515             return;
3516         }
3517         if (!isImsConnection()) {
3518             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection.");
3519             if (mCommunicator != null) {
3520                 mCommunicator = null;
3521             }
3522             notifyD2DAvailabilityChanged(false);
3523             return;
3524         }
3525         if (mTreatAsEmergencyCall || mIsNetworkIdentifiedEmergencyCall) {
3526             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: emergency call; no D2D");
3527             notifyD2DAvailabilityChanged(false);
3528             return;
3529         }
3530 
3531         ArrayList<TransportProtocol> supportedTransports = new ArrayList<>(2);
3532 
3533         if (supportsD2DUsingRtp()) {
3534             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports RTP.");
3535             // Implement abstracted out RTP functionality the RTP transport depends on.
3536             RtpAdapter rtpAdapter = new RtpAdapter() {
3537                 @Override
3538                 public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() {
3539                     ImsPhoneConnection originalConnection =
3540                             (ImsPhoneConnection) mOriginalConnection;
3541                     return originalConnection.getAcceptedRtpHeaderExtensions();
3542                 }
3543 
3544                 @Override
3545                 public void sendRtpHeaderExtensions(
3546                         @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
3547                     Log.i(TelephonyConnection.this, "sendRtpHeaderExtensions: sending: %s",
3548                             rtpHeaderExtensions.stream()
3549                                     .map(r -> r.toString())
3550                                     .collect(Collectors.joining(",")));
3551                     ImsPhoneConnection originalConnection =
3552                             (ImsPhoneConnection) mOriginalConnection;
3553                     originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions);
3554                 }
3555             };
3556             mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler,
3557                     supportsSdpNegotiationOfRtpHeaderExtensions());
3558             supportedTransports.add(mRtpTransport);
3559         }
3560         if (supportsD2DUsingDtmf()) {
3561             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports DTMF.");
3562             DtmfAdapter dtmfAdapter = digit -> {
3563                 Log.i(TelephonyConnection.this, "sendDtmf: send digit %c", digit);
3564                 ImsPhoneConnection originalConnection =
3565                         (ImsPhoneConnection) mOriginalConnection;
3566                 Message dtmfComplete = mHandler.obtainMessage(MSG_DTMF_DONE);
3567                 dtmfComplete.replyTo = mHandlerMessenger;
3568                 originalConnection.getImsCall().sendDtmf(digit, dtmfComplete);
3569             };
3570             ContentResolver cr = getPhone().getContext().getContentResolver();
3571             mDtmfTransport = new DtmfTransport(dtmfAdapter, new Timeouts.Adapter(cr),
3572                     Executors.newSingleThreadScheduledExecutor());
3573             supportedTransports.add(mDtmfTransport);
3574         }
3575         if (supportedTransports.size() > 0) {
3576             mCommunicator = new Communicator(supportedTransports, this);
3577             mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator);
3578             addTelephonyConnectionListener(mD2DCallStateAdapter);
3579         } else {
3580             Log.i(this, "maybeConfigureDeviceToDeviceCommunication: no transports; disabled.");
3581             notifyD2DAvailabilityChanged(false);
3582         }
3583     }
3584 
3585     /**
3586      * Notifies upper layers of the availability of D2D communication.
3587      * @param isAvailable {@code true} if D2D is available, {@code false} otherwise.
3588      */
notifyD2DAvailabilityChanged(boolean isAvailable)3589     private void notifyD2DAvailabilityChanged(boolean isAvailable) {
3590         Bundle extras = new Bundle();
3591         extras.putBoolean(Connection.EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE,
3592                 isAvailable);
3593         putTelephonyExtras(extras);
3594     }
3595 
3596     /**
3597      * @return The D2D communication class, or {@code null} if not set up.
3598      */
getCommunicator()3599     public @Nullable Communicator getCommunicator() {
3600         return mCommunicator;
3601     }
3602 
3603     /**
3604      * Called by {@link Communicator} associated with this {@link TelephonyConnection} when there
3605      * are incoming device-to-device messages received.
3606      * @param messages the incoming messages.
3607      */
3608     @Override
onMessagesReceived(@onNull Set<Communicator.Message> messages)3609     public void onMessagesReceived(@NonNull Set<Communicator.Message> messages) {
3610         Log.i(this, "onMessagesReceived: got d2d messages: %s", messages);
3611         // Send connection events up to Telecom so that we can relay the messages to a valid
3612         // CallDiagnosticService which is bound.
3613         for (Communicator.Message msg : messages) {
3614             Integer dcMsgType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getValue(
3615                     msg.getType());
3616             if (dcMsgType == null) {
3617                 // Invalid msg type, skip.
3618                 continue;
3619             }
3620 
3621             Integer dcMsgValue;
3622             switch (msg.getType()) {
3623                 case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC:
3624                     dcMsgValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getValue(
3625                             msg.getValue());
3626                     break;
3627                 case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE:
3628                     dcMsgValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getValue(
3629                             msg.getValue());
3630                     break;
3631                 case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE:
3632                     dcMsgValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE
3633                             .getValue(msg.getValue());
3634                     break;
3635                 case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE:
3636                     dcMsgValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE
3637                             .getValue(msg.getValue());
3638                     break;
3639                 default:
3640                     Log.w(this, "onMessagesReceived: msg=%d - invalid msg", msg.getValue());
3641                     continue;
3642             }
3643             if (dcMsgValue == null) {
3644                 Log.w(this, "onMessagesReceived: msg=%d/%d - invalid msg value", msg.getType(),
3645                         msg.getValue());
3646                 continue;
3647             }
3648             Bundle extras = new Bundle();
3649             extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, dcMsgType);
3650             extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, dcMsgValue);
3651             sendConnectionEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras);
3652         }
3653     }
3654 
3655     /**
3656      * Handles report from {@link Communicator} when the availability of D2D changes.
3657      * @param isAvailable {@code true} if D2D is available, {@code false} if unavailable.
3658      */
3659     @Override
onD2DAvailabilitychanged(boolean isAvailable)3660     public void onD2DAvailabilitychanged(boolean isAvailable) {
3661         notifyD2DAvailabilityChanged(isAvailable);
3662     }
3663 
3664     /**
3665      * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
3666      * operation has started.
3667      */
notifyConferenceStarted()3668     protected void notifyConferenceStarted() {
3669         for (TelephonyConnectionListener l : mTelephonyListeners) {
3670             l.onConferenceStarted();
3671         }
3672     }
3673 
3674     /**
3675      * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection
3676      * which impacts its ability to be a part of a conference call.
3677      * @param isConferenceSupported {@code true} if the connection supports being part of a
3678      *      conference call, {@code false} otherwise.
3679      */
notifyConferenceSupportedChanged(boolean isConferenceSupported)3680     private void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
3681         for (TelephonyConnectionListener l : mTelephonyListeners) {
3682             l.onConferenceSupportedChanged(this, isConferenceSupported);
3683         }
3684     }
3685 
3686     /**
3687      * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities.
3688      * @param newCapabilities the new capabilities.
3689      */
notifyConnectionCapabilitiesChanged(int newCapabilities)3690     private void notifyConnectionCapabilitiesChanged(int newCapabilities) {
3691         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3692             listener.onConnectionCapabilitiesChanged(this, newCapabilities);
3693         }
3694     }
3695 
3696     /**
3697      * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties.
3698      * @param newProperties the new properties.
3699      */
notifyConnectionPropertiesChanged(int newProperties)3700     private void notifyConnectionPropertiesChanged(int newProperties) {
3701         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3702             listener.onConnectionPropertiesChanged(this, newProperties);
3703         }
3704     }
3705 
3706     /**
3707      * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed.
3708      */
notifyDestroyed()3709     private void notifyDestroyed() {
3710         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3711             listener.onDestroyed(this);
3712         }
3713     }
3714 
3715     /**
3716      * Notifies {@link TelephonyConnectionListener}s when a connection disconnects.
3717      * @param cause The disconnect cause.
3718      */
notifyDisconnected(android.telecom.DisconnectCause cause)3719     private void notifyDisconnected(android.telecom.DisconnectCause cause) {
3720         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3721             listener.onDisconnected(this, cause);
3722         }
3723     }
3724 
3725     /**
3726      * Notifies {@link TelephonyConnectionListener}s of connection state changes.
3727      * @param newState The new state.
3728      */
notifyStateChanged(int newState)3729     private void notifyStateChanged(int newState) {
3730         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3731             listener.onStateChanged(this, newState);
3732         }
3733     }
3734 
3735     /**
3736      * Notifies {@link TelephonyConnectionListener}s of telephony connection events.
3737      * @param event The event.
3738      * @param extras Any extras.
3739      */
notifyTelephonyConnectionEvent(String event, Bundle extras)3740     private void notifyTelephonyConnectionEvent(String event, Bundle extras) {
3741         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3742             listener.onConnectionEvent(this, event, extras);
3743         }
3744     }
3745 
3746     /**
3747      * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection.
3748      * @param extras The new extras.
3749      */
notifyPutExtras(Bundle extras)3750     private void notifyPutExtras(Bundle extras) {
3751         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3752             listener.onExtrasChanged(this, extras);
3753         }
3754     }
3755 
3756     /**
3757      * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection.
3758      * @param keys The removed keys.
3759      */
notifyRemoveExtras(List<String> keys)3760     private void notifyRemoveExtras(List<String> keys) {
3761         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3762             listener.onExtrasRemoved(this, keys);
3763         }
3764     }
3765 
3766     /**
3767      * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection.
3768      * @param videoState The new video state. Valid values:
3769      *                   {@link VideoProfile#STATE_AUDIO_ONLY},
3770      *                   {@link VideoProfile#STATE_BIDIRECTIONAL},
3771      *                   {@link VideoProfile#STATE_TX_ENABLED},
3772      *                   {@link VideoProfile#STATE_RX_ENABLED}.
3773      */
notifyVideoStateChanged(int videoState)3774     private void notifyVideoStateChanged(int videoState) {
3775         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3776             listener.onVideoStateChanged(this, videoState);
3777         }
3778     }
3779 
3780     /**
3781      * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not.
3782      * @param ringback Whether the ringback tone is to be played
3783      */
notifyRingbackRequested(boolean ringback)3784     private void notifyRingbackRequested(boolean ringback) {
3785         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3786             listener.onRingbackRequested(this, ringback);
3787         }
3788     }
3789 
3790     /**
3791      * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
3792      * connection.
3793      * @param videoProvider The new video provider.
3794      */
notifyVideoProviderChanged(VideoProvider videoProvider)3795     private void notifyVideoProviderChanged(VideoProvider videoProvider) {
3796         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3797             listener.onVideoProviderChanged(this, videoProvider);
3798         }
3799     }
3800 
3801     /**
3802      * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a
3803      * connection.
3804      * @param statusHints The new status hints.
3805      */
notifyStatusHintsChanged(StatusHints statusHints)3806     private void notifyStatusHintsChanged(StatusHints statusHints) {
3807         for (TelephonyConnectionListener listener : mTelephonyListeners) {
3808             listener.onStatusHintsChanged(this, statusHints);
3809         }
3810     }
3811 
3812     /**
3813      * Whether the incoming call number should be formatted to national number for Japan.
3814      * @return {@code true} should be convert to the national format, {@code false} otherwise.
3815      */
isNeededToFormatIncomingNumberForJp()3816     private boolean isNeededToFormatIncomingNumberForJp() {
3817         if (mOriginalConnection.isIncoming()
3818                 && !TextUtils.isEmpty(mOriginalConnection.getAddress())
3819                 && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) {
3820             return getCarrierConfig().getBoolean(
3821                     CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL);
3822         }
3823         return false;
3824     }
3825 
3826     /**
3827      * Format the incoming call number to national number for Japan.
3828      * @param number
3829      * @return the formatted phone number (e.g, "+819012345678" -> "09012345678")
3830      */
formatIncomingNumberForJp(String number)3831     private String formatIncomingNumberForJp(String number) {
3832         return PhoneNumberUtils.stripSeparators(
3833                 PhoneNumberUtils.formatNumber(number, JAPAN_ISO_COUNTRY_CODE));
3834     }
3835 
getTelecomAccountRegistry(Context context)3836     public TelecomAccountRegistry getTelecomAccountRegistry(Context context) {
3837         return TelecomAccountRegistry.getInstance(context);
3838     }
3839 
3840     /**
3841      * @return {@code true} if the carrier supports D2D using RTP header extensions, {@code false}
3842      * otherwise.
3843      */
supportsD2DUsingRtp()3844     private boolean supportsD2DUsingRtp() {
3845         return getCarrierConfig().getBoolean(
3846                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
3847     }
3848 
3849     /**
3850      * @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise.
3851      */
supportsD2DUsingDtmf()3852     private boolean supportsD2DUsingDtmf() {
3853         return getCarrierConfig().getBoolean(
3854                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL);
3855     }
3856 
3857     /**
3858      * @return {@code true} if the carrier supports using SDP negotiation for the RTP header
3859      * extensions used in D2D comms, {@code false} otherwise.
3860      */
supportsSdpNegotiationOfRtpHeaderExtensions()3861     private boolean supportsSdpNegotiationOfRtpHeaderExtensions() {
3862         return getCarrierConfig().getBoolean(
3863                 CarrierConfigManager
3864                         .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
3865     }
3866 
3867     /**
3868      * Handles a device to device message which a {@link CallDiagnostics} wishes to send.
3869      * @param extras the call event extras bundle.
3870      */
handleOutgoingDeviceToDeviceMessage(Bundle extras)3871     private void handleOutgoingDeviceToDeviceMessage(Bundle extras) {
3872         int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE);
3873         int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE);
3874 
3875         Integer internalMessageValue;
3876         switch (messageType) {
3877             case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC:
3878                 internalMessageValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getKey(
3879                         messageValue);
3880                 break;
3881             case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE:
3882                 internalMessageValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getKey(
3883                         messageValue);
3884                 break;
3885             case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE:
3886                 internalMessageValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE
3887                         .getKey(messageValue);
3888                 break;
3889             case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE:
3890                 internalMessageValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE
3891                         .getKey(messageValue);
3892                 break;
3893             default:
3894                 Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d - invalid msg",
3895                         messageType);
3896                 return;
3897         }
3898         Integer internalMessageType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getKey(
3899                 messageType);
3900         if (internalMessageValue == null) {
3901             Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - invalid value",
3902                     messageType, messageValue);
3903             return;
3904         }
3905 
3906         if (mCommunicator != null) {
3907             Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - sending",
3908                     internalMessageType, internalMessageValue);
3909             Set<Communicator.Message> set = new ArraySet<>();
3910             set.add(new Communicator.Message(internalMessageType, internalMessageValue));
3911             mCommunicator.sendMessages(set);
3912         }
3913     }
3914 
3915     /**
3916      * Returns the current telephony connection listeners for test purposes.
3917      * @return list of telephony connection listeners.
3918      */
3919     @VisibleForTesting
getTelephonyConnectionListeners()3920     public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
3921         return new ArrayList<>(mTelephonyListeners);
3922     }
3923 
3924     /**
3925      * @return An {@link Integer} instance of the emergency service category.
3926      */
getEmergencyServiceCategory()3927     public @Nullable Integer getEmergencyServiceCategory() {
3928         return mEmergencyServiceCategory;
3929     }
3930 
3931     /**
3932      * Sets the emergency service category.
3933      *
3934      * @param eccCategory The emergency service category.
3935      */
3936     @VisibleForTesting
setEmergencyServiceCategory(int eccCategory)3937     public void setEmergencyServiceCategory(int eccCategory) {
3938         mEmergencyServiceCategory = eccCategory;
3939     }
3940 
3941     /**
3942      * @return a {@link List} of {@link String}s that are the emrgency URNs.
3943      */
getEmergencyUrns()3944     public @Nullable List<String> getEmergencyUrns() {
3945         return mEmergencyUrns;
3946     }
3947 
3948     /**
3949      * Set the emergency URNs.
3950      *
3951      * @param emergencyUrns The emergency URNs.
3952      */
3953     @VisibleForTesting
setEmergencyUrns(@ullable List<String> emergencyUrns)3954     public void setEmergencyUrns(@Nullable List<String> emergencyUrns) {
3955         mEmergencyUrns = emergencyUrns;
3956     }
3957 }
3958