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