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