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