• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.services.telephony;
18 
19 import android.content.Context;
20 import android.graphics.drawable.Icon;
21 import android.net.Uri;
22 import android.os.AsyncResult;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.os.PersistableBundle;
28 import android.telecom.CallAudioState;
29 import android.telecom.ConferenceParticipant;
30 import android.telecom.Connection;
31 import android.telecom.PhoneAccount;
32 import android.telecom.PhoneAccountHandle;
33 import android.telecom.StatusHints;
34 import android.telecom.TelecomManager;
35 import android.telecom.VideoProfile;
36 import android.telephony.CarrierConfigManager;
37 import android.telephony.DisconnectCause;
38 import android.telephony.PhoneNumberUtils;
39 import android.telephony.ServiceState;
40 import android.telephony.TelephonyManager;
41 import android.telephony.ims.ImsCallProfile;
42 import android.text.TextUtils;
43 import android.util.Pair;
44 
45 import com.android.ims.ImsCall;
46 import com.android.internal.telephony.Call;
47 import com.android.internal.telephony.CallFailCause;
48 import com.android.internal.telephony.CallStateException;
49 import com.android.internal.telephony.Connection.Capability;
50 import com.android.internal.telephony.Connection.PostDialListener;
51 import com.android.internal.telephony.Phone;
52 import com.android.internal.telephony.PhoneConstants;
53 import com.android.internal.telephony.gsm.SuppServiceNotification;
54 import com.android.internal.telephony.imsphone.ImsPhone;
55 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
56 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
57 import com.android.phone.ImsUtil;
58 import com.android.phone.PhoneGlobals;
59 import com.android.phone.PhoneUtils;
60 import com.android.phone.R;
61 
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collections;
65 import java.util.HashMap;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.Objects;
69 import java.util.Set;
70 import java.util.concurrent.ConcurrentHashMap;
71 
72 /**
73  * Base class for CDMA and GSM connections.
74  */
75 abstract class TelephonyConnection extends Connection implements Holdable {
76     private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
77     private static final int MSG_RINGBACK_TONE = 2;
78     private static final int MSG_HANDOVER_STATE_CHANGED = 3;
79     private static final int MSG_DISCONNECT = 4;
80     private static final int MSG_MULTIPARTY_STATE_CHANGED = 5;
81     private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
82     private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
83 
84     /**
85      * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
86      * equivalents defined in {@link android.telecom.Connection}.
87      */
88     private static final Map<String, String> sExtrasMap = createExtrasMap();
89 
90     private static final int MSG_SET_VIDEO_STATE = 8;
91     private static final int MSG_SET_VIDEO_PROVIDER = 9;
92     private static final int MSG_SET_AUDIO_QUALITY = 10;
93     private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 11;
94     private static final int MSG_CONNECTION_EXTRAS_CHANGED = 12;
95     private static final int MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES = 13;
96     private static final int MSG_ON_HOLD_TONE = 14;
97     private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
98     private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
99     private static final int MSG_HANGUP = 17;
100     private static final int MSG_SET_CALL_RADIO_TECH = 18;
101 
102     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
103         @Override
104         public void handleMessage(Message msg) {
105             switch (msg.what) {
106                 case MSG_PRECISE_CALL_STATE_CHANGED:
107                     Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED");
108                     updateState();
109                     break;
110                 case MSG_HANDOVER_STATE_CHANGED:
111                     Log.v(TelephonyConnection.this, "MSG_HANDOVER_STATE_CHANGED");
112                     AsyncResult ar = (AsyncResult) msg.obj;
113                     com.android.internal.telephony.Connection connection =
114                          (com.android.internal.telephony.Connection) ar.result;
115                     if (mOriginalConnection != null) {
116                         if (connection != null &&
117                             ((connection.getAddress() != null &&
118                             mOriginalConnection.getAddress() != null &&
119                             mOriginalConnection.getAddress().contains(connection.getAddress())) ||
120                             connection.getState() == mOriginalConnection.getStateBeforeHandover())) {
121                             Log.d(TelephonyConnection.this,
122                                     "SettingOriginalConnection " + mOriginalConnection.toString()
123                                             + " with " + connection.toString());
124                             setOriginalConnection(connection);
125                             mWasImsConnection = false;
126                         }
127                     } else {
128                         Log.w(TelephonyConnection.this,
129                                 "MSG_HANDOVER_STATE_CHANGED: mOriginalConnection==null - invalid state (not cleaned up)");
130                     }
131                     break;
132                 case MSG_RINGBACK_TONE:
133                     Log.v(TelephonyConnection.this, "MSG_RINGBACK_TONE");
134                     // TODO: This code assumes that there is only one connection in the foreground
135                     // call, in other words, it punts on network-mediated conference calling.
136                     if (getOriginalConnection() != getForegroundConnection()) {
137                         Log.v(TelephonyConnection.this, "handleMessage, original connection is " +
138                                 "not foreground connection, skipping");
139                         return;
140                     }
141                     setRingbackRequested((Boolean) ((AsyncResult) msg.obj).result);
142                     break;
143                 case MSG_DISCONNECT:
144                     updateState();
145                     break;
146                 case MSG_MULTIPARTY_STATE_CHANGED:
147                     boolean isMultiParty = (Boolean) msg.obj;
148                     Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
149                     mIsMultiParty = isMultiParty;
150                     if (isMultiParty) {
151                         notifyConferenceStarted();
152                     }
153                     break;
154                 case MSG_CONFERENCE_MERGE_FAILED:
155                     notifyConferenceMergeFailed();
156                     break;
157                 case MSG_SUPP_SERVICE_NOTIFY:
158                     Phone phone = getPhone();
159                     Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : "
160                             + (phone != null ? Integer.toString(phone.getPhoneId())
161                             : "null"));
162                     SuppServiceNotification mSsNotification = null;
163                     if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
164                         mSsNotification =
165                                 (SuppServiceNotification)((AsyncResult) msg.obj).result;
166                         if (mOriginalConnection != null) {
167                             handleSuppServiceNotification(mSsNotification);
168                         }
169                     }
170                     break;
171 
172                 case MSG_SET_VIDEO_STATE:
173                     int videoState = (int) msg.obj;
174                     setVideoState(videoState);
175 
176                     // A change to the video state of the call can influence whether or not it
177                     // can be part of a conference, whether another call can be added, and
178                     // whether the call should have the HD audio property set.
179                     refreshConferenceSupported();
180                     refreshDisableAddCall();
181                     updateConnectionProperties();
182                     break;
183 
184                 case MSG_SET_VIDEO_PROVIDER:
185                     VideoProvider videoProvider = (VideoProvider) msg.obj;
186                     setVideoProvider(videoProvider);
187                     break;
188 
189                 case MSG_SET_AUDIO_QUALITY:
190                     int audioQuality = (int) msg.obj;
191                     setAudioQuality(audioQuality);
192                     break;
193 
194                 case MSG_SET_CONFERENCE_PARTICIPANTS:
195                     List<ConferenceParticipant> participants = (List<ConferenceParticipant>) msg.obj;
196                     updateConferenceParticipants(participants);
197                     break;
198 
199                 case MSG_CONNECTION_EXTRAS_CHANGED:
200                     final Bundle extras = (Bundle) msg.obj;
201                     updateExtras(extras);
202                     break;
203 
204                 case MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES:
205                     setOriginalConnectionCapabilities(msg.arg1);
206                     break;
207 
208                 case MSG_ON_HOLD_TONE:
209                     AsyncResult asyncResult = (AsyncResult) msg.obj;
210                     Pair<com.android.internal.telephony.Connection, Boolean> heldInfo =
211                             (Pair<com.android.internal.telephony.Connection, Boolean>)
212                                     asyncResult.result;
213 
214                     // Determines if the hold tone is starting or stopping.
215                     boolean playTone = ((Boolean) (heldInfo.second)).booleanValue();
216 
217                     // Determine which connection the hold tone is stopping or starting for
218                     com.android.internal.telephony.Connection heldConnection = heldInfo.first;
219 
220                     // Only start or stop the hold tone if this is the connection which is starting
221                     // or stopping the hold tone.
222                     if (heldConnection == mOriginalConnection) {
223                         // If starting the hold tone, send a connection event to Telecom which will
224                         // cause it to play the on hold tone.
225                         if (playTone) {
226                             sendConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
227                         } else {
228                             sendConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
229                         }
230                     }
231                     break;
232 
233                 case MSG_CDMA_VOICE_PRIVACY_ON:
234                     Log.d(this, "MSG_CDMA_VOICE_PRIVACY_ON received");
235                     setCdmaVoicePrivacy(true);
236                     break;
237                 case MSG_CDMA_VOICE_PRIVACY_OFF:
238                     Log.d(this, "MSG_CDMA_VOICE_PRIVACY_OFF received");
239                     setCdmaVoicePrivacy(false);
240                     break;
241                 case MSG_HANGUP:
242                     int cause = (int) msg.obj;
243                     hangup(cause);
244                     break;
245 
246                 case MSG_SET_CALL_RADIO_TECH:
247                     int vrat = (int) msg.obj;
248                     // Check whether Wi-Fi call tech is changed, it means call radio tech is:
249                     //  a) changed from IWLAN to other value, or
250                     //  b) changed from other value to IWLAN.
251                     //
252                     // In other word, below conditions are all met:
253                     // 1) {@link #getCallRadioTech} is different from new vrat
254                     // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi}
255                     //    is true, or new vrat indicates Wi-Fi call.
256                     boolean isWifiTechChange = getCallRadioTech() != vrat
257                             && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
258 
259                     // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related
260                     // update actions are taken correctly.
261                     setCallRadioTech(vrat);
262 
263                     // Step 2) Handles Wi-Fi call tech change.
264                     if (isWifiTechChange) {
265                         updateConnectionProperties();
266                         updateStatusHints();
267                         refreshDisableAddCall();
268                     }
269                     break;
270             }
271         }
272     };
273 
274     /**
275      * Handles {@link SuppServiceNotification}s pertinent to Telephony.
276      * @param ssn the notification.
277      */
handleSuppServiceNotification(SuppServiceNotification ssn)278     private void handleSuppServiceNotification(SuppServiceNotification ssn) {
279         Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType,
280                 ssn.code);
281         if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
282                 && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
283             sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
284         }
285         sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
286     }
287 
288     /**
289      * Sends a supplementary service notification connection event.
290      * This connection event includes the type and code, as well as a human readable message which
291      * is suitable for display to the user if the UI chooses to do so.
292      * @param type the {@link SuppServiceNotification#type}.
293      * @param code the {@link SuppServiceNotification#code}.
294      */
sendSuppServiceNotificationEvent(int type, int code)295     private void sendSuppServiceNotificationEvent(int type, int code) {
296         Bundle extras = new Bundle();
297         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
298         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
299         extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
300                 getSuppServiceMessage(type, code));
301         sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
302     }
303 
304     /**
305      * Retrieves a human-readable message for a supplementary service notification.
306      * This message is suitable for display to the user.
307      * @param type the code group.
308      * @param code the code.
309      * @return A {@link CharSequence} containing the message, or {@code null} if none defined.
310      */
getSuppServiceMessage(int type, int code)311     private CharSequence getSuppServiceMessage(int type, int code) {
312         int messageId = -1;
313         if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) {
314             switch (code) {
315                 case SuppServiceNotification.CODE_1_CALL_DEFLECTED:
316                     messageId = R.string.supp_service_notification_call_deflected;
317                     break;
318                 case SuppServiceNotification.CODE_1_CALL_FORWARDED:
319                     messageId = R.string.supp_service_notification_call_forwarded;
320                     break;
321                 case SuppServiceNotification.CODE_1_CALL_IS_WAITING:
322                     messageId = R.string.supp_service_notification_call_waiting;
323                     break;
324                 case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED:
325                     messageId = R.string.supp_service_clir_suppression_rejected;
326                     break;
327                 case SuppServiceNotification.CODE_1_CUG_CALL:
328                     messageId = R.string.supp_service_closed_user_group_call;
329                     break;
330                 case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED:
331                     messageId = R.string.supp_service_incoming_calls_barred;
332                     break;
333                 case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED:
334                     messageId = R.string.supp_service_outgoing_calls_barred;
335                     break;
336                 case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE:
337                     // Intentional fall through.
338                 case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE:
339                     messageId = R.string.supp_service_call_forwarding_active;
340                     break;
341             }
342         } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) {
343             switch (code) {
344                 case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED:
345                     messageId = R.string.supp_service_additional_call_forwarded;
346                     break;
347                 case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT:
348                     messageId = R.string.supp_service_additional_ect_connected;
349                     break;
350                 case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT:
351                     messageId = R.string.supp_service_additional_ect_connecting;
352                     break;
353                 case SuppServiceNotification.CODE_2_CALL_ON_HOLD:
354                     messageId = R.string.supp_service_call_on_hold;
355                     break;
356                 case SuppServiceNotification.CODE_2_CALL_RETRIEVED:
357                     messageId = R.string.supp_service_call_resumed;
358                     break;
359                 case SuppServiceNotification.CODE_2_CUG_CALL:
360                     messageId = R.string.supp_service_closed_user_group_call;
361                     break;
362                 case SuppServiceNotification.CODE_2_DEFLECTED_CALL:
363                     messageId = R.string.supp_service_deflected_call;
364                     break;
365                 case SuppServiceNotification.CODE_2_FORWARDED_CALL:
366                     messageId = R.string.supp_service_forwarded_call;
367                     break;
368                 case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL:
369                     messageId = R.string.supp_service_conference_call;
370                     break;
371                 case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED:
372                     messageId = R.string.supp_service_held_call_released;
373                     break;
374             }
375         }
376         if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
377             return getPhone().getContext().getText(messageId);
378         } else {
379             return null;
380         }
381     }
382 
383     /**
384      * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
385      */
isCarrierVideoConferencingSupported()386     public boolean isCarrierVideoConferencingSupported() {
387         return mIsCarrierVideoConferencingSupported;
388     }
389 
390     /**
391      * A listener/callback mechanism that is specific communication from TelephonyConnections
392      * to TelephonyConnectionService (for now). It is more specific that Connection.Listener
393      * because it is only exposed in Telephony.
394      */
395     public abstract static class TelephonyConnectionListener {
onOriginalConnectionConfigured(TelephonyConnection c)396         public void onOriginalConnectionConfigured(TelephonyConnection c) {}
onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure)397         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
398     }
399 
400     private final PostDialListener mPostDialListener = new PostDialListener() {
401         @Override
402         public void onPostDialWait() {
403             Log.v(TelephonyConnection.this, "onPostDialWait");
404             if (mOriginalConnection != null) {
405                 setPostDialWait(mOriginalConnection.getRemainingPostDialString());
406             }
407         }
408 
409         @Override
410         public void onPostDialChar(char c) {
411             Log.v(TelephonyConnection.this, "onPostDialChar: %s", c);
412             if (mOriginalConnection != null) {
413                 setNextPostDialChar(c);
414             }
415         }
416     };
417 
418     /**
419      * Listener for listening to events in the {@link com.android.internal.telephony.Connection}.
420      */
421     private final com.android.internal.telephony.Connection.Listener mOriginalConnectionListener =
422             new com.android.internal.telephony.Connection.ListenerBase() {
423         @Override
424         public void onVideoStateChanged(int videoState) {
425             mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState).sendToTarget();
426         }
427 
428         /*
429          * The {@link com.android.internal.telephony.Connection} has reported a change in
430          * connection capability.
431          * @param capabilities bit mask containing voice or video or both capabilities.
432          */
433         @Override
434         public void onConnectionCapabilitiesChanged(int capabilities) {
435             mHandler.obtainMessage(MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES,
436                     capabilities, 0).sendToTarget();
437         }
438 
439         /**
440          * The {@link com.android.internal.telephony.Connection} has reported a change in the
441          * video call provider.
442          *
443          * @param videoProvider The video call provider.
444          */
445         @Override
446         public void onVideoProviderChanged(VideoProvider videoProvider) {
447             mHandler.obtainMessage(MSG_SET_VIDEO_PROVIDER, videoProvider).sendToTarget();
448         }
449 
450         /**
451          * Used by {@link com.android.internal.telephony.Connection} to report a change for
452          * the call radio technology.
453          *
454          * @param vrat the RIL Voice Radio Technology used for current connection.
455          */
456         @Override
457         public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) {
458             mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
459         }
460 
461         /**
462          * Used by the {@link com.android.internal.telephony.Connection} to report a change in the
463          * audio quality for the current call.
464          *
465          * @param audioQuality The audio quality.
466          */
467         @Override
468         public void onAudioQualityChanged(int audioQuality) {
469             mHandler.obtainMessage(MSG_SET_AUDIO_QUALITY, audioQuality).sendToTarget();
470         }
471         /**
472          * Handles a change in the state of conference participant(s), as reported by the
473          * {@link com.android.internal.telephony.Connection}.
474          *
475          * @param participants The participant(s) which changed.
476          */
477         @Override
478         public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {
479             mHandler.obtainMessage(MSG_SET_CONFERENCE_PARTICIPANTS, participants).sendToTarget();
480         }
481 
482         /*
483          * Handles a change to the multiparty state for this connection.
484          *
485          * @param isMultiParty {@code true} if the call became multiparty, {@code false}
486          *      otherwise.
487          */
488         @Override
489         public void onMultipartyStateChanged(boolean isMultiParty) {
490             handleMultipartyStateChange(isMultiParty);
491         }
492 
493         /**
494          * Handles the event that the request to merge calls failed.
495          */
496         @Override
497         public void onConferenceMergedFailed() {
498             handleConferenceMergeFailed();
499         }
500 
501         @Override
502         public void onExtrasChanged(Bundle extras) {
503             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, extras).sendToTarget();
504         }
505 
506         /**
507          * Handles the phone exiting ECM mode by updating the connection capabilities.  During an
508          * ongoing call, if ECM mode is exited, we will re-enable mute for CDMA calls.
509          */
510         @Override
511         public void onExitedEcmMode() {
512             handleExitedEcmMode();
513         }
514 
515         /**
516          * Called from {@link ImsPhoneCallTracker} when a request to pull an external call has
517          * failed.
518          * @param externalConnection
519          */
520         @Override
521         public void onCallPullFailed(com.android.internal.telephony.Connection externalConnection) {
522             if (externalConnection == null) {
523                 return;
524             }
525 
526             Log.i(this, "onCallPullFailed - pull failed; swapping back to call: %s",
527                     externalConnection);
528 
529             // Inform the InCallService of the fact that the call pull failed (it may choose to
530             // display a message informing the user of the pull failure).
531             sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
532 
533             // Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection
534             // which originally represented the call.
535             setOriginalConnection(externalConnection);
536 
537             // Set our state to active again since we're no longer pulling.
538             setActiveInternal();
539         }
540 
541         /**
542          * Called from {@link ImsPhoneCallTracker} when a handover to WIFI has failed.
543          */
544         @Override
545         public void onHandoverToWifiFailed() {
546             sendConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
547         }
548 
549         /**
550          * Informs the {@link android.telecom.ConnectionService} of a connection event raised by the
551          * original connection.
552          * @param event The connection event.
553          * @param extras The extras.
554          */
555         @Override
556         public void onConnectionEvent(String event, Bundle extras) {
557             sendConnectionEvent(event, extras);
558         }
559 
560         @Override
561         public void onRttModifyRequestReceived() {
562             sendRemoteRttRequest();
563         }
564 
565         @Override
566         public void onRttModifyResponseReceived(int status) {
567             updateConnectionProperties();
568             refreshConferenceSupported();
569             if (status == RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
570                 sendRttInitiationSuccess();
571             } else {
572                 sendRttInitiationFailure(status);
573             }
574         }
575 
576         @Override
577         public void onDisconnect(int cause) {
578             Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(),
579                     DisconnectCause.toString(cause));
580             mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget();
581         }
582 
583         @Override
584         public void onRttInitiated() {
585             if (mOriginalConnection != null) {
586                 // if mOriginalConnection is null, the properties will get set when
587                 // mOriginalConnection gets set.
588                 updateConnectionProperties();
589                 refreshConferenceSupported();
590             }
591             sendRttInitiationSuccess();
592         }
593 
594         @Override
595         public void onRttTerminated() {
596             updateConnectionProperties();
597             sendRttSessionRemotelyTerminated();
598         }
599 
600         @Override
601         public void onOriginalConnectionReplaced(
602                 com.android.internal.telephony.Connection newConnection) {
603             setOriginalConnection(newConnection);
604         }
605 
606         @Override
607         public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) {
608             setIsNetworkIdentifiedEmergencyCall(isEmergencyCall);
609         }
610     };
611 
612     protected com.android.internal.telephony.Connection mOriginalConnection;
613     private Call.State mConnectionState = Call.State.IDLE;
614     private Bundle mOriginalConnectionExtras = new Bundle();
615     private boolean mIsStateOverridden = false;
616     private Call.State mOriginalConnectionState = Call.State.IDLE;
617     private Call.State mConnectionOverriddenState = Call.State.IDLE;
618     private RttTextStream mRttTextStream = null;
619 
620     private boolean mWasImsConnection;
621 
622     /**
623      * Tracks the multiparty state of the ImsCall so that changes in the bit state can be detected.
624      */
625     private boolean mIsMultiParty = false;
626 
627     /**
628      * The {@link com.android.internal.telephony.Connection} capabilities associated with the
629      * current {@link #mOriginalConnection}.
630      */
631     private int mOriginalConnectionCapabilities;
632 
633     /**
634      * Determines the audio quality is high for the {@link TelephonyConnection}.
635      * This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to
636      * indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
637      */
638     private boolean mHasHighDefAudio;
639 
640     /**
641      * Indicates that the connection should be treated as an emergency call because the
642      * number dialed matches an internal list of emergency numbers. Does not guarantee whether
643      * the network will treat the call as an emergency call.
644      */
645     private boolean mTreatAsEmergencyCall;
646 
647     /**
648      * Indicates whether the network has identified this call as an emergency call.  Where
649      * {@link #mTreatAsEmergencyCall} is based on comparing dialed numbers to a list of known
650      * emergency numbers, this property is based on whether the network itself has identified the
651      * call as an emergency call (which can be the case for an incoming call from emergency
652      * services).
653      */
654     private boolean mIsNetworkIdentifiedEmergencyCall;
655 
656     /**
657      * For video calls, indicates whether the outgoing video for the call can be paused using
658      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
659      */
660     private boolean mIsVideoPauseSupported;
661 
662     /**
663      * Indicates whether this connection supports being a part of a conference..
664      */
665     private boolean mIsConferenceSupported;
666 
667     /**
668      * Indicates whether managing conference call is supported after this connection being
669      * a part of a IMS conference.
670      */
671     private boolean mIsManageImsConferenceCallSupported;
672 
673     /**
674      * Indicates whether the carrier supports video conferencing; captures the current state of the
675      * carrier config
676      * {@link android.telephony.CarrierConfigManager#KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL}.
677      */
678     private boolean mIsCarrierVideoConferencingSupported;
679 
680     /**
681      * Indicates whether or not this connection has CDMA Enhanced Voice Privacy enabled.
682      */
683     private boolean mIsCdmaVoicePrivacyEnabled;
684 
685     /**
686      * Indicates whether this call is an outgoing call.
687      */
688     protected final boolean mIsOutgoing;
689 
690     /**
691      * Indicates whether the connection can be held. This filed combined with the state of the
692      * connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the
693      * connection.
694      */
695     private boolean mIsHoldable;
696 
697     /**
698      * Indicates whether TTY is enabled; used to determine whether a call is VT capable.
699      */
700     private boolean mIsTtyEnabled;
701 
702     /**
703      * Indicates whether this call is using assisted dialing.
704      */
705     private boolean mIsUsingAssistedDialing;
706 
707     /**
708      * Indicates whether this connection supports showing preciese call failed cause.
709      */
710     private boolean mShowPreciseFailedCause;
711 
712     /**
713      * Listeners to our TelephonyConnection specific callbacks
714      */
715     private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
716             new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
717 
TelephonyConnection(com.android.internal.telephony.Connection originalConnection, String callId, boolean isOutgoingCall)718     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
719             String callId, boolean isOutgoingCall) {
720         mIsOutgoing = isOutgoingCall;
721         setTelecomCallId(callId);
722         if (originalConnection != null) {
723             setOriginalConnection(originalConnection);
724         }
725     }
726 
727     /**
728      * Creates a clone of the current {@link TelephonyConnection}.
729      *
730      * @return The clone.
731      */
cloneConnection()732     public abstract TelephonyConnection cloneConnection();
733 
734     @Override
onCallAudioStateChanged(CallAudioState audioState)735     public void onCallAudioStateChanged(CallAudioState audioState) {
736         // TODO: update TTY mode.
737         if (getPhone() != null) {
738             getPhone().setEchoSuppressionEnabled();
739         }
740     }
741 
742     @Override
onStateChanged(int state)743     public void onStateChanged(int state) {
744         Log.v(this, "onStateChanged, state: " + Connection.stateToString(state));
745         updateStatusHints();
746     }
747 
748     @Override
onDisconnect()749     public void onDisconnect() {
750         Log.v(this, "onDisconnect");
751         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
752     }
753 
754     /**
755      * Notifies this Connection of a request to disconnect a participant of the conference managed
756      * by the connection.
757      *
758      * @param endpoint the {@link Uri} of the participant to disconnect.
759      */
760     @Override
onDisconnectConferenceParticipant(Uri endpoint)761     public void onDisconnectConferenceParticipant(Uri endpoint) {
762         Log.v(this, "onDisconnectConferenceParticipant %s", endpoint);
763 
764         if (mOriginalConnection == null) {
765             return;
766         }
767 
768         mOriginalConnection.onDisconnectConferenceParticipant(endpoint);
769     }
770 
771     @Override
onSeparate()772     public void onSeparate() {
773         Log.v(this, "onSeparate");
774         if (mOriginalConnection != null) {
775             try {
776                 mOriginalConnection.separate();
777             } catch (CallStateException e) {
778                 Log.e(this, e, "Call to Connection.separate failed with exception");
779             }
780         }
781     }
782 
783     @Override
onAbort()784     public void onAbort() {
785         Log.v(this, "onAbort");
786         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
787     }
788 
789     @Override
onHold()790     public void onHold() {
791         performHold();
792     }
793 
794     @Override
onUnhold()795     public void onUnhold() {
796         performUnhold();
797     }
798 
799     @Override
onAnswer(int videoState)800     public void onAnswer(int videoState) {
801         Log.v(this, "onAnswer");
802         if (isValidRingingCall() && getPhone() != null) {
803             try {
804                 getPhone().acceptCall(videoState);
805             } catch (CallStateException e) {
806                 Log.e(this, e, "Failed to accept call.");
807             }
808         }
809     }
810 
811     @Override
onDeflect(Uri address)812     public void onDeflect(Uri address) {
813         Log.v(this, "onDeflect");
814         if (mOriginalConnection != null && isValidRingingCall()) {
815             if (address == null) {
816                 Log.w(this, "call deflect address uri is null");
817                 return;
818             }
819             String scheme = address.getScheme();
820             String deflectNumber = "";
821             String uriString = address.getSchemeSpecificPart();
822             if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
823                 if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
824                     Log.w(this, "onDeflect, address scheme is not of type tel instead: " +
825                             scheme);
826                     return;
827                 }
828                 if (PhoneNumberUtils.isUriNumber(uriString)) {
829                     Log.w(this, "Invalid deflect address. Not a legal PSTN number.");
830                     return;
831                 }
832                 deflectNumber = PhoneNumberUtils.convertAndStrip(uriString);
833                 if (TextUtils.isEmpty(deflectNumber)) {
834                     Log.w(this, "Empty deflect number obtained from address uri");
835                     return;
836                 }
837             } else {
838                 Log.w(this, "Cannot deflect to voicemail uri");
839                 return;
840             }
841 
842             try {
843                 mOriginalConnection.deflect(deflectNumber);
844             } catch (CallStateException e) {
845                 Log.e(this, e, "Failed to deflect call.");
846             }
847         }
848     }
849 
850     @Override
onReject()851     public void onReject() {
852         Log.v(this, "onReject");
853         if (isValidRingingCall()) {
854             mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
855                     .sendToTarget();
856         }
857         super.onReject();
858     }
859 
860     @Override
onPostDialContinue(boolean proceed)861     public void onPostDialContinue(boolean proceed) {
862         Log.v(this, "onPostDialContinue, proceed: " + proceed);
863         if (mOriginalConnection != null) {
864             if (proceed) {
865                 mOriginalConnection.proceedAfterWaitChar();
866             } else {
867                 mOriginalConnection.cancelPostDial();
868             }
869         }
870     }
871 
872     /**
873      * Handles requests to pull an external call.
874      */
875     @Override
onPullExternalCall()876     public void onPullExternalCall() {
877         if ((getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) !=
878                 Connection.PROPERTY_IS_EXTERNAL_CALL) {
879             Log.w(this, "onPullExternalCall - cannot pull non-external call");
880             return;
881         }
882 
883         if (mOriginalConnection != null) {
884             mOriginalConnection.pullExternalCall();
885         }
886     }
887 
888     @Override
onStartRtt(RttTextStream textStream)889     public void onStartRtt(RttTextStream textStream) {
890         if (isImsConnection()) {
891             ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
892             if (originalConnection.isRttEnabledForCall()) {
893                 originalConnection.setCurrentRttTextStream(textStream);
894             } else {
895                 originalConnection.startRtt(textStream);
896             }
897         } else {
898             Log.w(this, "onStartRtt - not in IMS, so RTT cannot be enabled.");
899         }
900     }
901 
902     @Override
onStopRtt()903     public void onStopRtt() {
904         if (isImsConnection()) {
905             ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
906             if (originalConnection.isRttEnabledForCall()) {
907                 originalConnection.stopRtt();
908             } else {
909                 Log.w(this, "onStopRtt - not in RTT call, ignoring");
910             }
911         } else {
912             Log.w(this, "onStopRtt - not in IMS, ignoring");
913         }
914     }
915 
916     @Override
handleRttUpgradeResponse(RttTextStream textStream)917     public void handleRttUpgradeResponse(RttTextStream textStream) {
918         if (!isImsConnection()) {
919             Log.w(this, "handleRttUpgradeResponse - not in IMS, so RTT cannot be enabled.");
920             return;
921         }
922         ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
923         originalConnection.sendRttModifyResponse(textStream);
924     }
925 
performHold()926     public void performHold() {
927         Log.v(this, "performHold");
928         // TODO: Can dialing calls be put on hold as well since they take up the
929         // foreground call slot?
930         if (Call.State.ACTIVE == mConnectionState) {
931             Log.v(this, "Holding active call");
932             try {
933                 Phone phone = mOriginalConnection.getCall().getPhone();
934 
935                 Call ringingCall = phone.getRingingCall();
936 
937                 // Although the method says switchHoldingAndActive, it eventually calls a RIL method
938                 // called switchWaitingOrHoldingAndActive. What this means is that if we try to put
939                 // a call on hold while a call-waiting call exists, it'll end up accepting the
940                 // call-waiting call, which is bad if that was not the user's intention. We are
941                 // cheating here and simply skipping it because we know any attempt to hold a call
942                 // while a call-waiting call is happening is likely a request from Telecom prior to
943                 // accepting the call-waiting call.
944                 // TODO: Investigate a better solution. It would be great here if we
945                 // could "fake" hold by silencing the audio and microphone streams for this call
946                 // instead of actually putting it on hold.
947                 if (ringingCall.getState() != Call.State.WAITING) {
948                     // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
949                     if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
950                         ImsPhone imsPhone = (ImsPhone) phone;
951                         imsPhone.holdActiveCall();
952                         return;
953                     }
954                     phone.switchHoldingAndActive();
955                 }
956 
957                 // TODO: Cdma calls are slightly different.
958             } catch (CallStateException e) {
959                 Log.e(this, e, "Exception occurred while trying to put call on hold.");
960             }
961         } else {
962             Log.w(this, "Cannot put a call that is not currently active on hold.");
963         }
964     }
965 
performUnhold()966     public void performUnhold() {
967         Log.v(this, "performUnhold");
968         if (Call.State.HOLDING == mConnectionState) {
969             try {
970                 Phone phone = mOriginalConnection.getCall().getPhone();
971                 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
972                 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
973                     ImsPhone imsPhone = (ImsPhone) phone;
974                     imsPhone.unholdHeldCall();
975                     return;
976                 }
977                 // Here's the deal--Telephony hold/unhold is weird because whenever there exists
978                 // more than one call, one of them must always be active. In other words, if you
979                 // have an active call and holding call, and you put the active call on hold, it
980                 // will automatically activate the holding call. This is weird with how Telecom
981                 // sends its commands. When a user opts to "unhold" a background call, telecom
982                 // issues hold commands to all active calls, and then the unhold command to the
983                 // background call. This means that we get two commands...each of which reduces to
984                 // switchHoldingAndActive(). The result is that they simply cancel each other out.
985                 // To fix this so that it works well with telecom we add a minor hack. If we
986                 // have one telephony call, everything works as normally expected. But if we have
987                 // two or more calls, we will ignore all requests to "unhold" knowing that the hold
988                 // requests already do what we want. If you've read up to this point, I'm very sorry
989                 // that we are doing this. I didn't think of a better solution that wouldn't also
990                 // make the Telecom APIs very ugly.
991 
992                 if (!hasMultipleTopLevelCalls()) {
993                     mOriginalConnection.getCall().getPhone().switchHoldingAndActive();
994                 } else {
995                     Log.i(this, "Skipping unhold command for %s", this);
996                 }
997             } catch (CallStateException e) {
998                 Log.e(this, e, "Exception occurred while trying to release call from hold.");
999             }
1000         } else {
1001             Log.w(this, "Cannot release a call that is not already on hold from hold.");
1002         }
1003     }
1004 
performConference(Connection otherConnection)1005     public void performConference(Connection otherConnection) {
1006         Log.d(this, "performConference - %s", this);
1007         if (getPhone() != null) {
1008             try {
1009                 // We dont use the "other" connection because there is no concept of that in the
1010                 // implementation of calls inside telephony. Basically, you can "conference" and it
1011                 // will conference with the background call.  We know that otherConnection is the
1012                 // background call because it would never have called setConferenceableConnections()
1013                 // otherwise.
1014                 getPhone().conference();
1015             } catch (CallStateException e) {
1016                 Log.e(this, e, "Failed to conference call.");
1017             }
1018         }
1019     }
1020 
1021     /**
1022      * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
1023      * capabilities.
1024      */
buildConnectionCapabilities()1025     protected int buildConnectionCapabilities() {
1026         int callCapabilities = 0;
1027         if (mOriginalConnection != null && mOriginalConnection.isIncoming()) {
1028             callCapabilities |= CAPABILITY_SPEED_UP_MT_AUDIO;
1029         }
1030         if (!shouldTreatAsEmergencyCall() && isImsConnection() && canHoldImsCalls()) {
1031             callCapabilities |= CAPABILITY_SUPPORT_HOLD;
1032             if (mIsHoldable && (getState() == STATE_ACTIVE || getState() == STATE_HOLDING)) {
1033                 callCapabilities |= CAPABILITY_HOLD;
1034             }
1035         }
1036 
1037         Log.d(this, "buildConnectionCapabilities: isHoldable = "
1038                 + mIsHoldable + " State = " + getState() + " capabilities = " + callCapabilities);
1039 
1040         return callCapabilities;
1041     }
1042 
updateConnectionCapabilities()1043     protected final void updateConnectionCapabilities() {
1044         int newCapabilities = buildConnectionCapabilities();
1045 
1046         newCapabilities = applyOriginalConnectionCapabilities(newCapabilities);
1047         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO,
1048                 mIsVideoPauseSupported && isVideoCapable());
1049         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
1050                 isExternalConnection() && isPullable());
1051         newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
1052         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
1053                 isImsConnection() && canDeflectImsCalls());
1054 
1055         if (getConnectionCapabilities() != newCapabilities) {
1056             setConnectionCapabilities(newCapabilities);
1057         }
1058     }
1059 
buildConnectionProperties()1060     protected int buildConnectionProperties() {
1061         int connectionProperties = 0;
1062 
1063         // If the phone is in ECM mode, mark the call to indicate that the callback number should be
1064         // shown.
1065         Phone phone = getPhone();
1066         if (phone != null && phone.isInEcm()) {
1067             connectionProperties |= PROPERTY_EMERGENCY_CALLBACK_MODE;
1068         }
1069 
1070         return connectionProperties;
1071     }
1072 
1073     /**
1074      * Updates the properties of the connection.
1075      */
updateConnectionProperties()1076     protected final void updateConnectionProperties() {
1077         int newProperties = buildConnectionProperties();
1078 
1079         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
1080                 hasHighDefAudioProperty());
1081         newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi());
1082         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
1083                 isExternalConnection());
1084         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
1085                 mIsCdmaVoicePrivacyEnabled);
1086         newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
1087                 mIsUsingAssistedDialing);
1088         newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
1089         newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
1090                 isNetworkIdentifiedEmergencyCall());
1091 
1092         if (getConnectionProperties() != newProperties) {
1093             setConnectionProperties(newProperties);
1094         }
1095     }
1096 
updateAddress()1097     protected final void updateAddress() {
1098         updateConnectionCapabilities();
1099         updateConnectionProperties();
1100         if (mOriginalConnection != null) {
1101             Uri address;
1102             if (isShowingOriginalDialString()
1103                     && mOriginalConnection.getOrigDialString() != null) {
1104                 address = getAddressFromNumber(mOriginalConnection.getOrigDialString());
1105             } else {
1106                 address = getAddressFromNumber(mOriginalConnection.getAddress());
1107             }
1108             int presentation = mOriginalConnection.getNumberPresentation();
1109             if (!Objects.equals(address, getAddress()) ||
1110                     presentation != getAddressPresentation()) {
1111                 Log.v(this, "updateAddress, address changed");
1112                 if ((getConnectionProperties() & PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
1113                     address = null;
1114                 }
1115                 setAddress(address, presentation);
1116             }
1117 
1118             String name = filterCnapName(mOriginalConnection.getCnapName());
1119             int namePresentation = mOriginalConnection.getCnapNamePresentation();
1120             if (!Objects.equals(name, getCallerDisplayName()) ||
1121                     namePresentation != getCallerDisplayNamePresentation()) {
1122                 Log.v(this, "updateAddress, caller display name changed");
1123                 setCallerDisplayName(name, namePresentation);
1124             }
1125 
1126             if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
1127                 mTreatAsEmergencyCall = true;
1128             }
1129 
1130             // Changing the address of the connection can change whether it is an emergency call or
1131             // not, which can impact whether it can be part of a conference.
1132             refreshConferenceSupported();
1133         }
1134     }
1135 
onRemovedFromCallService()1136     void onRemovedFromCallService() {
1137         // Subclass can override this to do cleanup.
1138     }
1139 
setOriginalConnection(com.android.internal.telephony.Connection originalConnection)1140     void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
1141         Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection);
1142         if (mOriginalConnection != null && originalConnection != null
1143                && !originalConnection.isIncoming()
1144                && originalConnection.getOrigDialString() == null
1145                && isShowingOriginalDialString()) {
1146             Log.i(this, "new original dial string is null, convert to: "
1147                    +  mOriginalConnection.getOrigDialString());
1148             originalConnection.setConverted(mOriginalConnection.getOrigDialString());
1149         }
1150 
1151         clearOriginalConnection();
1152         mOriginalConnectionExtras.clear();
1153         mOriginalConnection = originalConnection;
1154         mOriginalConnection.setTelecomCallId(getTelecomCallId());
1155         getPhone().registerForPreciseCallStateChanged(
1156                 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
1157         getPhone().registerForHandoverStateChanged(
1158                 mHandler, MSG_HANDOVER_STATE_CHANGED, null);
1159         getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
1160         getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
1161         getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
1162         getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
1163         getPhone().registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null);
1164         mOriginalConnection.addPostDialListener(mPostDialListener);
1165         mOriginalConnection.addListener(mOriginalConnectionListener);
1166 
1167         // Set video state and capabilities
1168         setVideoState(mOriginalConnection.getVideoState());
1169         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
1170         setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
1171         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
1172         setVideoProvider(mOriginalConnection.getVideoProvider());
1173         setAudioQuality(mOriginalConnection.getAudioQuality());
1174         setTechnologyTypeExtra();
1175 
1176         setCallRadioTech(mOriginalConnection.getCallRadioTech());
1177 
1178         // Post update of extras to the handler; extras are updated via the handler to ensure thread
1179         // safety. The Extras Bundle is cloned in case the original extras are modified while they
1180         // are being added to mOriginalConnectionExtras in updateExtras.
1181         Bundle connExtras = mOriginalConnection.getConnectionExtras();
1182             mHandler.obtainMessage(MSG_CONNECTION_EXTRAS_CHANGED, connExtras == null ? null :
1183                     new Bundle(connExtras)).sendToTarget();
1184 
1185         if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
1186             mTreatAsEmergencyCall = true;
1187         }
1188 
1189         if (isImsConnection()) {
1190             mWasImsConnection = true;
1191         }
1192         mIsMultiParty = mOriginalConnection.isMultiparty();
1193 
1194         Bundle extrasToPut = new Bundle();
1195         List<String> extrasToRemove = new ArrayList<>();
1196         if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
1197             extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
1198         } else {
1199             extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
1200         }
1201 
1202         if (shouldSetDisableAddCallExtra()) {
1203             extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
1204         } else {
1205             extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
1206         }
1207         putExtras(extrasToPut);
1208         removeExtras(extrasToRemove);
1209 
1210         // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
1211         // should be executed *after* the above setters have run.
1212         updateState();
1213         if (mOriginalConnection == null) {
1214             Log.w(this, "original Connection was nulled out as part of setOriginalConnection. " +
1215                     originalConnection);
1216         }
1217 
1218         fireOnOriginalConnectionConfigured();
1219     }
1220 
1221     /**
1222      * Filters the CNAP name to not include a list of names that are unhelpful to the user for
1223      * Caller ID purposes.
1224      */
filterCnapName(final String cnapName)1225     private String filterCnapName(final String cnapName) {
1226         if (cnapName == null) {
1227             return null;
1228         }
1229         PersistableBundle carrierConfig = getCarrierConfig();
1230         String[] filteredCnapNames = null;
1231         if (carrierConfig != null) {
1232             filteredCnapNames = carrierConfig.getStringArray(
1233                     CarrierConfigManager.KEY_FILTERED_CNAP_NAMES_STRING_ARRAY);
1234         }
1235         if (filteredCnapNames != null) {
1236             long cnapNameMatches = Arrays.asList(filteredCnapNames)
1237                     .stream()
1238                     .filter(filteredCnapName -> filteredCnapName.equals(cnapName.toUpperCase()))
1239                     .count();
1240             if (cnapNameMatches > 0) {
1241                 Log.i(this, "filterCnapName: Filtered CNAP Name: " + cnapName);
1242                 return "";
1243             }
1244         }
1245         return cnapName;
1246     }
1247 
1248     /**
1249      * Sets the EXTRA_CALL_TECHNOLOGY_TYPE extra on the connection to report back to Telecom.
1250      */
setTechnologyTypeExtra()1251     private void setTechnologyTypeExtra() {
1252         if (getPhone() != null) {
1253             putExtra(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
1254         }
1255     }
1256 
refreshDisableAddCall()1257     private void refreshDisableAddCall() {
1258         if (shouldSetDisableAddCallExtra()) {
1259             putExtra(Connection.EXTRA_DISABLE_ADD_CALL, true);
1260         } else {
1261             removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
1262         }
1263     }
1264 
shouldSetDisableAddCallExtra()1265     private boolean shouldSetDisableAddCallExtra() {
1266         if (mOriginalConnection == null) {
1267             return false;
1268         }
1269         boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall();
1270         if (carrierShouldAllowAddCall) {
1271             return false;
1272         }
1273         Phone phone = getPhone();
1274         if (phone == null) {
1275             return false;
1276         }
1277         boolean isCurrentVideoCall = false;
1278         boolean wasVideoCall = false;
1279         boolean isVowifiEnabled = false;
1280         if (phone instanceof ImsPhone) {
1281             ImsPhone imsPhone = (ImsPhone) phone;
1282             if (imsPhone.getForegroundCall() != null
1283                     && imsPhone.getForegroundCall().getImsCall() != null) {
1284                 ImsCall call = imsPhone.getForegroundCall().getImsCall();
1285                 isCurrentVideoCall = call.isVideoCall();
1286                 wasVideoCall = call.wasVideoCall();
1287             }
1288 
1289             isVowifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
1290         }
1291 
1292         if (isCurrentVideoCall) {
1293             return true;
1294         } else if (wasVideoCall && isWifi() && !isVowifiEnabled) {
1295             return true;
1296         }
1297         return false;
1298     }
1299 
hasHighDefAudioProperty()1300     private boolean hasHighDefAudioProperty() {
1301         if (!mHasHighDefAudio) {
1302             return false;
1303         }
1304 
1305         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
1306 
1307         PersistableBundle b = getCarrierConfig();
1308         boolean canWifiCallsBeHdAudio =
1309                 b != null && b.getBoolean(CarrierConfigManager.KEY_WIFI_CALLS_CAN_BE_HD_AUDIO);
1310         boolean canVideoCallsBeHdAudio =
1311                 b != null && b.getBoolean(CarrierConfigManager.KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO);
1312         boolean canGsmCdmaCallsBeHdAudio =
1313                 b != null && b.getBoolean(CarrierConfigManager.KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO);
1314         boolean shouldDisplayHdAudio =
1315                 b != null && b.getBoolean(CarrierConfigManager.KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL);
1316 
1317         if (!shouldDisplayHdAudio) {
1318             return false;
1319         }
1320 
1321         if (isGsmCdmaConnection() && !canGsmCdmaCallsBeHdAudio) {
1322             return false;
1323         }
1324 
1325         if (isVideoCall && !canVideoCallsBeHdAudio) {
1326             return false;
1327         }
1328 
1329         if (isWifi() && !canWifiCallsBeHdAudio) {
1330             return false;
1331         }
1332 
1333         return true;
1334     }
1335 
canHoldImsCalls()1336     private boolean canHoldImsCalls() {
1337         PersistableBundle b = getCarrierConfig();
1338         // Return true if the CarrierConfig is unavailable
1339         return !doesDeviceRespectHoldCarrierConfig() || b == null ||
1340                 b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
1341     }
1342 
getCarrierConfig()1343     private PersistableBundle getCarrierConfig() {
1344         Phone phone = getPhone();
1345         if (phone == null) {
1346             return null;
1347         }
1348         return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
1349     }
1350 
canDeflectImsCalls()1351     private boolean canDeflectImsCalls() {
1352         PersistableBundle b = getCarrierConfig();
1353         // Return false if the CarrierConfig is unavailable
1354         if (b != null) {
1355             return b.getBoolean(
1356                     CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) &&
1357                     isValidRingingCall();
1358         }
1359         return false;
1360     }
1361 
1362     /**
1363      * Determines if the device will respect the value of the
1364      * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
1365      *
1366      * @return {@code false} if the device always supports holding IMS calls, {@code true} if it
1367      *      will use {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} to determine if
1368      *      hold is supported.
1369      */
doesDeviceRespectHoldCarrierConfig()1370     private boolean doesDeviceRespectHoldCarrierConfig() {
1371         Phone phone = getPhone();
1372         if (phone == null) {
1373             return true;
1374         }
1375         return phone.getContext().getResources().getBoolean(
1376                 com.android.internal.R.bool.config_device_respects_hold_carrier_config);
1377     }
1378 
1379     /**
1380      * Whether the connection should be treated as an emergency.
1381      * @return {@code true} if the connection should be treated as an emergency call based
1382      * on the number dialed, {@code false} otherwise.
1383      */
shouldTreatAsEmergencyCall()1384     protected boolean shouldTreatAsEmergencyCall() {
1385         return mTreatAsEmergencyCall;
1386     }
1387 
1388     /**
1389      * Un-sets the underlying radio connection.
1390      */
clearOriginalConnection()1391     void clearOriginalConnection() {
1392         if (mOriginalConnection != null) {
1393             if (getPhone() != null) {
1394                 getPhone().unregisterForPreciseCallStateChanged(mHandler);
1395                 getPhone().unregisterForRingbackTone(mHandler);
1396                 getPhone().unregisterForHandoverStateChanged(mHandler);
1397                 getPhone().unregisterForDisconnect(mHandler);
1398                 getPhone().unregisterForSuppServiceNotification(mHandler);
1399                 getPhone().unregisterForOnHoldTone(mHandler);
1400                 getPhone().unregisterForInCallVoicePrivacyOn(mHandler);
1401                 getPhone().unregisterForInCallVoicePrivacyOff(mHandler);
1402             }
1403             mOriginalConnection.removePostDialListener(mPostDialListener);
1404             mOriginalConnection.removeListener(mOriginalConnectionListener);
1405             mOriginalConnection = null;
1406         }
1407     }
1408 
hangup(int telephonyDisconnectCode)1409     protected void hangup(int telephonyDisconnectCode) {
1410         if (mOriginalConnection != null) {
1411             try {
1412                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
1413                 // connection.hangup(). Without this change, the party originating the call
1414                 // will not get sent to voicemail if the user opts to reject the call.
1415                 if (isValidRingingCall()) {
1416                     Call call = getCall();
1417                     if (call != null) {
1418                         call.hangup();
1419                     } else {
1420                         Log.w(this, "Attempting to hangup a connection without backing call.");
1421                     }
1422                 } else {
1423                     // We still prefer to call connection.hangup() for non-ringing calls
1424                     // in order to support hanging-up specific calls within a conference call.
1425                     // If we invoked call.hangup() while in a conference, we would end up
1426                     // hanging up the entire conference call instead of the specific connection.
1427                     mOriginalConnection.hangup();
1428                 }
1429             } catch (CallStateException e) {
1430                 Log.e(this, e, "Call to Connection.hangup failed with exception");
1431             }
1432         } else {
1433             if (getState() == STATE_DISCONNECTED) {
1434                 Log.i(this, "hangup called on an already disconnected call!");
1435                 close();
1436             } else {
1437                 // There are a few cases where mOriginalConnection has not been set yet. For
1438                 // example, when the radio has to be turned on to make an emergency call,
1439                 // mOriginalConnection could not be set for many seconds.
1440                 setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
1441                         android.telephony.DisconnectCause.LOCAL,
1442                         "Local Disconnect before connection established."));
1443                 close();
1444             }
1445         }
1446     }
1447 
getOriginalConnection()1448     com.android.internal.telephony.Connection getOriginalConnection() {
1449         return mOriginalConnection;
1450     }
1451 
getCall()1452     protected Call getCall() {
1453         if (mOriginalConnection != null) {
1454             return mOriginalConnection.getCall();
1455         }
1456         return null;
1457     }
1458 
getPhone()1459     Phone getPhone() {
1460         Call call = getCall();
1461         if (call != null) {
1462             return call.getPhone();
1463         }
1464         return null;
1465     }
1466 
hasMultipleTopLevelCalls()1467     private boolean hasMultipleTopLevelCalls() {
1468         int numCalls = 0;
1469         Phone phone = getPhone();
1470         if (phone != null) {
1471             if (!phone.getRingingCall().isIdle()) {
1472                 numCalls++;
1473             }
1474             if (!phone.getForegroundCall().isIdle()) {
1475                 numCalls++;
1476             }
1477             if (!phone.getBackgroundCall().isIdle()) {
1478                 numCalls++;
1479             }
1480         }
1481         return numCalls > 1;
1482     }
1483 
getForegroundConnection()1484     private com.android.internal.telephony.Connection getForegroundConnection() {
1485         if (getPhone() != null) {
1486             return getPhone().getForegroundCall().getEarliestConnection();
1487         }
1488         return null;
1489     }
1490 
1491      /**
1492      * Checks for and returns the list of conference participants
1493      * associated with this connection.
1494      */
getConferenceParticipants()1495     public List<ConferenceParticipant> getConferenceParticipants() {
1496         if (mOriginalConnection == null) {
1497             Log.v(this, "Null mOriginalConnection, cannot get conf participants.");
1498             return null;
1499         }
1500         return mOriginalConnection.getConferenceParticipants();
1501     }
1502 
1503     /**
1504      * Checks to see the original connection corresponds to an active incoming call. Returns false
1505      * if there is no such actual call, or if the associated call is not incoming (See
1506      * {@link Call.State#isRinging}).
1507      */
isValidRingingCall()1508     private boolean isValidRingingCall() {
1509         if (getPhone() == null) {
1510             Log.v(this, "isValidRingingCall, phone is null");
1511             return false;
1512         }
1513 
1514         Call ringingCall = getPhone().getRingingCall();
1515         if (!ringingCall.getState().isRinging()) {
1516             Log.v(this, "isValidRingingCall, ringing call is not in ringing state");
1517             return false;
1518         }
1519 
1520         if (ringingCall.getEarliestConnection() != mOriginalConnection) {
1521             Log.v(this, "isValidRingingCall, ringing call connection does not match");
1522             return false;
1523         }
1524 
1525         Log.v(this, "isValidRingingCall, returning true");
1526         return true;
1527     }
1528 
1529     // Make sure the extras being passed into this method is a COPY of the original extras Bundle.
1530     // We do not want the extras to be cleared or modified during mOriginalConnectionExtras.putAll
1531     // below.
updateExtras(Bundle extras)1532     protected void updateExtras(Bundle extras) {
1533         if (mOriginalConnection != null) {
1534             if (extras != null) {
1535                 // Check if extras have changed and need updating.
1536                 if (!areBundlesEqual(mOriginalConnectionExtras, extras)) {
1537                     if (Log.DEBUG) {
1538                         Log.d(TelephonyConnection.this, "Updating extras:");
1539                         for (String key : extras.keySet()) {
1540                             Object value = extras.get(key);
1541                             if (value instanceof String) {
1542                                 Log.d(this, "updateExtras Key=" + Log.pii(key) +
1543                                              " value=" + Log.pii((String)value));
1544                             }
1545                         }
1546                     }
1547                     mOriginalConnectionExtras.clear();
1548 
1549                     mOriginalConnectionExtras.putAll(extras);
1550 
1551                     // Remap any string extras that have a remapping defined.
1552                     for (String key : mOriginalConnectionExtras.keySet()) {
1553                         if (sExtrasMap.containsKey(key)) {
1554                             String newKey = sExtrasMap.get(key);
1555                             mOriginalConnectionExtras.putString(newKey, extras.getString(key));
1556                             mOriginalConnectionExtras.remove(key);
1557                         }
1558                     }
1559 
1560                     // Ensure extras are propagated to Telecom.
1561                     putExtras(mOriginalConnectionExtras);
1562                 } else {
1563                     Log.d(this, "Extras update not required");
1564                 }
1565             } else {
1566                 Log.d(this, "updateExtras extras: " + Log.pii(extras));
1567             }
1568         }
1569     }
1570 
areBundlesEqual(Bundle extras, Bundle newExtras)1571     private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
1572         if (extras == null || newExtras == null) {
1573             return extras == newExtras;
1574         }
1575 
1576         if (extras.size() != newExtras.size()) {
1577             return false;
1578         }
1579 
1580         for(String key : extras.keySet()) {
1581             if (key != null) {
1582                 final Object value = extras.get(key);
1583                 final Object newValue = newExtras.get(key);
1584                 if (!Objects.equals(value, newValue)) {
1585                     return false;
1586                 }
1587             }
1588         }
1589         return true;
1590     }
1591 
setStateOverride(Call.State state)1592     void setStateOverride(Call.State state) {
1593         mIsStateOverridden = true;
1594         mConnectionOverriddenState = state;
1595         // Need to keep track of the original connection's state before override.
1596         mOriginalConnectionState = mOriginalConnection.getState();
1597         updateStateInternal();
1598     }
1599 
resetStateOverride()1600     void resetStateOverride() {
1601         mIsStateOverridden = false;
1602         updateStateInternal();
1603     }
1604 
updateStateInternal()1605     void updateStateInternal() {
1606         if (mOriginalConnection == null) {
1607             return;
1608         }
1609         Call.State newState;
1610         // If the state is overridden and the state of the original connection hasn't changed since,
1611         // then we continue in the overridden state, else we go to the original connection's state.
1612         if (mIsStateOverridden && mOriginalConnectionState == mOriginalConnection.getState()) {
1613             newState = mConnectionOverriddenState;
1614         } else {
1615             newState = mOriginalConnection.getState();
1616         }
1617         int cause = mOriginalConnection.getDisconnectCause();
1618         Log.v(this, "Update state from %s to %s for %s", mConnectionState, newState,
1619                 getTelecomCallId());
1620 
1621         if (mConnectionState != newState) {
1622             mConnectionState = newState;
1623             switch (newState) {
1624                 case IDLE:
1625                     break;
1626                 case ACTIVE:
1627                     setActiveInternal();
1628                     break;
1629                 case HOLDING:
1630                     setOnHold();
1631                     break;
1632                 case DIALING:
1633                 case ALERTING:
1634                     if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
1635                         setPulling();
1636                     } else {
1637                         setDialing();
1638                     }
1639                     break;
1640                 case INCOMING:
1641                 case WAITING:
1642                     setRinging();
1643                     break;
1644                 case DISCONNECTED:
1645                     if (shouldTreatAsEmergencyCall()
1646                             && (cause
1647                             == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
1648                             || cause
1649                             == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE)) {
1650                         // We can get into a situation where the radio wants us to redial the
1651                         // same emergency call on the other available slot. This will not set
1652                         // the state to disconnected and will instead tell the
1653                         // TelephonyConnectionService to
1654                         // create a new originalConnection using the new Slot.
1655                         fireOnOriginalConnectionRetryDial(cause
1656                                 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE);
1657                     } else {
1658                         int preciseDisconnectCause = CallFailCause.NOT_VALID;
1659                         if (mShowPreciseFailedCause) {
1660                             preciseDisconnectCause =
1661                                     mOriginalConnection.getPreciseDisconnectCause();
1662                         }
1663                         setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
1664                                 mOriginalConnection.getDisconnectCause(),
1665                                 preciseDisconnectCause,
1666                                 mOriginalConnection.getVendorDisconnectCause(),
1667                                 getPhone().getPhoneId()));
1668                         close();
1669                     }
1670                     break;
1671                 case DISCONNECTING:
1672                     break;
1673             }
1674         }
1675     }
1676 
updateState()1677     void updateState() {
1678         if (mOriginalConnection == null) {
1679             return;
1680         }
1681 
1682         updateStateInternal();
1683         updateStatusHints();
1684         updateConnectionCapabilities();
1685         updateConnectionProperties();
1686         updateAddress();
1687         updateMultiparty();
1688         refreshDisableAddCall();
1689     }
1690 
1691     /**
1692      * Checks for changes to the multiparty bit.  If a conference has started, informs listeners.
1693      */
updateMultiparty()1694     private void updateMultiparty() {
1695         if (mOriginalConnection == null) {
1696             return;
1697         }
1698 
1699         if (mIsMultiParty != mOriginalConnection.isMultiparty()) {
1700             mIsMultiParty = mOriginalConnection.isMultiparty();
1701 
1702             if (mIsMultiParty) {
1703                 notifyConferenceStarted();
1704             }
1705         }
1706     }
1707 
1708     /**
1709      * Handles a failure when merging calls into a conference.
1710      * {@link com.android.internal.telephony.Connection.Listener#onConferenceMergedFailed()}
1711      * listener.
1712      */
handleConferenceMergeFailed()1713     private void handleConferenceMergeFailed(){
1714         mHandler.obtainMessage(MSG_CONFERENCE_MERGE_FAILED).sendToTarget();
1715     }
1716 
1717     /**
1718      * Handles requests to update the multiparty state received via the
1719      * {@link com.android.internal.telephony.Connection.Listener#onMultipartyStateChanged(boolean)}
1720      * listener.
1721      * <p>
1722      * Note: We post this to the mHandler to ensure that if a conference must be created as a
1723      * result of the multiparty state change, the conference creation happens on the correct
1724      * thread.  This ensures that the thread check in
1725      * {@link com.android.internal.telephony.Phone#checkCorrectThread(android.os.Handler)}
1726      * does not fire.
1727      *
1728      * @param isMultiParty {@code true} if this connection is multiparty, {@code false} otherwise.
1729      */
handleMultipartyStateChange(boolean isMultiParty)1730     private void handleMultipartyStateChange(boolean isMultiParty) {
1731         Log.i(this, "Update multiparty state to %s", isMultiParty ? "Y" : "N");
1732         mHandler.obtainMessage(MSG_MULTIPARTY_STATE_CHANGED, isMultiParty).sendToTarget();
1733     }
1734 
setActiveInternal()1735     private void setActiveInternal() {
1736         if (getState() == STATE_ACTIVE) {
1737             Log.w(this, "Should not be called if this is already ACTIVE");
1738             return;
1739         }
1740 
1741         // When we set a call to active, we need to make sure that there are no other active
1742         // calls. However, the ordering of state updates to connections can be non-deterministic
1743         // since all connections register for state changes on the phone independently.
1744         // To "optimize", we check here to see if there already exists any active calls.  If so,
1745         // we issue an update for those calls first to make sure we only have one top-level
1746         // active call.
1747         if (getConnectionService() != null) {
1748             for (Connection current : getConnectionService().getAllConnections()) {
1749                 if (current != this && current instanceof TelephonyConnection) {
1750                     TelephonyConnection other = (TelephonyConnection) current;
1751                     if (other.getState() == STATE_ACTIVE) {
1752                         other.updateState();
1753                     }
1754                 }
1755             }
1756         }
1757         setActive();
1758     }
1759 
close()1760     protected void close() {
1761         Log.v(this, "close");
1762         clearOriginalConnection();
1763         destroy();
1764     }
1765 
1766     /**
1767      * Determines if the current connection is video capable.
1768      *
1769      * A connection is deemed to be video capable if the original connection capabilities state that
1770      * both local and remote video is supported.
1771      *
1772      * @return {@code true} if the connection is video capable, {@code false} otherwise.
1773      */
isVideoCapable()1774     private boolean isVideoCapable() {
1775         return can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
1776                 && can(mOriginalConnectionCapabilities,
1777                 Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
1778     }
1779 
1780     /**
1781      * Determines if the current connection is an external connection.
1782      *
1783      * A connection is deemed to be external if the original connection capabilities state that it
1784      * is.
1785      *
1786      * @return {@code true} if the connection is external, {@code false} otherwise.
1787      */
isExternalConnection()1788     private boolean isExternalConnection() {
1789         return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION);
1790     }
1791 
1792     /**
1793      * Determines if the current connection has RTT enabled.
1794      */
isRtt()1795     private boolean isRtt() {
1796         return mOriginalConnection != null
1797                 && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
1798                 && mOriginalConnection instanceof ImsPhoneConnection
1799                 && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall();
1800     }
1801 
1802     /**
1803      * Determines if the current connection is pullable.
1804      *
1805      * A connection is deemed to be pullable if the original connection capabilities state that it
1806      * is.
1807      *
1808      * @return {@code true} if the connection is pullable, {@code false} otherwise.
1809      */
isPullable()1810     private boolean isPullable() {
1811         return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION)
1812                 && can(mOriginalConnectionCapabilities, Capability.IS_PULLABLE);
1813     }
1814 
1815     /**
1816      * Sets whether or not CDMA enhanced call privacy is enabled for this connection.
1817      */
setCdmaVoicePrivacy(boolean isEnabled)1818     private void setCdmaVoicePrivacy(boolean isEnabled) {
1819         if(mIsCdmaVoicePrivacyEnabled != isEnabled) {
1820             mIsCdmaVoicePrivacyEnabled = isEnabled;
1821             updateConnectionProperties();
1822         }
1823     }
1824 
1825     /**
1826      * Applies capabilities specific to conferences termination to the
1827      * {@code ConnectionCapabilities} bit-mask.
1828      *
1829      * @param capabilities The {@code ConnectionCapabilities} bit-mask.
1830      * @return The capabilities with the IMS conference capabilities applied.
1831      */
applyConferenceTerminationCapabilities(int capabilities)1832     private int applyConferenceTerminationCapabilities(int capabilities) {
1833         int currentCapabilities = capabilities;
1834 
1835         // An IMS call cannot be individually disconnected or separated from its parent conference.
1836         // If the call was IMS, even if it hands over to GMS, these capabilities are not supported.
1837         if (!mWasImsConnection) {
1838             currentCapabilities |= CAPABILITY_DISCONNECT_FROM_CONFERENCE;
1839             currentCapabilities |= CAPABILITY_SEPARATE_FROM_CONFERENCE;
1840         }
1841 
1842         return currentCapabilities;
1843     }
1844 
1845     /**
1846      * Stores the new original connection capabilities, and applies them to the current connection,
1847      * notifying any listeners as necessary.
1848      *
1849      * @param connectionCapabilities The original connection capabilties.
1850      */
setOriginalConnectionCapabilities(int connectionCapabilities)1851     public void setOriginalConnectionCapabilities(int connectionCapabilities) {
1852         mOriginalConnectionCapabilities = connectionCapabilities;
1853         updateConnectionCapabilities();
1854         updateConnectionProperties();
1855     }
1856 
1857     /**
1858      * Called to apply the capabilities present in the {@link #mOriginalConnection} to this
1859      * {@link Connection}.  Provides a mapping between the capabilities present in the original
1860      * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in
1861      * this {@link Connection}.
1862      *
1863      * @param capabilities The capabilities bitmask from the {@link Connection}.
1864      * @return the capabilities bitmask with the original connection capabilities remapped and
1865      *      applied.
1866      */
applyOriginalConnectionCapabilities(int capabilities)1867     public int applyOriginalConnectionCapabilities(int capabilities) {
1868         // We only support downgrading to audio if both the remote and local side support
1869         // downgrading to audio.
1870         boolean supportsDowngradeToAudio = can(mOriginalConnectionCapabilities,
1871                 Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
1872                         Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE);
1873         capabilities = changeBitmask(capabilities,
1874                 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
1875 
1876         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
1877                 can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
1878 
1879         boolean isLocalVideoSupported = can(mOriginalConnectionCapabilities,
1880                 Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) && !mIsTtyEnabled;
1881         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
1882                 isLocalVideoSupported);
1883 
1884         return capabilities;
1885     }
1886 
1887     /**
1888      * Whether the call is using wifi.
1889      */
isWifi()1890     boolean isWifi() {
1891         return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
1892     }
1893 
1894     /**
1895      * Sets whether this call has been identified by the network as an emergency call.
1896      * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call
1897      * as an emergency call, {@code false} otherwise.
1898      */
setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall)1899     public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) {
1900         Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, "
1901                 + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(),
1902                 isNetworkIdentifiedEmergencyCall);
1903         mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall;
1904         updateConnectionProperties();
1905     }
1906 
1907     /**
1908      * @return {@code true} if the network has identified this call as an emergency call,
1909      * {@code false} otherwise.
1910      */
isNetworkIdentifiedEmergencyCall()1911     public boolean isNetworkIdentifiedEmergencyCall() {
1912         return mIsNetworkIdentifiedEmergencyCall;
1913     }
1914 
1915     /**
1916      * @return {@code true} if this is an outgoing call, {@code false} otherwise.
1917      */
isOutgoingCall()1918     boolean isOutgoingCall() {
1919         return mIsOutgoing;
1920     }
1921 
1922     /**
1923      * Sets the current call audio quality. Used during rebuild of the properties
1924      * to set or unset the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
1925      *
1926      * @param audioQuality The audio quality.
1927      */
setAudioQuality(int audioQuality)1928     public void setAudioQuality(int audioQuality) {
1929         mHasHighDefAudio = audioQuality ==
1930                 com.android.internal.telephony.Connection.AUDIO_QUALITY_HIGH_DEFINITION;
1931         updateConnectionProperties();
1932     }
1933 
resetStateForConference()1934     void resetStateForConference() {
1935         if (getState() == Connection.STATE_HOLDING) {
1936             resetStateOverride();
1937         }
1938     }
1939 
setHoldingForConference()1940     boolean setHoldingForConference() {
1941         if (getState() == Connection.STATE_ACTIVE) {
1942             setStateOverride(Call.State.HOLDING);
1943             return true;
1944         }
1945         return false;
1946     }
1947 
setRttTextStream(RttTextStream s)1948     public void setRttTextStream(RttTextStream s) {
1949         mRttTextStream = s;
1950     }
1951 
getRttTextStream()1952     public RttTextStream getRttTextStream() {
1953         return mRttTextStream;
1954     }
1955 
1956     /**
1957      * For video calls, sets whether this connection supports pausing the outgoing video for the
1958      * call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
1959      *
1960      * @param isVideoPauseSupported {@code true} if pause state supported, {@code false} otherwise.
1961      */
setVideoPauseSupported(boolean isVideoPauseSupported)1962     public void setVideoPauseSupported(boolean isVideoPauseSupported) {
1963         mIsVideoPauseSupported = isVideoPauseSupported;
1964     }
1965 
1966     /**
1967      * @return {@code true} if this connection supports pausing the outgoing video using the
1968      * {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
1969      */
getVideoPauseSupported()1970     public boolean getVideoPauseSupported() {
1971         return mIsVideoPauseSupported;
1972     }
1973 
1974     /**
1975      * Sets whether this connection supports conference calling.
1976      * @param isConferenceSupported {@code true} if conference calling is supported by this
1977      *                                         connection, {@code false} otherwise.
1978      */
setConferenceSupported(boolean isConferenceSupported)1979     public void setConferenceSupported(boolean isConferenceSupported) {
1980         mIsConferenceSupported = isConferenceSupported;
1981     }
1982 
1983     /**
1984      * @return {@code true} if this connection supports merging calls into a conference.
1985      */
isConferenceSupported()1986     public boolean isConferenceSupported() {
1987         return mIsConferenceSupported;
1988     }
1989 
1990     /**
1991      * Sets whether managing conference call is supported after this connection being a part of a
1992      * Ims conference.
1993      *
1994      * @param isManageImsConferenceCallSupported {@code true} if manage conference calling is
1995      *        supported after this connection being a part of a IMS conference,
1996      *        {@code false} otherwise.
1997      */
setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported)1998     public void setManageImsConferenceCallSupported(boolean isManageImsConferenceCallSupported) {
1999         mIsManageImsConferenceCallSupported = isManageImsConferenceCallSupported;
2000     }
2001 
2002     /**
2003      * @return {@code true} if manage conference calling is supported after this connection being a
2004      * part of a IMS conference.
2005      */
isManageImsConferenceCallSupported()2006     public boolean isManageImsConferenceCallSupported() {
2007         return mIsManageImsConferenceCallSupported;
2008     }
2009 
2010     /**
2011      * Sets whether this connection supports showing precise call disconnect cause.
2012      * @param showPreciseFailedCause  {@code true} if showing precise call
2013      * disconnect cause is supported by this connection, {@code false} otherwise.
2014      */
setShowPreciseFailedCause(boolean showPreciseFailedCause)2015     public void setShowPreciseFailedCause(boolean showPreciseFailedCause) {
2016         mShowPreciseFailedCause = showPreciseFailedCause;
2017     }
2018 
2019     /**
2020      * Sets whether TTY is enabled or not.
2021      * @param isTtyEnabled
2022      */
setTtyEnabled(boolean isTtyEnabled)2023     public void setTtyEnabled(boolean isTtyEnabled) {
2024         mIsTtyEnabled = isTtyEnabled;
2025         updateConnectionCapabilities();
2026     }
2027 
2028     /**
2029      * Whether the original connection is an IMS connection.
2030      * @return {@code True} if the original connection is an IMS connection, {@code false}
2031      *     otherwise.
2032      */
isImsConnection()2033     protected boolean isImsConnection() {
2034         com.android.internal.telephony.Connection originalConnection = getOriginalConnection();
2035         return originalConnection != null &&
2036                 originalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
2037     }
2038 
2039     /**
2040      * Whether the original connection is an GSM/CDMA connection.
2041      * @return {@code True} if the original connection is an GSM/CDMA connection, {@code false}
2042      *     otherwise.
2043      */
isGsmCdmaConnection()2044     protected boolean isGsmCdmaConnection() {
2045         Phone phone = getPhone();
2046         if (phone != null) {
2047             switch (phone.getPhoneType()) {
2048                 case PhoneConstants.PHONE_TYPE_GSM:
2049                 case PhoneConstants.PHONE_TYPE_CDMA:
2050                     return true;
2051                 default:
2052                     return false;
2053             }
2054         }
2055         return false;
2056     }
2057 
2058     /**
2059      * Whether the original connection was ever an IMS connection, either before or now.
2060      * @return {@code True} if the original connection was ever an IMS connection, {@code false}
2061      *     otherwise.
2062      */
wasImsConnection()2063     public boolean wasImsConnection() {
2064         return mWasImsConnection;
2065     }
2066 
getIsUsingAssistedDialing()2067     boolean getIsUsingAssistedDialing() {
2068         return mIsUsingAssistedDialing;
2069     }
2070 
setIsUsingAssistedDialing(Boolean isUsingAssistedDialing)2071     void setIsUsingAssistedDialing(Boolean isUsingAssistedDialing) {
2072         mIsUsingAssistedDialing = isUsingAssistedDialing;
2073         updateConnectionProperties();
2074     }
2075 
getAddressFromNumber(String number)2076     private static Uri getAddressFromNumber(String number) {
2077         // Address can be null for blocked calls.
2078         if (number == null) {
2079             number = "";
2080         }
2081         return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
2082     }
2083 
2084     /**
2085      * Changes a capabilities bit-mask to add or remove a capability.
2086      *
2087      * @param bitmask The bit-mask.
2088      * @param bitfield The bit-field to change.
2089      * @param enabled Whether the bit-field should be set or removed.
2090      * @return The bit-mask with the bit-field changed.
2091      */
changeBitmask(int bitmask, int bitfield, boolean enabled)2092     private int changeBitmask(int bitmask, int bitfield, boolean enabled) {
2093         if (enabled) {
2094             return bitmask | bitfield;
2095         } else {
2096             return bitmask & ~bitfield;
2097         }
2098     }
2099 
updateStatusHints()2100     private void updateStatusHints() {
2101         if (isWifi() && getPhone() != null) {
2102             int labelId = isValidRingingCall()
2103                     ? R.string.status_hint_label_incoming_wifi_call
2104                     : R.string.status_hint_label_wifi_call;
2105 
2106             Context context = getPhone().getContext();
2107             setStatusHints(new StatusHints(
2108                     context.getString(labelId),
2109                     Icon.createWithResource(
2110                             context, R.drawable.ic_signal_wifi_4_bar_24dp),
2111                     null /* extras */));
2112         } else {
2113             setStatusHints(null);
2114         }
2115     }
2116 
2117     /**
2118      * Register a listener for {@link TelephonyConnection} specific triggers.
2119      * @param l The instance of the listener to add
2120      * @return The connection being listened to
2121      */
addTelephonyConnectionListener(TelephonyConnectionListener l)2122     public final TelephonyConnection addTelephonyConnectionListener(TelephonyConnectionListener l) {
2123         mTelephonyListeners.add(l);
2124         // If we already have an original connection, let's call back immediately.
2125         // This would be the case for incoming calls.
2126         if (mOriginalConnection != null) {
2127             fireOnOriginalConnectionConfigured();
2128         }
2129         return this;
2130     }
2131 
2132     /**
2133      * Remove a listener for {@link TelephonyConnection} specific triggers.
2134      * @param l The instance of the listener to remove
2135      * @return The connection being listened to
2136      */
removeTelephonyConnectionListener( TelephonyConnectionListener l)2137     public final TelephonyConnection removeTelephonyConnectionListener(
2138             TelephonyConnectionListener l) {
2139         if (l != null) {
2140             mTelephonyListeners.remove(l);
2141         }
2142         return this;
2143     }
2144 
2145     @Override
setHoldable(boolean isHoldable)2146     public void setHoldable(boolean isHoldable) {
2147         mIsHoldable = isHoldable;
2148         updateConnectionCapabilities();
2149     }
2150 
2151     @Override
isChildHoldable()2152     public boolean isChildHoldable() {
2153         return getConference() != null;
2154     }
2155 
isHoldable()2156     public boolean isHoldable() {
2157         return mIsHoldable;
2158     }
2159 
2160     /**
2161      * Fire a callback to the various listeners for when the original connection is
2162      * set in this {@link TelephonyConnection}
2163      */
fireOnOriginalConnectionConfigured()2164     private final void fireOnOriginalConnectionConfigured() {
2165         for (TelephonyConnectionListener l : mTelephonyListeners) {
2166             l.onOriginalConnectionConfigured(this);
2167         }
2168     }
2169 
fireOnOriginalConnectionRetryDial(boolean isPermanentFailure)2170     private final void fireOnOriginalConnectionRetryDial(boolean isPermanentFailure) {
2171         for (TelephonyConnectionListener l : mTelephonyListeners) {
2172             l.onOriginalConnectionRetry(this, isPermanentFailure);
2173         }
2174     }
2175 
2176     /**
2177      * Handles exiting ECM mode.
2178      */
handleExitedEcmMode()2179     protected void handleExitedEcmMode() {
2180         updateConnectionProperties();
2181     }
2182 
2183     /**
2184      * Determines whether the connection supports conference calling.  A connection supports
2185      * conference calling if it:
2186      * 1. Is not an emergency call.
2187      * 2. Carrier supports conference calls.
2188      * 3. If call is a video call, carrier supports video conference calls.
2189      * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
2190      */
refreshConferenceSupported()2191     private void refreshConferenceSupported() {
2192         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
2193         Phone phone = getPhone();
2194         if (phone == null) {
2195             Log.w(this, "refreshConferenceSupported = false; phone is null");
2196             if (isConferenceSupported()) {
2197                 setConferenceSupported(false);
2198                 notifyConferenceSupportedChanged(false);
2199             }
2200             return;
2201         }
2202 
2203         boolean isIms = phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS;
2204         boolean isVoWifiEnabled = false;
2205         if (isIms) {
2206             isVoWifiEnabled = ImsUtil.isWfcEnabled(phone.getContext(), phone.getPhoneId());
2207         }
2208         PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
2209                 .makePstnPhoneAccountHandle(phone.getDefaultPhone())
2210                 : PhoneUtils.makePstnPhoneAccountHandle(phone);
2211         TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry
2212                 .getInstance(getPhone().getContext());
2213         boolean isConferencingSupported = telecomAccountRegistry
2214                 .isMergeCallSupported(phoneAccountHandle);
2215         boolean isImsConferencingSupported = telecomAccountRegistry
2216                 .isMergeImsCallSupported(phoneAccountHandle);
2217         mIsCarrierVideoConferencingSupported = telecomAccountRegistry
2218                 .isVideoConferencingSupported(phoneAccountHandle);
2219         boolean isMergeOfWifiCallsAllowedWhenVoWifiOff = telecomAccountRegistry
2220                 .isMergeOfWifiCallsAllowedWhenVoWifiOff(phoneAccountHandle);
2221 
2222         Log.v(this, "refreshConferenceSupported : isConfSupp=%b, isImsConfSupp=%b, " +
2223                 "isVidConfSupp=%b, isMergeOfWifiAllowed=%b, " +
2224                 "isWifi=%b, isVoWifiEnabled=%b",
2225                 isConferencingSupported, isImsConferencingSupported,
2226                 mIsCarrierVideoConferencingSupported, isMergeOfWifiCallsAllowedWhenVoWifiOff,
2227                 isWifi(), isVoWifiEnabled);
2228         boolean isConferenceSupported = true;
2229         if (mTreatAsEmergencyCall) {
2230             isConferenceSupported = false;
2231             Log.d(this, "refreshConferenceSupported = false; emergency call");
2232         } else if (isRtt()) {
2233             isConferenceSupported = false;
2234             Log.d(this, "refreshConferenceSupported = false; rtt call");
2235         } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) {
2236             isConferenceSupported = false;
2237             Log.d(this, "refreshConferenceSupported = false; carrier doesn't support conf.");
2238         } else if (isVideoCall && !mIsCarrierVideoConferencingSupported) {
2239             isConferenceSupported = false;
2240             Log.d(this, "refreshConferenceSupported = false; video conf not supported.");
2241         } else if (!isMergeOfWifiCallsAllowedWhenVoWifiOff && isWifi() && !isVoWifiEnabled) {
2242             isConferenceSupported = false;
2243             Log.d(this,
2244                     "refreshConferenceSupported = false; can't merge wifi calls when voWifi off.");
2245         } else {
2246             Log.d(this, "refreshConferenceSupported = true.");
2247         }
2248 
2249         if (isConferenceSupported != isConferenceSupported()) {
2250             setConferenceSupported(isConferenceSupported);
2251             notifyConferenceSupportedChanged(isConferenceSupported);
2252         }
2253     }
2254     /**
2255      * Provides a mapping from extras keys which may be found in the
2256      * {@link com.android.internal.telephony.Connection} to their equivalents defined in
2257      * {@link android.telecom.Connection}.
2258      *
2259      * @return Map containing key mappings.
2260      */
createExtrasMap()2261     private static Map<String, String> createExtrasMap() {
2262         Map<String, String> result = new HashMap<String, String>();
2263         result.put(ImsCallProfile.EXTRA_CHILD_NUMBER,
2264                 android.telecom.Connection.EXTRA_CHILD_ADDRESS);
2265         result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT,
2266                 android.telecom.Connection.EXTRA_CALL_SUBJECT);
2267         result.put(ImsCallProfile.EXTRA_ADDITIONAL_SIP_INVITE_FIELDS,
2268                 android.telecom.Connection.EXTRA_SIP_INVITE);
2269         return Collections.unmodifiableMap(result);
2270     }
2271 
isShowingOriginalDialString()2272     private boolean isShowingOriginalDialString() {
2273         boolean showOrigDialString = false;
2274         Phone phone = getPhone();
2275         if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)
2276                 && !mOriginalConnection.isIncoming()) {
2277             PersistableBundle pb = getCarrierConfig();
2278             if (pb != null) {
2279                 showOrigDialString = pb.getBoolean(CarrierConfigManager
2280                         .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
2281                 Log.d(this, "showOrigDialString: " + showOrigDialString);
2282             }
2283         }
2284         return showOrigDialString;
2285     }
2286 
2287     /**
2288      * Creates a string representation of this {@link TelephonyConnection}.  Primarily intended for
2289      * use in log statements.
2290      *
2291      * @return String representation of the connection.
2292      */
2293     @Override
toString()2294     public String toString() {
2295         StringBuilder sb = new StringBuilder();
2296         sb.append("[TelephonyConnection objId:");
2297         sb.append(System.identityHashCode(this));
2298         sb.append(" telecomCallID:");
2299         sb.append(getTelecomCallId());
2300         sb.append(" type:");
2301         if (isImsConnection()) {
2302             sb.append("ims");
2303         } else if (this instanceof com.android.services.telephony.GsmConnection) {
2304             sb.append("gsm");
2305         } else if (this instanceof CdmaConnection) {
2306             sb.append("cdma");
2307         }
2308         sb.append(" state:");
2309         sb.append(Connection.stateToString(getState()));
2310         sb.append(" capabilities:");
2311         sb.append(capabilitiesToString(getConnectionCapabilities()));
2312         sb.append(" properties:");
2313         sb.append(propertiesToString(getConnectionProperties()));
2314         sb.append(" address:");
2315         sb.append(Log.pii(getAddress()));
2316         sb.append(" originalConnection:");
2317         sb.append(mOriginalConnection);
2318         sb.append(" partOfConf:");
2319         if (getConference() == null) {
2320             sb.append("N");
2321         } else {
2322             sb.append("Y");
2323         }
2324         sb.append(" confSupported:");
2325         sb.append(mIsConferenceSupported ? "Y" : "N");
2326         sb.append("]");
2327         return sb.toString();
2328     }
2329 }
2330