ImsPhoneCallTracker.java revision 75329293e3c0e298deeb310811f70e5d01691772
1/* 2 * Copyright (C) 2013 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 17package com.android.internal.telephony.imsphone; 18 19import android.app.PendingIntent; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.SharedPreferences; 25import android.content.pm.PackageManager; 26import android.net.ConnectivityManager; 27import android.net.NetworkInfo; 28import android.net.NetworkStats; 29import android.net.Uri; 30import android.os.AsyncResult; 31import android.os.Bundle; 32import android.os.Handler; 33import android.os.Message; 34import android.os.PersistableBundle; 35import android.os.Registrant; 36import android.os.RegistrantList; 37import android.os.RemoteException; 38import android.os.SystemClock; 39import android.os.SystemProperties; 40import android.preference.PreferenceManager; 41import android.provider.Settings; 42import android.telecom.ConferenceParticipant; 43import android.telecom.TelecomManager; 44import android.telecom.VideoProfile; 45import android.telephony.CarrierConfigManager; 46import android.telephony.DisconnectCause; 47import android.telephony.PhoneNumberUtils; 48import android.telephony.PreciseDisconnectCause; 49import android.telephony.Rlog; 50import android.telephony.ServiceState; 51import android.telephony.SubscriptionManager; 52import android.telephony.TelephonyManager; 53import android.telephony.ims.feature.ImsFeature; 54import android.text.TextUtils; 55import android.util.ArrayMap; 56import android.util.Log; 57import android.util.Pair; 58import android.util.SparseIntArray; 59 60import com.android.ims.ImsCall; 61import com.android.ims.ImsCallProfile; 62import com.android.ims.ImsConfig; 63import com.android.ims.ImsConfigListener; 64import com.android.ims.ImsConnectionStateListener; 65import com.android.ims.ImsEcbm; 66import com.android.ims.ImsException; 67import com.android.ims.ImsManager; 68import com.android.ims.ImsMultiEndpoint; 69import com.android.ims.ImsReasonInfo; 70import com.android.ims.ImsServiceClass; 71import com.android.ims.ImsServiceProxy; 72import com.android.ims.ImsSuppServiceNotification; 73import com.android.ims.ImsUtInterface; 74import com.android.ims.internal.IImsVideoCallProvider; 75import com.android.ims.internal.ImsVideoCallProviderWrapper; 76import com.android.ims.internal.VideoPauseTracker; 77import com.android.internal.annotations.VisibleForTesting; 78import com.android.internal.os.SomeArgs; 79import com.android.internal.telephony.Call; 80import com.android.internal.telephony.CallStateException; 81import com.android.internal.telephony.CallTracker; 82import com.android.internal.telephony.CommandException; 83import com.android.internal.telephony.CommandsInterface; 84import com.android.internal.telephony.Connection; 85import com.android.internal.telephony.Phone; 86import com.android.internal.telephony.PhoneConstants; 87import com.android.internal.telephony.TelephonyProperties; 88import com.android.internal.telephony.dataconnection.DataEnabledSettings; 89import com.android.internal.telephony.gsm.SuppServiceNotification; 90import com.android.internal.telephony.metrics.TelephonyMetrics; 91import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; 92import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession; 93import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand; 94import com.android.server.net.NetworkStatsService; 95 96import java.io.FileDescriptor; 97import java.io.PrintWriter; 98import java.util.ArrayList; 99import java.util.HashMap; 100import java.util.List; 101import java.util.Map; 102import java.util.concurrent.atomic.AtomicInteger; 103import java.util.regex.Pattern; 104 105/** 106 * {@hide} 107 */ 108public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { 109 static final String LOG_TAG = "ImsPhoneCallTracker"; 110 static final String VERBOSE_STATE_TAG = "IPCTState"; 111 112 public interface PhoneStateListener { 113 void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState); 114 } 115 116 public interface SharedPreferenceProxy { 117 SharedPreferences getDefaultSharedPreferences(Context context); 118 } 119 120 public interface PhoneNumberUtilsProxy { 121 boolean isEmergencyNumber(String number); 122 } 123 124 private static final boolean DBG = true; 125 126 // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background 127 // calls. This is helpful for debugging. It is also possible to enable this at runtime by 128 // setting the IPCTState log tag to VERBOSE. 129 private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */ 130 private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING || 131 Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE); 132 133 //Indices map to ImsConfig.FeatureConstants 134 private boolean[] mImsFeatureEnabled = {false, false, false, false, false, false}; 135 private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi", 136 "UTLTE", "UTWiFi"}; 137 138 private TelephonyMetrics mMetrics; 139 private boolean mCarrierConfigLoaded = false; 140 141 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 142 @Override 143 public void onReceive(Context context, Intent intent) { 144 if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) { 145 if (DBG) log("onReceive : incoming call intent"); 146 147 if (mImsManager == null) return; 148 149 if (mServiceId < 0) return; 150 151 try { 152 // Network initiated USSD will be treated by mImsUssdListener 153 boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false); 154 if (isUssd) { 155 if (DBG) log("onReceive : USSD"); 156 mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener); 157 if (mUssdSession != null) { 158 mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE); 159 } 160 return; 161 } 162 163 boolean isUnknown = intent.getBooleanExtra(ImsManager.EXTRA_IS_UNKNOWN_CALL, 164 false); 165 if (DBG) { 166 log("onReceive : isUnknown = " + isUnknown + 167 " fg = " + mForegroundCall.getState() + 168 " bg = " + mBackgroundCall.getState()); 169 } 170 171 // Normal MT/Unknown call 172 ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener); 173 ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall, 174 ImsPhoneCallTracker.this, 175 (isUnknown? mForegroundCall: mRingingCall), isUnknown); 176 177 // If there is an active call. 178 if (mForegroundCall.hasConnections()) { 179 ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall(); 180 if (activeCall != null && imsCall != null) { 181 // activeCall could be null if the foreground call is in a disconnected 182 // state. If either of the calls is null there is no need to check if 183 // one will be disconnected on answer. 184 boolean answeringWillDisconnect = 185 shouldDisconnectActiveCallOnAnswer(activeCall, imsCall); 186 conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect); 187 } 188 } 189 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall); 190 addConnection(conn); 191 192 setVideoCallProvider(conn, imsCall); 193 194 TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(), 195 imsCall.getSession()); 196 197 if (isUnknown) { 198 mPhone.notifyUnknownConnection(conn); 199 } else { 200 if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) || 201 (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) { 202 conn.update(imsCall, ImsPhoneCall.State.WAITING); 203 } 204 205 mPhone.notifyNewRingingConnection(conn); 206 mPhone.notifyIncomingRing(); 207 } 208 209 updatePhoneState(); 210 mPhone.notifyPreciseCallStateChanged(); 211 } catch (ImsException e) { 212 loge("onReceive : exception " + e); 213 } catch (RemoteException e) { 214 } 215 } else if (intent.getAction().equals( 216 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 217 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, 218 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 219 if (subId == mPhone.getSubId()) { 220 cacheCarrierConfiguration(subId); 221 log("onReceive : Updating mAllowEmergencyVideoCalls = " + 222 mAllowEmergencyVideoCalls); 223 } 224 mCarrierConfigLoaded = true; 225 } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) { 226 mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra( 227 TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME))); 228 } 229 } 230 }; 231 232 //***** Constants 233 234 static final int MAX_CONNECTIONS = 7; 235 static final int MAX_CONNECTIONS_PER_CALL = 5; 236 237 private static final int EVENT_HANGUP_PENDINGMO = 18; 238 private static final int EVENT_RESUME_BACKGROUND = 19; 239 private static final int EVENT_DIAL_PENDINGMO = 20; 240 private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21; 241 private static final int EVENT_VT_DATA_USAGE_UPDATE = 22; 242 private static final int EVENT_DATA_ENABLED_CHANGED = 23; 243 private static final int EVENT_GET_IMS_SERVICE = 24; 244 private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25; 245 private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26; 246 private static final int EVENT_SUPP_SERVICE_INDICATION = 27; 247 248 private static final int TIMEOUT_HANGUP_PENDINGMO = 500; 249 250 // Initial condition for ims connection retry. 251 private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms 252 // Ceiling bitshift amount for service query timeout, calculated as: 253 // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where 254 // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT]. 255 private static final int CEILING_SERVICE_RETRY_COUNT = 6; 256 257 private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms 258 259 //***** Instance Variables 260 private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>(); 261 private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 262 private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 263 264 public ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING); 265 public ImsPhoneCall mForegroundCall = new ImsPhoneCall(this, 266 ImsPhoneCall.CONTEXT_FOREGROUND); 267 public ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this, 268 ImsPhoneCall.CONTEXT_BACKGROUND); 269 public ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER); 270 271 // Hold aggregated video call data usage for each video call since boot. 272 // The ImsCall's call id is the key of the map. 273 private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>(); 274 275 private volatile NetworkStats mVtDataUsageSnapshot = null; 276 private volatile NetworkStats mVtDataUsageUidSnapshot = null; 277 278 private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL); 279 280 private ImsPhoneConnection mPendingMO; 281 private int mClirMode = CommandsInterface.CLIR_DEFAULT; 282 private Object mSyncHold = new Object(); 283 284 private ImsCall mUssdSession = null; 285 private Message mPendingUssd = null; 286 287 ImsPhone mPhone; 288 289 private boolean mDesiredMute = false; // false = mute off 290 private boolean mOnHoldToneStarted = false; 291 private int mOnHoldToneId = -1; 292 293 private PhoneConstants.State mState = PhoneConstants.State.IDLE; 294 295 private int mImsServiceRetryCount; 296 private ImsManager mImsManager; 297 private ImsUtInterface mUtInterface; 298 private int mServiceId = -1; 299 300 private Call.SrvccState mSrvccState = Call.SrvccState.NONE; 301 302 private boolean mIsInEmergencyCall = false; 303 private boolean mIsDataEnabled = false; 304 305 private int pendingCallClirMode; 306 private int mPendingCallVideoState; 307 private Bundle mPendingIntentExtras; 308 private boolean pendingCallInEcm = false; 309 private boolean mSwitchingFgAndBgCalls = false; 310 private ImsCall mCallExpectedToResume = null; 311 private boolean mAllowEmergencyVideoCalls = false; 312 private boolean mIgnoreDataEnabledChangedForVideoCalls = false; 313 private boolean mIsViLteDataMetered = false; 314 private boolean mAlwaysPlayRemoteHoldTone = false; 315 316 /** 317 * Listeners to changes in the phone state. Intended for use by other interested IMS components 318 * without the need to register a full blown {@link android.telephony.PhoneStateListener}. 319 */ 320 private List<PhoneStateListener> mPhoneStateListeners = new ArrayList<>(); 321 322 /** 323 * Carrier configuration option which determines if video calls which have been downgraded to an 324 * audio call should be treated as if they are still video calls. 325 */ 326 private boolean mTreatDowngradedVideoCallsAsVideoCalls = false; 327 328 /** 329 * Carrier configuration option which determines if an ongoing video call over wifi should be 330 * dropped when an audio call is answered. 331 */ 332 private boolean mDropVideoCallWhenAnsweringAudioCall = false; 333 334 /** 335 * Carrier configuration option which determines whether adding a call during a video call 336 * should be allowed. 337 */ 338 private boolean mAllowAddCallDuringVideoCall = true; 339 340 /** 341 * Carrier configuration option which determines whether to notify the connection if a handover 342 * to wifi fails. 343 */ 344 private boolean mNotifyVtHandoverToWifiFail = false; 345 346 /** 347 * Carrier configuration option which determines whether the carrier supports downgrading a 348 * TX/RX/TX-RX video call directly to an audio-only call. 349 */ 350 private boolean mSupportDowngradeVtToAudio = false; 351 352 /** 353 * Stores the mapping of {@code ImsReasonInfo#CODE_*} to {@code PreciseDisconnectCause#*} 354 */ 355 private static final SparseIntArray PRECISE_CAUSE_MAP = new SparseIntArray(); 356 static { 357 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT, 358 PreciseDisconnectCause.LOCAL_ILLEGAL_ARGUMENT); 359 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE, 360 PreciseDisconnectCause.LOCAL_ILLEGAL_STATE); 361 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR, 362 PreciseDisconnectCause.LOCAL_INTERNAL_ERROR); 363 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 364 PreciseDisconnectCause.LOCAL_IMS_SERVICE_DOWN); 365 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL, 366 PreciseDisconnectCause.LOCAL_NO_PENDING_CALL); 367 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE, 368 PreciseDisconnectCause.NORMAL); 369 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_POWER_OFF, 370 PreciseDisconnectCause.LOCAL_POWER_OFF); 371 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 372 PreciseDisconnectCause.LOCAL_LOW_BATTERY); 373 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE, 374 PreciseDisconnectCause.LOCAL_NETWORK_NO_SERVICE); 375 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, 376 PreciseDisconnectCause.LOCAL_NETWORK_NO_LTE_COVERAGE); 377 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING, 378 PreciseDisconnectCause.LOCAL_NETWORK_ROAMING); 379 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED, 380 PreciseDisconnectCause.LOCAL_NETWORK_IP_CHANGED); 381 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE, 382 PreciseDisconnectCause.LOCAL_SERVICE_UNAVAILABLE); 383 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, 384 PreciseDisconnectCause.LOCAL_NOT_REGISTERED); 385 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED, 386 PreciseDisconnectCause.LOCAL_MAX_CALL_EXCEEDED); 387 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE, 388 PreciseDisconnectCause.LOCAL_CALL_DECLINE); 389 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING, 390 PreciseDisconnectCause.LOCAL_CALL_VCC_ON_PROGRESSING); 391 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED, 392 PreciseDisconnectCause.LOCAL_CALL_RESOURCE_RESERVATION_FAILED); 393 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED, 394 PreciseDisconnectCause.LOCAL_CALL_CS_RETRY_REQUIRED); 395 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED, 396 PreciseDisconnectCause.LOCAL_CALL_VOLTE_RETRY_REQUIRED); 397 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED, 398 PreciseDisconnectCause.LOCAL_CALL_TERMINATED); 399 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE, 400 PreciseDisconnectCause.LOCAL_HO_NOT_FEASIBLE); 401 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING, 402 PreciseDisconnectCause.TIMEOUT_1XX_WAITING); 403 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER, 404 PreciseDisconnectCause.TIMEOUT_NO_ANSWER); 405 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE, 406 PreciseDisconnectCause.TIMEOUT_NO_ANSWER_CALL_UPDATE); 407 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_FDN_BLOCKED, 408 PreciseDisconnectCause.FDN_BLOCKED); 409 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REDIRECTED, 410 PreciseDisconnectCause.SIP_REDIRECTED); 411 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_REQUEST, 412 PreciseDisconnectCause.SIP_BAD_REQUEST); 413 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_FORBIDDEN, 414 PreciseDisconnectCause.SIP_FORBIDDEN); 415 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_FOUND, 416 PreciseDisconnectCause.SIP_NOT_FOUND); 417 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED, 418 PreciseDisconnectCause.SIP_NOT_SUPPORTED); 419 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT, 420 PreciseDisconnectCause.SIP_REQUEST_TIMEOUT); 421 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE, 422 PreciseDisconnectCause.SIP_TEMPRARILY_UNAVAILABLE); 423 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_ADDRESS, 424 PreciseDisconnectCause.SIP_BAD_ADDRESS); 425 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BUSY, 426 PreciseDisconnectCause.SIP_BUSY); 427 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, 428 PreciseDisconnectCause.SIP_REQUEST_CANCELLED); 429 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE, 430 PreciseDisconnectCause.SIP_NOT_ACCEPTABLE); 431 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_REACHABLE, 432 PreciseDisconnectCause.SIP_NOT_REACHABLE); 433 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_CLIENT_ERROR, 434 PreciseDisconnectCause.SIP_CLIENT_ERROR); 435 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR, 436 PreciseDisconnectCause.SIP_SERVER_INTERNAL_ERROR); 437 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE, 438 PreciseDisconnectCause.SIP_SERVICE_UNAVAILABLE); 439 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT, 440 PreciseDisconnectCause.SIP_SERVER_TIMEOUT); 441 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_ERROR, 442 PreciseDisconnectCause.SIP_SERVER_ERROR); 443 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_USER_REJECTED, 444 PreciseDisconnectCause.SIP_USER_REJECTED); 445 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR, 446 PreciseDisconnectCause.SIP_GLOBAL_ERROR); 447 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE, 448 PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE); 449 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE, 450 PreciseDisconnectCause.EMERGENCY_PERM_FAILURE); 451 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_INIT_FAILED, 452 PreciseDisconnectCause.MEDIA_INIT_FAILED); 453 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NO_DATA, 454 PreciseDisconnectCause.MEDIA_NO_DATA); 455 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE, 456 PreciseDisconnectCause.MEDIA_NOT_ACCEPTABLE); 457 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED, 458 PreciseDisconnectCause.MEDIA_UNSPECIFIED); 459 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED, 460 PreciseDisconnectCause.USER_TERMINATED); 461 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_NOANSWER, 462 PreciseDisconnectCause.USER_NOANSWER); 463 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_IGNORE, 464 PreciseDisconnectCause.USER_IGNORE); 465 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_DECLINE, 466 PreciseDisconnectCause.USER_DECLINE); 467 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOW_BATTERY, 468 PreciseDisconnectCause.LOW_BATTERY); 469 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID, 470 PreciseDisconnectCause.BLACKLISTED_CALL_ID); 471 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 472 PreciseDisconnectCause.USER_TERMINATED_BY_REMOTE); 473 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NOT_SUPPORTED, 474 PreciseDisconnectCause.UT_NOT_SUPPORTED); 475 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE, 476 PreciseDisconnectCause.UT_SERVICE_UNAVAILABLE); 477 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED, 478 PreciseDisconnectCause.UT_OPERATION_NOT_ALLOWED); 479 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NETWORK_ERROR, 480 PreciseDisconnectCause.UT_NETWORK_ERROR); 481 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH, 482 PreciseDisconnectCause.UT_CB_PASSWORD_MISMATCH); 483 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED, 484 PreciseDisconnectCause.ECBM_NOT_SUPPORTED); 485 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED, 486 PreciseDisconnectCause.MULTIENDPOINT_NOT_SUPPORTED); 487 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE, 488 PreciseDisconnectCause.CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE); 489 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, 490 PreciseDisconnectCause.ANSWERED_ELSEWHERE); 491 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC, 492 PreciseDisconnectCause.CALL_PULL_OUT_OF_SYNC); 493 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL, 494 PreciseDisconnectCause.CALL_PULLED); 495 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_FAILED, 496 PreciseDisconnectCause.SUPP_SVC_FAILED); 497 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED, 498 PreciseDisconnectCause.SUPP_SVC_CANCELLED); 499 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION, 500 PreciseDisconnectCause.SUPP_SVC_REINVITE_COLLISION); 501 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE, 502 PreciseDisconnectCause.IWLAN_DPD_FAILURE); 503 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE, 504 PreciseDisconnectCause.EPDG_TUNNEL_ESTABLISH_FAILURE); 505 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE, 506 PreciseDisconnectCause.EPDG_TUNNEL_REKEY_FAILURE); 507 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION, 508 PreciseDisconnectCause.EPDG_TUNNEL_LOST_CONNECTION); 509 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED, 510 PreciseDisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED); 511 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, 512 PreciseDisconnectCause.REMOTE_CALL_DECLINE); 513 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_LIMIT_REACHED, 514 PreciseDisconnectCause.DATA_LIMIT_REACHED); 515 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_DISABLED, 516 PreciseDisconnectCause.DATA_DISABLED); 517 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_WIFI_LOST, 518 PreciseDisconnectCause.WIFI_LOST); 519 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_OFF, 520 PreciseDisconnectCause.RADIO_OFF); 521 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NO_VALID_SIM, 522 PreciseDisconnectCause.NO_VALID_SIM); 523 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR, 524 PreciseDisconnectCause.RADIO_INTERNAL_ERROR); 525 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT, 526 PreciseDisconnectCause.NETWORK_RESP_TIMEOUT); 527 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_REJECT, 528 PreciseDisconnectCause.NETWORK_REJECT); 529 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE, 530 PreciseDisconnectCause.RADIO_ACCESS_FAILURE); 531 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_FAILURE, 532 PreciseDisconnectCause.RADIO_LINK_FAILURE); 533 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_LOST, 534 PreciseDisconnectCause.RADIO_LINK_LOST); 535 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE, 536 PreciseDisconnectCause.RADIO_UPLINK_FAILURE); 537 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE, 538 PreciseDisconnectCause.RADIO_SETUP_FAILURE); 539 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL, 540 PreciseDisconnectCause.RADIO_RELEASE_NORMAL); 541 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL, 542 PreciseDisconnectCause.RADIO_RELEASE_ABNORMAL); 543 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED, 544 PreciseDisconnectCause.ACCESS_CLASS_BLOCKED); 545 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_DETACH, 546 PreciseDisconnectCause.NETWORK_DETACH); 547 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_1, 548 PreciseDisconnectCause.OEM_CAUSE_1); 549 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_2, 550 PreciseDisconnectCause.OEM_CAUSE_2); 551 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_3, 552 PreciseDisconnectCause.OEM_CAUSE_3); 553 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_4, 554 PreciseDisconnectCause.OEM_CAUSE_4); 555 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_5, 556 PreciseDisconnectCause.OEM_CAUSE_5); 557 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_6, 558 PreciseDisconnectCause.OEM_CAUSE_6); 559 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_7, 560 PreciseDisconnectCause.OEM_CAUSE_7); 561 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_8, 562 PreciseDisconnectCause.OEM_CAUSE_8); 563 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_9, 564 PreciseDisconnectCause.OEM_CAUSE_9); 565 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_10, 566 PreciseDisconnectCause.OEM_CAUSE_10); 567 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_11, 568 PreciseDisconnectCause.OEM_CAUSE_11); 569 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_12, 570 PreciseDisconnectCause.OEM_CAUSE_12); 571 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_13, 572 PreciseDisconnectCause.OEM_CAUSE_13); 573 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_14, 574 PreciseDisconnectCause.OEM_CAUSE_14); 575 PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_15, 576 PreciseDisconnectCause.OEM_CAUSE_15); 577 } 578 579 /** 580 * Carrier configuration option which determines whether the carrier wants to inform the user 581 * when a video call is handed over from WIFI to LTE. 582 * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL} for more 583 * information. 584 */ 585 private boolean mNotifyHandoverVideoFromWifiToLTE = false; 586 587 /** 588 * Carrier configuration option which determines whether the carrier supports the 589 * {@link VideoProfile#STATE_PAUSED} signalling. 590 * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information. 591 */ 592 private boolean mSupportPauseVideo = false; 593 594 /** 595 * Carrier configuration option which defines a mapping from pairs of 596 * {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()} values to a new 597 * {@code ImsReasonInfo#CODE_*} value. 598 * 599 * See {@link CarrierConfigManager#KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY}. 600 */ 601 private Map<Pair<Integer, String>, Integer> mImsReasonCodeMap = new ArrayMap<>(); 602 603 604 /** 605 * TODO: Remove this code; it is a workaround. 606 * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(boolean)} to 607 * be called when an ongoing video call is disconnected. In some cases, where video pause is 608 * supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data 609 * has been disabled we will pause the video rather than disconnecting the call. When this 610 * happens we need to prevent the IMS service config from being updated, as this will cause VT 611 * to be disabled mid-call, resulting in an inability to un-pause the video. 612 */ 613 private boolean mShouldUpdateImsConfigOnDisconnect = false; 614 615 /** 616 * Default implementation for retrieving shared preferences; uses the actual PreferencesManager. 617 */ 618 private SharedPreferenceProxy mSharedPreferenceProxy = (Context context) -> { 619 return PreferenceManager.getDefaultSharedPreferences(context); 620 }; 621 622 /** 623 * Default implementation for determining if a number is an emergency number. Uses the real 624 * PhoneNumberUtils. 625 */ 626 private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = (String string) -> { 627 return PhoneNumberUtils.isEmergencyNumber(string); 628 }; 629 630 // Callback fires when ImsManager MMTel Feature changes state 631 private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> { 632 try { 633 int status = mImsManager.getImsServiceStatus(); 634 log("Status Changed: " + status); 635 switch(status) { 636 case ImsFeature.STATE_READY: { 637 startListeningForCalls(); 638 break; 639 } 640 case ImsFeature.STATE_INITIALIZING: 641 // fall through 642 case ImsFeature.STATE_NOT_AVAILABLE: { 643 stopListeningForCalls(); 644 break; 645 } 646 default: { 647 Log.w(LOG_TAG, "Unexpected State!"); 648 } 649 } 650 } catch (ImsException e) { 651 // Could not get the ImsService, retry! 652 retryGetImsService(); 653 } 654 }; 655 656 @VisibleForTesting 657 public interface IRetryTimeout { 658 int get(); 659 } 660 661 /** 662 * Default implementation of interface that calculates the ImsService retry timeout. 663 * Override-able for testing. 664 */ 665 @VisibleForTesting 666 public IRetryTimeout mRetryTimeout = () -> { 667 int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS; 668 if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) { 669 mImsServiceRetryCount++; 670 } 671 return timeout; 672 }; 673 674 //***** Events 675 676 677 //***** Constructors 678 679 public ImsPhoneCallTracker(ImsPhone phone) { 680 this.mPhone = phone; 681 682 mMetrics = TelephonyMetrics.getInstance(); 683 684 IntentFilter intentfilter = new IntentFilter(); 685 intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL); 686 intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 687 intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER); 688 mPhone.getContext().registerReceiver(mReceiver, intentfilter); 689 cacheCarrierConfiguration(mPhone.getSubId()); 690 691 mPhone.getDefaultPhone().registerForDataEnabledChanged( 692 this, EVENT_DATA_ENABLED_CHANGED, null); 693 694 mImsServiceRetryCount = 0; 695 696 final TelecomManager telecomManager = 697 (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE); 698 mDefaultDialerUid.set( 699 getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage())); 700 701 long currentTime = SystemClock.elapsedRealtime(); 702 mVtDataUsageSnapshot = new NetworkStats(currentTime, 1); 703 mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1); 704 705 // Send a message to connect to the Ims Service and open a connection through 706 // getImsService(). 707 sendEmptyMessage(EVENT_GET_IMS_SERVICE); 708 } 709 710 /** 711 * Test-only method used to mock out access to the shared preferences through the 712 * {@link PreferenceManager}. 713 * @param sharedPreferenceProxy 714 */ 715 @VisibleForTesting 716 public void setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy) { 717 mSharedPreferenceProxy = sharedPreferenceProxy; 718 } 719 720 /** 721 * Test-only method used to mock out access to the phone number utils class. 722 * @param phoneNumberUtilsProxy 723 */ 724 @VisibleForTesting 725 public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy phoneNumberUtilsProxy) { 726 mPhoneNumberUtilsProxy = phoneNumberUtilsProxy; 727 } 728 729 private int getPackageUid(Context context, String pkg) { 730 if (pkg == null) { 731 return NetworkStats.UID_ALL; 732 } 733 734 // Initialize to UID_ALL so at least it can be counted to overall data usage if 735 // the dialer's package uid is not available. 736 int uid = NetworkStats.UID_ALL; 737 try { 738 uid = context.getPackageManager().getPackageUid(pkg, 0); 739 } catch (PackageManager.NameNotFoundException e) { 740 loge("Cannot find package uid. pkg = " + pkg); 741 } 742 return uid; 743 } 744 745 private PendingIntent createIncomingCallPendingIntent() { 746 Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL); 747 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 748 return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, 749 PendingIntent.FLAG_UPDATE_CURRENT); 750 } 751 752 private void getImsService() throws ImsException { 753 if (DBG) log("getImsService"); 754 mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()); 755 // Adding to set, will be safe adding multiple times. If the ImsService is not active yet, 756 // this method will throw an ImsException. 757 mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback); 758 // Wait for ImsService.STATE_READY to start listening for calls. 759 // Call the callback right away for compatibility with older devices that do not use states. 760 mNotifyStatusChangedCallback.notifyStatusChanged(); 761 } 762 763 private void startListeningForCalls() throws ImsException { 764 mImsServiceRetryCount = 0; 765 mServiceId = mImsManager.open(ImsServiceClass.MMTEL, 766 createIncomingCallPendingIntent(), 767 mImsConnectionStateListener); 768 769 mImsManager.setImsConfigListener(mImsConfigListener); 770 771 // Get the ECBM interface and set IMSPhone's listener object for notifications 772 getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener()); 773 if (mPhone.isInEcm()) { 774 // Call exit ECBM which will invoke onECBMExited 775 mPhone.exitEmergencyCallbackMode(); 776 } 777 int mPreferredTtyMode = Settings.Secure.getInt( 778 mPhone.getContext().getContentResolver(), 779 Settings.Secure.PREFERRED_TTY_MODE, 780 Phone.TTY_MODE_OFF); 781 mImsManager.setUiTTYMode(mPhone.getContext(), mPreferredTtyMode, null); 782 783 ImsMultiEndpoint multiEndpoint = getMultiEndpointInterface(); 784 if (multiEndpoint != null) { 785 multiEndpoint.setExternalCallStateListener( 786 mPhone.getExternalCallTracker().getExternalCallStateListener()); 787 } 788 789 //Set UT interface listener to receive UT indications. 790 mUtInterface = getUtInterface(); 791 if (mUtInterface != null) { 792 mUtInterface.registerForSuppServiceIndication(this, 793 EVENT_SUPP_SERVICE_INDICATION, null); 794 } 795 796 if (mCarrierConfigLoaded) { 797 mImsManager.updateImsServiceConfig(true); 798 } 799 } 800 801 private void stopListeningForCalls() { 802 try { 803 resetImsCapabilities(); 804 // Only close on valid session. 805 if (mImsManager != null && mServiceId > 0) { 806 mImsManager.close(mServiceId); 807 mServiceId = -1; 808 } 809 } catch (ImsException e) { 810 // If the binder is unavailable, then the ImsService doesn't need to close. 811 } 812 } 813 814 public void dispose() { 815 if (DBG) log("dispose"); 816 mRingingCall.dispose(); 817 mBackgroundCall.dispose(); 818 mForegroundCall.dispose(); 819 mHandoverCall.dispose(); 820 821 clearDisconnected(); 822 if (mUtInterface != null) { 823 mUtInterface.unregisterForSuppServiceIndication(this); 824 } 825 mPhone.getContext().unregisterReceiver(mReceiver); 826 mPhone.getDefaultPhone().unregisterForDataEnabledChanged(this); 827 removeMessages(EVENT_GET_IMS_SERVICE); 828 } 829 830 @Override 831 protected void finalize() { 832 log("ImsPhoneCallTracker finalized"); 833 } 834 835 //***** Instance Methods 836 837 //***** Public Methods 838 @Override 839 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 840 Registrant r = new Registrant(h, what, obj); 841 mVoiceCallStartedRegistrants.add(r); 842 } 843 844 @Override 845 public void unregisterForVoiceCallStarted(Handler h) { 846 mVoiceCallStartedRegistrants.remove(h); 847 } 848 849 @Override 850 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 851 Registrant r = new Registrant(h, what, obj); 852 mVoiceCallEndedRegistrants.add(r); 853 } 854 855 @Override 856 public void unregisterForVoiceCallEnded(Handler h) { 857 mVoiceCallEndedRegistrants.remove(h); 858 } 859 860 public Connection dial(String dialString, int videoState, Bundle intentExtras) throws 861 CallStateException { 862 int oirMode; 863 if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) { 864 SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences( 865 mPhone.getContext()); 866 oirMode = sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(), 867 CommandsInterface.CLIR_DEFAULT); 868 } else { 869 loge("dial; could not get default CLIR mode."); 870 oirMode = CommandsInterface.CLIR_DEFAULT; 871 } 872 return dial(dialString, oirMode, videoState, intentExtras); 873 } 874 875 /** 876 * oirMode is one of the CLIR_ constants 877 */ 878 synchronized Connection 879 dial(String dialString, int clirMode, int videoState, Bundle intentExtras) 880 throws CallStateException { 881 boolean isPhoneInEcmMode = isPhoneInEcbMode(); 882 boolean isEmergencyNumber = mPhoneNumberUtilsProxy.isEmergencyNumber(dialString); 883 884 if (DBG) log("dial clirMode=" + clirMode); 885 if (isEmergencyNumber) { 886 clirMode = CommandsInterface.CLIR_SUPPRESSION; 887 if (DBG) log("dial emergency call, set clirModIe=" + clirMode); 888 } 889 890 // note that this triggers call state changed notif 891 clearDisconnected(); 892 893 if (mImsManager == null) { 894 throw new CallStateException("service not available"); 895 } 896 897 if (!canDial()) { 898 throw new CallStateException("cannot dial in current state"); 899 } 900 901 if (isPhoneInEcmMode && isEmergencyNumber) { 902 handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER); 903 } 904 905 // If the call is to an emergency number and the carrier does not support video emergency 906 // calls, dial as an audio-only call. 907 if (isEmergencyNumber && VideoProfile.isVideo(videoState) && 908 !mAllowEmergencyVideoCalls) { 909 loge("dial: carrier does not support video emergency calls; downgrade to audio-only"); 910 videoState = VideoProfile.STATE_AUDIO_ONLY; 911 } 912 913 boolean holdBeforeDial = false; 914 915 // The new call must be assigned to the foreground call. 916 // That call must be idle, so place anything that's 917 // there on hold 918 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 919 if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) { 920 //we should have failed in !canDial() above before we get here 921 throw new CallStateException("cannot dial in current state"); 922 } 923 // foreground call is empty for the newly dialed connection 924 holdBeforeDial = true; 925 // Cache the video state for pending MO call. 926 mPendingCallVideoState = videoState; 927 mPendingIntentExtras = intentExtras; 928 switchWaitingOrHoldingAndActive(); 929 } 930 931 ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE; 932 ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE; 933 934 mClirMode = clirMode; 935 936 synchronized (mSyncHold) { 937 if (holdBeforeDial) { 938 fgState = mForegroundCall.getState(); 939 bgState = mBackgroundCall.getState(); 940 941 //holding foreground call failed 942 if (fgState == ImsPhoneCall.State.ACTIVE) { 943 throw new CallStateException("cannot dial in current state"); 944 } 945 946 //holding foreground call succeeded 947 if (bgState == ImsPhoneCall.State.HOLDING) { 948 holdBeforeDial = false; 949 } 950 } 951 952 mPendingMO = new ImsPhoneConnection(mPhone, 953 checkForTestEmergencyNumber(dialString), this, mForegroundCall, 954 isEmergencyNumber); 955 mPendingMO.setVideoState(videoState); 956 } 957 addConnection(mPendingMO); 958 959 if (!holdBeforeDial) { 960 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 961 dialInternal(mPendingMO, clirMode, videoState, intentExtras); 962 } else { 963 try { 964 getEcbmInterface().exitEmergencyCallbackMode(); 965 } catch (ImsException e) { 966 e.printStackTrace(); 967 throw new CallStateException("service not available"); 968 } 969 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 970 pendingCallClirMode = clirMode; 971 mPendingCallVideoState = videoState; 972 pendingCallInEcm = true; 973 } 974 } 975 976 updatePhoneState(); 977 mPhone.notifyPreciseCallStateChanged(); 978 979 return mPendingMO; 980 } 981 982 boolean isImsServiceReady() { 983 if (mImsManager == null) { 984 return false; 985 } 986 987 return mImsManager.isServiceReady(); 988 } 989 990 /** 991 * Caches frequently used carrier configuration items locally. 992 * 993 * @param subId The sub id. 994 */ 995 private void cacheCarrierConfiguration(int subId) { 996 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 997 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 998 if (carrierConfigManager == null) { 999 loge("cacheCarrierConfiguration: No carrier config service found."); 1000 return; 1001 } 1002 1003 PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId); 1004 if (carrierConfig == null) { 1005 loge("cacheCarrierConfiguration: Empty carrier config."); 1006 return; 1007 } 1008 1009 mAllowEmergencyVideoCalls = 1010 carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL); 1011 mTreatDowngradedVideoCallsAsVideoCalls = 1012 carrierConfig.getBoolean( 1013 CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL); 1014 mDropVideoCallWhenAnsweringAudioCall = 1015 carrierConfig.getBoolean( 1016 CarrierConfigManager.KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL); 1017 mAllowAddCallDuringVideoCall = 1018 carrierConfig.getBoolean( 1019 CarrierConfigManager.KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL); 1020 mNotifyVtHandoverToWifiFail = carrierConfig.getBoolean( 1021 CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL); 1022 mSupportDowngradeVtToAudio = carrierConfig.getBoolean( 1023 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL); 1024 mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean( 1025 CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL); 1026 mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean( 1027 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS); 1028 mIsViLteDataMetered = carrierConfig.getBoolean( 1029 CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL); 1030 mSupportPauseVideo = carrierConfig.getBoolean( 1031 CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL); 1032 mAlwaysPlayRemoteHoldTone = carrierConfig.getBoolean( 1033 CarrierConfigManager.KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL); 1034 1035 String[] mappings = carrierConfig 1036 .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY); 1037 if (mappings != null && mappings.length > 0) { 1038 for (String mapping : mappings) { 1039 String[] values = mapping.split(Pattern.quote("|")); 1040 if (values.length != 3) { 1041 continue; 1042 } 1043 1044 try { 1045 Integer fromCode; 1046 if (values[0].equals("*")) { 1047 fromCode = null; 1048 } else { 1049 fromCode = Integer.parseInt(values[0]); 1050 } 1051 String message = values[1]; 1052 int toCode = Integer.parseInt(values[2]); 1053 1054 addReasonCodeRemapping(fromCode, message, toCode); 1055 log("Loaded ImsReasonInfo mapping : fromCode = " + 1056 fromCode == null ? "any" : fromCode + " ; message = " + 1057 message + " ; toCode = " + toCode); 1058 } catch (NumberFormatException nfe) { 1059 loge("Invalid ImsReasonInfo mapping found: " + mapping); 1060 } 1061 } 1062 } else { 1063 log("No carrier ImsReasonInfo mappings defined."); 1064 } 1065 } 1066 1067 private void handleEcmTimer(int action) { 1068 mPhone.handleTimerInEmergencyCallbackMode(action); 1069 switch (action) { 1070 case ImsPhone.CANCEL_ECM_TIMER: 1071 break; 1072 case ImsPhone.RESTART_ECM_TIMER: 1073 break; 1074 default: 1075 log("handleEcmTimer, unsupported action " + action); 1076 } 1077 } 1078 1079 private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState, 1080 Bundle intentExtras) { 1081 1082 if (conn == null) { 1083 return; 1084 } 1085 1086 if (conn.getAddress()== null || conn.getAddress().length() == 0 1087 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { 1088 // Phone number is invalid 1089 conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER); 1090 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1091 return; 1092 } 1093 1094 // Always unmute when initiating a new call 1095 setMute(false); 1096 int serviceType = mPhoneNumberUtilsProxy.isEmergencyNumber(conn.getAddress()) ? 1097 ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL; 1098 int callType = ImsCallProfile.getCallTypeFromVideoState(videoState); 1099 //TODO(vt): Is this sufficient? At what point do we know the video state of the call? 1100 conn.setVideoState(videoState); 1101 1102 try { 1103 String[] callees = new String[] { conn.getAddress() }; 1104 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 1105 serviceType, callType); 1106 profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode); 1107 1108 // Translate call subject intent-extra from Telecom-specific extra key to the 1109 // ImsCallProfile key. 1110 if (intentExtras != null) { 1111 if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) { 1112 intentExtras.putString(ImsCallProfile.EXTRA_DISPLAY_TEXT, 1113 cleanseInstantLetteringMessage(intentExtras.getString( 1114 android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) 1115 ); 1116 } 1117 1118 if (intentExtras.containsKey(ImsCallProfile.EXTRA_IS_CALL_PULL)) { 1119 profile.mCallExtras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, 1120 intentExtras.getBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL)); 1121 int dialogId = intentExtras.getInt( 1122 ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID); 1123 conn.setIsPulledCall(true); 1124 conn.setPulledDialogId(dialogId); 1125 } 1126 1127 // Pack the OEM-specific call extras. 1128 profile.mCallExtras.putBundle(ImsCallProfile.EXTRA_OEM_EXTRAS, intentExtras); 1129 1130 // NOTE: Extras to be sent over the network are packed into the 1131 // intentExtras individually, with uniquely defined keys. 1132 // These key-value pairs are processed by IMS Service before 1133 // being sent to the lower layers/to the network. 1134 } 1135 1136 ImsCall imsCall = mImsManager.makeCall(mServiceId, profile, 1137 callees, mImsCallListener); 1138 conn.setImsCall(imsCall); 1139 1140 mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), 1141 imsCall.getSession()); 1142 1143 setVideoCallProvider(conn, imsCall); 1144 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall); 1145 } catch (ImsException e) { 1146 loge("dialInternal : " + e); 1147 conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 1148 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1149 retryGetImsService(); 1150 } catch (RemoteException e) { 1151 } 1152 } 1153 1154 /** 1155 * Accepts a call with the specified video state. The video state is the video state that the 1156 * user has agreed upon in the InCall UI. 1157 * 1158 * @param videoState The video State 1159 * @throws CallStateException 1160 */ 1161 public void acceptCall (int videoState) throws CallStateException { 1162 if (DBG) log("acceptCall"); 1163 1164 if (mForegroundCall.getState().isAlive() 1165 && mBackgroundCall.getState().isAlive()) { 1166 throw new CallStateException("cannot accept call"); 1167 } 1168 1169 if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING) 1170 && mForegroundCall.getState().isAlive()) { 1171 setMute(false); 1172 1173 boolean answeringWillDisconnect = false; 1174 ImsCall activeCall = mForegroundCall.getImsCall(); 1175 ImsCall ringingCall = mRingingCall.getImsCall(); 1176 if (mForegroundCall.hasConnections() && mRingingCall.hasConnections()) { 1177 answeringWillDisconnect = 1178 shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall); 1179 } 1180 1181 // Cache video state for pending MT call. 1182 mPendingCallVideoState = videoState; 1183 1184 if (answeringWillDisconnect) { 1185 // We need to disconnect the foreground call before answering the background call. 1186 mForegroundCall.hangup(); 1187 try { 1188 ringingCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 1189 } catch (ImsException e) { 1190 throw new CallStateException("cannot accept call"); 1191 } 1192 } else { 1193 switchWaitingOrHoldingAndActive(); 1194 } 1195 } else if (mRingingCall.getState().isRinging()) { 1196 if (DBG) log("acceptCall: incoming..."); 1197 // Always unmute when answering a new call 1198 setMute(false); 1199 try { 1200 ImsCall imsCall = mRingingCall.getImsCall(); 1201 if (imsCall != null) { 1202 imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 1203 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1204 ImsCommand.IMS_CMD_ACCEPT); 1205 } else { 1206 throw new CallStateException("no valid ims call"); 1207 } 1208 } catch (ImsException e) { 1209 throw new CallStateException("cannot accept call"); 1210 } 1211 } else { 1212 throw new CallStateException("phone not ringing"); 1213 } 1214 } 1215 1216 public void rejectCall () throws CallStateException { 1217 if (DBG) log("rejectCall"); 1218 1219 if (mRingingCall.getState().isRinging()) { 1220 hangup(mRingingCall); 1221 } else { 1222 throw new CallStateException("phone not ringing"); 1223 } 1224 } 1225 1226 1227 private void switchAfterConferenceSuccess() { 1228 if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() + 1229 ", bg = " + mBackgroundCall.getState()); 1230 1231 if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 1232 log("switchAfterConferenceSuccess"); 1233 mForegroundCall.switchWith(mBackgroundCall); 1234 } 1235 } 1236 1237 public void switchWaitingOrHoldingAndActive() throws CallStateException { 1238 if (DBG) log("switchWaitingOrHoldingAndActive"); 1239 1240 if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) { 1241 throw new CallStateException("cannot be in the incoming state"); 1242 } 1243 1244 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 1245 ImsCall imsCall = mForegroundCall.getImsCall(); 1246 if (imsCall == null) { 1247 throw new CallStateException("no ims call"); 1248 } 1249 1250 // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls. 1251 // If hold or resume later fails, we will swap them back. 1252 boolean switchingWithWaitingCall = !mBackgroundCall.getState().isAlive() && 1253 mRingingCall != null && 1254 mRingingCall.getState() == ImsPhoneCall.State.WAITING; 1255 1256 mSwitchingFgAndBgCalls = true; 1257 if (switchingWithWaitingCall) { 1258 mCallExpectedToResume = mRingingCall.getImsCall(); 1259 } else { 1260 mCallExpectedToResume = mBackgroundCall.getImsCall(); 1261 } 1262 mForegroundCall.switchWith(mBackgroundCall); 1263 1264 // Hold the foreground call; once the foreground call is held, the background call will 1265 // be resumed. 1266 try { 1267 imsCall.hold(); 1268 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1269 ImsCommand.IMS_CMD_HOLD); 1270 1271 // If there is no background call to resume, then don't expect there to be a switch. 1272 if (mCallExpectedToResume == null) { 1273 log("mCallExpectedToResume is null"); 1274 mSwitchingFgAndBgCalls = false; 1275 } 1276 } catch (ImsException e) { 1277 mForegroundCall.switchWith(mBackgroundCall); 1278 throw new CallStateException(e.getMessage()); 1279 } 1280 } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 1281 resumeWaitingOrHolding(); 1282 } 1283 } 1284 1285 public void 1286 conference() { 1287 ImsCall fgImsCall = mForegroundCall.getImsCall(); 1288 if (fgImsCall == null) { 1289 log("conference no foreground ims call"); 1290 return; 1291 } 1292 1293 ImsCall bgImsCall = mBackgroundCall.getImsCall(); 1294 if (bgImsCall == null) { 1295 log("conference no background ims call"); 1296 return; 1297 } 1298 1299 if (fgImsCall.isCallSessionMergePending()) { 1300 log("conference: skip; foreground call already in process of merging."); 1301 return; 1302 } 1303 1304 if (bgImsCall.isCallSessionMergePending()) { 1305 log("conference: skip; background call already in process of merging."); 1306 return; 1307 } 1308 1309 // Keep track of the connect time of the earliest call so that it can be set on the 1310 // {@code ImsConference} when it is created. 1311 long foregroundConnectTime = mForegroundCall.getEarliestConnectTime(); 1312 long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime(); 1313 long conferenceConnectTime; 1314 if (foregroundConnectTime > 0 && backgroundConnectTime > 0) { 1315 conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(), 1316 mBackgroundCall.getEarliestConnectTime()); 1317 log("conference - using connect time = " + conferenceConnectTime); 1318 } else if (foregroundConnectTime > 0) { 1319 log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime); 1320 conferenceConnectTime = foregroundConnectTime; 1321 } else { 1322 log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime); 1323 conferenceConnectTime = backgroundConnectTime; 1324 } 1325 1326 String foregroundId = ""; 1327 ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection(); 1328 if (foregroundConnection != null) { 1329 foregroundConnection.setConferenceConnectTime(conferenceConnectTime); 1330 foregroundConnection.handleMergeStart(); 1331 foregroundId = foregroundConnection.getTelecomCallId(); 1332 } 1333 String backgroundId = ""; 1334 ImsPhoneConnection backgroundConnection = findConnection(bgImsCall); 1335 if (backgroundConnection != null) { 1336 backgroundConnection.handleMergeStart(); 1337 backgroundId = backgroundConnection.getTelecomCallId(); 1338 } 1339 log("conference: fgCallId=" + foregroundId + ", bgCallId=" + backgroundId); 1340 1341 try { 1342 fgImsCall.merge(bgImsCall); 1343 } catch (ImsException e) { 1344 log("conference " + e.getMessage()); 1345 } 1346 } 1347 1348 public void 1349 explicitCallTransfer() { 1350 //TODO : implement 1351 } 1352 1353 public void 1354 clearDisconnected() { 1355 if (DBG) log("clearDisconnected"); 1356 1357 internalClearDisconnected(); 1358 1359 updatePhoneState(); 1360 mPhone.notifyPreciseCallStateChanged(); 1361 } 1362 1363 public boolean 1364 canConference() { 1365 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 1366 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING 1367 && !mBackgroundCall.isFull() 1368 && !mForegroundCall.isFull(); 1369 } 1370 1371 public boolean canDial() { 1372 boolean ret; 1373 String disableCall = SystemProperties.get( 1374 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 1375 1376 ret = mPendingMO == null 1377 && !mRingingCall.isRinging() 1378 && !disableCall.equals("true") 1379 && (!mForegroundCall.getState().isAlive() 1380 || !mBackgroundCall.getState().isAlive()); 1381 1382 return ret; 1383 } 1384 1385 public boolean 1386 canTransfer() { 1387 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 1388 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING; 1389 } 1390 1391 //***** Private Instance Methods 1392 1393 private void 1394 internalClearDisconnected() { 1395 mRingingCall.clearDisconnected(); 1396 mForegroundCall.clearDisconnected(); 1397 mBackgroundCall.clearDisconnected(); 1398 mHandoverCall.clearDisconnected(); 1399 } 1400 1401 private void 1402 updatePhoneState() { 1403 PhoneConstants.State oldState = mState; 1404 1405 boolean isPendingMOIdle = mPendingMO == null || !mPendingMO.getState().isAlive(); 1406 1407 if (mRingingCall.isRinging()) { 1408 mState = PhoneConstants.State.RINGING; 1409 } else if (!isPendingMOIdle || !mForegroundCall.isIdle() || !mBackgroundCall.isIdle()) { 1410 // There is a non-idle call, so we're off the hook. 1411 mState = PhoneConstants.State.OFFHOOK; 1412 } else { 1413 mState = PhoneConstants.State.IDLE; 1414 } 1415 1416 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 1417 mVoiceCallEndedRegistrants.notifyRegistrants( 1418 new AsyncResult(null, null, null)); 1419 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 1420 mVoiceCallStartedRegistrants.notifyRegistrants ( 1421 new AsyncResult(null, null, null)); 1422 } 1423 1424 if (DBG) { 1425 log("updatePhoneState pendingMo = " + (mPendingMO == null ? "null" 1426 : mPendingMO.getState()) + ", fg= " + mForegroundCall.getState() + "(" 1427 + mForegroundCall.getConnections().size() + "), bg= " + mBackgroundCall 1428 .getState() + "(" + mBackgroundCall.getConnections().size() + ")"); 1429 log("updatePhoneState oldState=" + oldState + ", newState=" + mState); 1430 } 1431 1432 if (mState != oldState) { 1433 mPhone.notifyPhoneStateChanged(); 1434 mMetrics.writePhoneState(mPhone.getPhoneId(), mState); 1435 notifyPhoneStateChanged(oldState, mState); 1436 } 1437 } 1438 1439 private void 1440 handleRadioNotAvailable() { 1441 // handlePollCalls will clear out its 1442 // call list when it gets the CommandException 1443 // error result from this 1444 pollCallsWhenSafe(); 1445 } 1446 1447 private void 1448 dumpState() { 1449 List l; 1450 1451 log("Phone State:" + mState); 1452 1453 log("Ringing call: " + mRingingCall.toString()); 1454 1455 l = mRingingCall.getConnections(); 1456 for (int i = 0, s = l.size(); i < s; i++) { 1457 log(l.get(i).toString()); 1458 } 1459 1460 log("Foreground call: " + mForegroundCall.toString()); 1461 1462 l = mForegroundCall.getConnections(); 1463 for (int i = 0, s = l.size(); i < s; i++) { 1464 log(l.get(i).toString()); 1465 } 1466 1467 log("Background call: " + mBackgroundCall.toString()); 1468 1469 l = mBackgroundCall.getConnections(); 1470 for (int i = 0, s = l.size(); i < s; i++) { 1471 log(l.get(i).toString()); 1472 } 1473 1474 } 1475 1476 //***** Called from ImsPhone 1477 /** 1478 * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status) 1479 */ 1480 public void setTtyMode(int ttyMode) { 1481 if (mImsManager == null) { 1482 Log.w(LOG_TAG, "ImsManager is null when setting TTY mode"); 1483 return; 1484 } 1485 1486 try { 1487 mImsManager.setTtyMode(ttyMode); 1488 } catch (ImsException e) { 1489 loge("setTtyMode : " + e); 1490 retryGetImsService(); 1491 } 1492 } 1493 1494 /** 1495 * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call 1496 * settings screen. 1497 */ 1498 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 1499 if (mImsManager == null) { 1500 mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException()); 1501 return; 1502 } 1503 1504 try { 1505 mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete); 1506 } catch (ImsException e) { 1507 loge("setUITTYMode : " + e); 1508 mPhone.sendErrorResponse(onComplete, e); 1509 retryGetImsService(); 1510 } 1511 } 1512 1513 public void setMute(boolean mute) { 1514 mDesiredMute = mute; 1515 mForegroundCall.setMute(mute); 1516 } 1517 1518 public boolean getMute() { 1519 return mDesiredMute; 1520 } 1521 1522 public void sendDtmf(char c, Message result) { 1523 if (DBG) log("sendDtmf"); 1524 1525 ImsCall imscall = mForegroundCall.getImsCall(); 1526 if (imscall != null) { 1527 imscall.sendDtmf(c, result); 1528 } 1529 } 1530 1531 public void 1532 startDtmf(char c) { 1533 if (DBG) log("startDtmf"); 1534 1535 ImsCall imscall = mForegroundCall.getImsCall(); 1536 if (imscall != null) { 1537 imscall.startDtmf(c); 1538 } else { 1539 loge("startDtmf : no foreground call"); 1540 } 1541 } 1542 1543 public void 1544 stopDtmf() { 1545 if (DBG) log("stopDtmf"); 1546 1547 ImsCall imscall = mForegroundCall.getImsCall(); 1548 if (imscall != null) { 1549 imscall.stopDtmf(); 1550 } else { 1551 loge("stopDtmf : no foreground call"); 1552 } 1553 } 1554 1555 //***** Called from ImsPhoneConnection 1556 1557 public void hangup (ImsPhoneConnection conn) throws CallStateException { 1558 if (DBG) log("hangup connection"); 1559 1560 if (conn.getOwner() != this) { 1561 throw new CallStateException ("ImsPhoneConnection " + conn 1562 + "does not belong to ImsPhoneCallTracker " + this); 1563 } 1564 1565 hangup(conn.getCall()); 1566 } 1567 1568 //***** Called from ImsPhoneCall 1569 1570 public void hangup (ImsPhoneCall call) throws CallStateException { 1571 if (DBG) log("hangup call"); 1572 1573 if (call.getConnections().size() == 0) { 1574 throw new CallStateException("no connections"); 1575 } 1576 1577 ImsCall imsCall = call.getImsCall(); 1578 boolean rejectCall = false; 1579 1580 if (call == mRingingCall) { 1581 if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); 1582 rejectCall = true; 1583 } else if (call == mForegroundCall) { 1584 if (call.isDialingOrAlerting()) { 1585 if (Phone.DEBUG_PHONE) { 1586 log("(foregnd) hangup dialing or alerting..."); 1587 } 1588 } else { 1589 if (Phone.DEBUG_PHONE) { 1590 log("(foregnd) hangup foreground"); 1591 } 1592 //held call will be resumed by onCallTerminated 1593 } 1594 } else if (call == mBackgroundCall) { 1595 if (Phone.DEBUG_PHONE) { 1596 log("(backgnd) hangup waiting or background"); 1597 } 1598 } else { 1599 throw new CallStateException ("ImsPhoneCall " + call + 1600 "does not belong to ImsPhoneCallTracker " + this); 1601 } 1602 1603 call.onHangupLocal(); 1604 1605 try { 1606 if (imsCall != null) { 1607 if (rejectCall) { 1608 imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); 1609 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1610 ImsCommand.IMS_CMD_REJECT); 1611 } else { 1612 imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 1613 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1614 ImsCommand.IMS_CMD_TERMINATE); 1615 } 1616 } else if (mPendingMO != null && call == mForegroundCall) { 1617 // is holding a foreground call 1618 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); 1619 mPendingMO.onDisconnect(); 1620 removeConnection(mPendingMO); 1621 mPendingMO = null; 1622 updatePhoneState(); 1623 removeMessages(EVENT_DIAL_PENDINGMO); 1624 } 1625 } catch (ImsException e) { 1626 throw new CallStateException(e.getMessage()); 1627 } 1628 1629 mPhone.notifyPreciseCallStateChanged(); 1630 } 1631 1632 void callEndCleanupHandOverCallIfAny() { 1633 if (mHandoverCall.mConnections.size() > 0) { 1634 if (DBG) log("callEndCleanupHandOverCallIfAny, mHandoverCall.mConnections=" 1635 + mHandoverCall.mConnections); 1636 mHandoverCall.mConnections.clear(); 1637 mConnections.clear(); 1638 mState = PhoneConstants.State.IDLE; 1639 } 1640 } 1641 1642 /* package */ 1643 void resumeWaitingOrHolding() throws CallStateException { 1644 if (DBG) log("resumeWaitingOrHolding"); 1645 1646 try { 1647 if (mForegroundCall.getState().isAlive()) { 1648 //resume foreground call after holding background call 1649 //they were switched before holding 1650 ImsCall imsCall = mForegroundCall.getImsCall(); 1651 if (imsCall != null) { 1652 imsCall.resume(); 1653 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1654 ImsCommand.IMS_CMD_RESUME); 1655 } 1656 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) { 1657 //accept waiting call after holding background call 1658 ImsCall imsCall = mRingingCall.getImsCall(); 1659 if (imsCall != null) { 1660 imsCall.accept( 1661 ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState)); 1662 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1663 ImsCommand.IMS_CMD_ACCEPT); 1664 } 1665 } else { 1666 //Just resume background call. 1667 //To distinguish resuming call with swapping calls 1668 //we do not switch calls.here 1669 //ImsPhoneConnection.update will chnage the parent when completed 1670 ImsCall imsCall = mBackgroundCall.getImsCall(); 1671 if (imsCall != null) { 1672 imsCall.resume(); 1673 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1674 ImsCommand.IMS_CMD_RESUME); 1675 } 1676 } 1677 } catch (ImsException e) { 1678 throw new CallStateException(e.getMessage()); 1679 } 1680 } 1681 1682 public void sendUSSD (String ussdString, Message response) { 1683 if (DBG) log("sendUSSD"); 1684 1685 try { 1686 if (mUssdSession != null) { 1687 mUssdSession.sendUssd(ussdString); 1688 AsyncResult.forMessage(response, null, null); 1689 response.sendToTarget(); 1690 return; 1691 } 1692 1693 if (mImsManager == null) { 1694 mPhone.sendErrorResponse(response, getImsManagerIsNullException()); 1695 return; 1696 } 1697 1698 String[] callees = new String[] { ussdString }; 1699 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 1700 ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE); 1701 profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, 1702 ImsCallProfile.DIALSTRING_USSD); 1703 1704 mUssdSession = mImsManager.makeCall(mServiceId, profile, 1705 callees, mImsUssdListener); 1706 } catch (ImsException e) { 1707 loge("sendUSSD : " + e); 1708 mPhone.sendErrorResponse(response, e); 1709 retryGetImsService(); 1710 } 1711 } 1712 1713 public void cancelUSSD() { 1714 if (mUssdSession == null) return; 1715 1716 try { 1717 mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 1718 } catch (ImsException e) { 1719 } 1720 1721 } 1722 1723 private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) { 1724 for (ImsPhoneConnection conn : mConnections) { 1725 if (conn.getImsCall() == imsCall) { 1726 return conn; 1727 } 1728 } 1729 return null; 1730 } 1731 1732 private synchronized void removeConnection(ImsPhoneConnection conn) { 1733 mConnections.remove(conn); 1734 // If not emergency call is remaining, notify emergency call registrants 1735 if (mIsInEmergencyCall) { 1736 boolean isEmergencyCallInList = false; 1737 // if no emergency calls pending, set this to false 1738 for (ImsPhoneConnection imsPhoneConnection : mConnections) { 1739 if (imsPhoneConnection != null && imsPhoneConnection.isEmergency() == true) { 1740 isEmergencyCallInList = true; 1741 break; 1742 } 1743 } 1744 1745 if (!isEmergencyCallInList) { 1746 mIsInEmergencyCall = false; 1747 mPhone.sendEmergencyCallStateChange(false); 1748 } 1749 } 1750 } 1751 1752 private synchronized void addConnection(ImsPhoneConnection conn) { 1753 mConnections.add(conn); 1754 if (conn.isEmergency()) { 1755 mIsInEmergencyCall = true; 1756 mPhone.sendEmergencyCallStateChange(true); 1757 } 1758 } 1759 1760 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { 1761 if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause); 1762 // This method is called on onCallUpdate() where there is not necessarily a call state 1763 // change. In these situations, we'll ignore the state related updates and only process 1764 // the change in media capabilities (as expected). The default is to not ignore state 1765 // changes so we do not change existing behavior. 1766 processCallStateChange(imsCall, state, cause, false /* do not ignore state update */); 1767 } 1768 1769 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause, 1770 boolean ignoreState) { 1771 if (DBG) { 1772 log("processCallStateChange state=" + state + " cause=" + cause 1773 + " ignoreState=" + ignoreState); 1774 } 1775 1776 if (imsCall == null) return; 1777 1778 boolean changed = false; 1779 ImsPhoneConnection conn = findConnection(imsCall); 1780 1781 if (conn == null) { 1782 // TODO : what should be done? 1783 return; 1784 } 1785 1786 // processCallStateChange is triggered for onCallUpdated as well. 1787 // onCallUpdated should not modify the state of the call 1788 // It should modify only other capabilities of call through updateMediaCapabilities 1789 // State updates will be triggered through individual callbacks 1790 // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update 1791 conn.updateMediaCapabilities(imsCall); 1792 if (ignoreState) { 1793 conn.updateAddressDisplay(imsCall); 1794 conn.updateExtras(imsCall); 1795 1796 maybeSetVideoCallProvider(conn, imsCall); 1797 return; 1798 } 1799 1800 changed = conn.update(imsCall, state); 1801 if (state == ImsPhoneCall.State.DISCONNECTED) { 1802 changed = conn.onDisconnect(cause) || changed; 1803 //detach the disconnected connections 1804 conn.getCall().detach(conn); 1805 removeConnection(conn); 1806 } 1807 1808 if (changed) { 1809 if (conn.getCall() == mHandoverCall) return; 1810 updatePhoneState(); 1811 mPhone.notifyPreciseCallStateChanged(); 1812 } 1813 } 1814 1815 private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) { 1816 android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider(); 1817 if (connVideoProvider != null || imsCall.getCallSession().getVideoCallProvider() == null) { 1818 return; 1819 } 1820 1821 try { 1822 setVideoCallProvider(conn, imsCall); 1823 } catch (RemoteException e) { 1824 loge("maybeSetVideoCallProvider: exception " + e); 1825 } 1826 } 1827 1828 /** 1829 * Adds a reason code remapping, for test purposes. 1830 * 1831 * @param fromCode The from code, or {@code null} if all. 1832 * @param message The message to map. 1833 * @param toCode The code to remap to. 1834 */ 1835 @VisibleForTesting 1836 public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) { 1837 mImsReasonCodeMap.put(new Pair<>(fromCode, message), toCode); 1838 } 1839 1840 /** 1841 * Returns the {@link ImsReasonInfo#getCode()}, potentially remapping to a new value based on 1842 * the {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()}. 1843 * 1844 * See {@link #mImsReasonCodeMap}. 1845 * 1846 * @param reasonInfo The {@link ImsReasonInfo}. 1847 * @return The remapped code. 1848 */ 1849 @VisibleForTesting 1850 public int maybeRemapReasonCode(ImsReasonInfo reasonInfo) { 1851 int code = reasonInfo.getCode(); 1852 1853 Pair<Integer, String> toCheck = new Pair<>(code, reasonInfo.getExtraMessage()); 1854 Pair<Integer, String> wildcardToCheck = new Pair<>(null, reasonInfo.getExtraMessage()); 1855 if (mImsReasonCodeMap.containsKey(toCheck)) { 1856 int toCode = mImsReasonCodeMap.get(toCheck); 1857 1858 log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = " 1859 + reasonInfo.getExtraMessage() + " ; toCode = " + toCode); 1860 return toCode; 1861 } else if (mImsReasonCodeMap.containsKey(wildcardToCheck)) { 1862 // Handle the case where a wildcard is specified for the fromCode; in this case we will 1863 // match without caring about the fromCode. 1864 int toCode = mImsReasonCodeMap.get(wildcardToCheck); 1865 1866 log("maybeRemapReasonCode : fromCode(wildcard) = " + reasonInfo.getCode() + 1867 " ; message = " + reasonInfo.getExtraMessage() + " ; toCode = " + toCode); 1868 return toCode; 1869 } 1870 return code; 1871 } 1872 1873 /** 1874 * Maps an {@link ImsReasonInfo} reason code to a {@link DisconnectCause} cause code. 1875 * The {@link Call.State} provided is the state of the call prior to disconnection. 1876 * @param reasonInfo the {@link ImsReasonInfo} for the disconnection. 1877 * @param callState The {@link Call.State} prior to disconnection. 1878 * @return The {@link DisconnectCause} code. 1879 */ 1880 @VisibleForTesting 1881 public int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState) { 1882 int cause = DisconnectCause.ERROR_UNSPECIFIED; 1883 1884 int code = maybeRemapReasonCode(reasonInfo); 1885 switch (code) { 1886 case ImsReasonInfo.CODE_SIP_BAD_ADDRESS: 1887 case ImsReasonInfo.CODE_SIP_NOT_REACHABLE: 1888 return DisconnectCause.NUMBER_UNREACHABLE; 1889 1890 case ImsReasonInfo.CODE_SIP_BUSY: 1891 return DisconnectCause.BUSY; 1892 1893 case ImsReasonInfo.CODE_USER_TERMINATED: 1894 return DisconnectCause.LOCAL; 1895 1896 case ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE: 1897 return DisconnectCause.IMS_MERGED_SUCCESSFULLY; 1898 1899 case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE: 1900 case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE: 1901 // If the call has been declined locally (on this device), or on remotely (on 1902 // another device using multiendpoint functionality), mark it as rejected. 1903 return DisconnectCause.INCOMING_REJECTED; 1904 1905 case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE: 1906 return DisconnectCause.NORMAL; 1907 1908 case ImsReasonInfo.CODE_SIP_FORBIDDEN: 1909 return DisconnectCause.SERVER_ERROR; 1910 1911 case ImsReasonInfo.CODE_SIP_REDIRECTED: 1912 case ImsReasonInfo.CODE_SIP_BAD_REQUEST: 1913 case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE: 1914 case ImsReasonInfo.CODE_SIP_USER_REJECTED: 1915 case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR: 1916 return DisconnectCause.SERVER_ERROR; 1917 1918 case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE: 1919 case ImsReasonInfo.CODE_SIP_NOT_FOUND: 1920 case ImsReasonInfo.CODE_SIP_SERVER_ERROR: 1921 return DisconnectCause.SERVER_UNREACHABLE; 1922 1923 case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING: 1924 case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED: 1925 case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN: 1926 case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE: 1927 case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED: 1928 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE: 1929 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE: 1930 case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING: 1931 return DisconnectCause.OUT_OF_SERVICE; 1932 1933 case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT: 1934 case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING: 1935 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER: 1936 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE: 1937 return DisconnectCause.TIMED_OUT; 1938 1939 case ImsReasonInfo.CODE_LOCAL_POWER_OFF: 1940 return DisconnectCause.POWER_OFF; 1941 1942 case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY: 1943 case ImsReasonInfo.CODE_LOW_BATTERY: { 1944 if (callState == Call.State.DIALING) { 1945 return DisconnectCause.DIAL_LOW_BATTERY; 1946 } else { 1947 return DisconnectCause.LOW_BATTERY; 1948 } 1949 } 1950 1951 case ImsReasonInfo.CODE_CALL_BARRED: 1952 return DisconnectCause.CALL_BARRED; 1953 1954 case ImsReasonInfo.CODE_FDN_BLOCKED: 1955 return DisconnectCause.FDN_BLOCKED; 1956 1957 case ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED: 1958 return DisconnectCause.IMEI_NOT_ACCEPTED; 1959 1960 case ImsReasonInfo.CODE_ANSWERED_ELSEWHERE: 1961 return DisconnectCause.ANSWERED_ELSEWHERE; 1962 1963 case ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL: 1964 return DisconnectCause.CALL_PULLED; 1965 1966 case ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED: 1967 return DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED; 1968 1969 case ImsReasonInfo.CODE_DATA_DISABLED: 1970 return DisconnectCause.DATA_DISABLED; 1971 1972 case ImsReasonInfo.CODE_DATA_LIMIT_REACHED: 1973 return DisconnectCause.DATA_LIMIT_REACHED; 1974 1975 case ImsReasonInfo.CODE_WIFI_LOST: 1976 return DisconnectCause.WIFI_LOST; 1977 1978 case ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED: 1979 return DisconnectCause.IMS_ACCESS_BLOCKED; 1980 1981 case ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE: 1982 return DisconnectCause.EMERGENCY_TEMP_FAILURE; 1983 1984 case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE: 1985 return DisconnectCause.EMERGENCY_PERM_FAILURE; 1986 1987 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_USSD: 1988 return DisconnectCause.DIAL_MODIFIED_TO_USSD; 1989 1990 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_SS: 1991 return DisconnectCause.DIAL_MODIFIED_TO_SS; 1992 1993 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL: 1994 return DisconnectCause.DIAL_MODIFIED_TO_DIAL; 1995 1996 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL_VIDEO: 1997 return DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO; 1998 1999 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL: 2000 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL; 2001 2002 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO: 2003 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO; 2004 2005 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_SS: 2006 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS; 2007 2008 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_USSD: 2009 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD; 2010 2011 default: 2012 } 2013 2014 return cause; 2015 } 2016 2017 private int getPreciseDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) { 2018 return PRECISE_CAUSE_MAP.get(maybeRemapReasonCode(reasonInfo), 2019 PreciseDisconnectCause.ERROR_UNSPECIFIED); 2020 } 2021 2022 /** 2023 * @return true if the phone is in Emergency Callback mode, otherwise false 2024 */ 2025 private boolean isPhoneInEcbMode() { 2026 return mPhone.isInEcm(); 2027 } 2028 2029 /** 2030 * Before dialing pending MO request, check for the Emergency Callback mode. 2031 * If device is in Emergency callback mode, then exit the mode before dialing pending MO. 2032 */ 2033 private void dialPendingMO() { 2034 boolean isPhoneInEcmMode = isPhoneInEcbMode(); 2035 boolean isEmergencyNumber = mPendingMO.isEmergency(); 2036 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 2037 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 2038 } else { 2039 sendEmptyMessage(EVENT_EXIT_ECBM_BEFORE_PENDINGMO); 2040 } 2041 } 2042 2043 /** 2044 * Listen to the IMS call state change 2045 */ 2046 private ImsCall.Listener mImsCallListener = new ImsCall.Listener() { 2047 @Override 2048 public void onCallProgressing(ImsCall imsCall) { 2049 if (DBG) log("onCallProgressing"); 2050 2051 mPendingMO = null; 2052 processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING, 2053 DisconnectCause.NOT_DISCONNECTED); 2054 mMetrics.writeOnImsCallProgressing(mPhone.getPhoneId(), imsCall.getCallSession()); 2055 } 2056 2057 @Override 2058 public void onCallStarted(ImsCall imsCall) { 2059 if (DBG) log("onCallStarted"); 2060 2061 if (mSwitchingFgAndBgCalls) { 2062 // If we put a call on hold to answer an incoming call, we should reset the 2063 // variables that keep track of the switch here. 2064 if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) { 2065 if (DBG) log("onCallStarted: starting a call as a result of a switch."); 2066 mSwitchingFgAndBgCalls = false; 2067 mCallExpectedToResume = null; 2068 } 2069 } 2070 2071 mPendingMO = null; 2072 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 2073 DisconnectCause.NOT_DISCONNECTED); 2074 2075 if (mNotifyVtHandoverToWifiFail && 2076 !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) { 2077 // Schedule check to see if handover succeeded. 2078 sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall), 2079 HANDOVER_TO_WIFI_TIMEOUT_MS); 2080 } 2081 2082 mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession()); 2083 } 2084 2085 @Override 2086 public void onCallUpdated(ImsCall imsCall) { 2087 if (DBG) log("onCallUpdated"); 2088 if (imsCall == null) { 2089 return; 2090 } 2091 ImsPhoneConnection conn = findConnection(imsCall); 2092 if (conn != null) { 2093 processCallStateChange(imsCall, conn.getCall().mState, 2094 DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/); 2095 mMetrics.writeImsCallState(mPhone.getPhoneId(), 2096 imsCall.getCallSession(), conn.getCall().mState); 2097 } 2098 } 2099 2100 /** 2101 * onCallStartFailed will be invoked when: 2102 * case 1) Dialing fails 2103 * case 2) Ringing call is disconnected by local or remote user 2104 */ 2105 @Override 2106 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2107 if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); 2108 2109 if (mSwitchingFgAndBgCalls) { 2110 // If we put a call on hold to answer an incoming call, we should reset the 2111 // variables that keep track of the switch here. 2112 if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) { 2113 if (DBG) log("onCallStarted: starting a call as a result of a switch."); 2114 mSwitchingFgAndBgCalls = false; 2115 mCallExpectedToResume = null; 2116 } 2117 } 2118 2119 if (mPendingMO != null) { 2120 // To initiate dialing circuit-switched call 2121 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED 2122 && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE 2123 && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { 2124 mForegroundCall.detach(mPendingMO); 2125 removeConnection(mPendingMO); 2126 mPendingMO.finalize(); 2127 mPendingMO = null; 2128 mPhone.initiateSilentRedial(); 2129 return; 2130 } else { 2131 mPendingMO = null; 2132 ImsPhoneConnection conn = findConnection(imsCall); 2133 Call.State callState; 2134 if (conn != null) { 2135 callState = conn.getState(); 2136 } else { 2137 // Need to fall back in case connection is null; it shouldn't be, but a sane 2138 // fallback is to assume we're dialing. This state is only used to 2139 // determine which disconnect string to show in the case of a low battery 2140 // disconnect. 2141 callState = Call.State.DIALING; 2142 } 2143 int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState); 2144 2145 if(conn != null) { 2146 conn.setPreciseDisconnectCause( 2147 getPreciseDisconnectCauseFromReasonInfo(reasonInfo)); 2148 } 2149 2150 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 2151 } 2152 mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(), imsCall.getCallSession(), 2153 reasonInfo); 2154 } 2155 } 2156 2157 @Override 2158 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2159 if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); 2160 2161 ImsPhoneConnection conn = findConnection(imsCall); 2162 Call.State callState; 2163 if (conn != null) { 2164 callState = conn.getState(); 2165 } else { 2166 // Connection shouldn't be null, but if it is, we can assume the call was active. 2167 // This call state is only used for determining which disconnect message to show in 2168 // the case of the device's battery being low resulting in a call drop. 2169 callState = Call.State.ACTIVE; 2170 } 2171 int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState); 2172 2173 if (DBG) log("cause = " + cause + " conn = " + conn); 2174 2175 if (conn != null) { 2176 android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider(); 2177 if (videoProvider instanceof ImsVideoCallProviderWrapper) { 2178 ImsVideoCallProviderWrapper wrapper = (ImsVideoCallProviderWrapper) 2179 videoProvider; 2180 2181 wrapper.removeImsVideoProviderCallback(conn); 2182 } 2183 } 2184 if (mOnHoldToneId == System.identityHashCode(conn)) { 2185 if (conn != null && mOnHoldToneStarted) { 2186 mPhone.stopOnHoldTone(conn); 2187 } 2188 mOnHoldToneStarted = false; 2189 mOnHoldToneId = -1; 2190 } 2191 if (conn != null) { 2192 if (conn.isPulledCall() && ( 2193 reasonInfo.getCode() == ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC || 2194 reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE || 2195 reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_FORBIDDEN) && 2196 mPhone != null && mPhone.getExternalCallTracker() != null) { 2197 2198 log("Call pull failed."); 2199 // Call was being pulled, but the call pull has failed -- inform the associated 2200 // TelephonyConnection that the pull failed, and provide it with the original 2201 // external connection which was pulled so that it can be swapped back. 2202 conn.onCallPullFailed(mPhone.getExternalCallTracker() 2203 .getConnectionById(conn.getPulledDialogId())); 2204 // Do not mark as disconnected; the call will just change from being a regular 2205 // call to being an external call again. 2206 cause = DisconnectCause.NOT_DISCONNECTED; 2207 2208 } else if (conn.isIncoming() && conn.getConnectTime() == 0 2209 && cause != DisconnectCause.ANSWERED_ELSEWHERE) { 2210 // Missed 2211 if (cause == DisconnectCause.NORMAL) { 2212 cause = DisconnectCause.INCOMING_MISSED; 2213 } else { 2214 cause = DisconnectCause.INCOMING_REJECTED; 2215 } 2216 if (DBG) log("Incoming connection of 0 connect time detected - translated " + 2217 "cause = " + cause); 2218 } 2219 } 2220 2221 if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) { 2222 // Call was terminated while it is merged instead of a remote disconnect. 2223 cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY; 2224 } 2225 2226 mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(), 2227 reasonInfo); 2228 2229 if(conn != null) { 2230 conn.setPreciseDisconnectCause(getPreciseDisconnectCauseFromReasonInfo(reasonInfo)); 2231 } 2232 2233 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 2234 if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE) { 2235 if (mRingingCall.getState().isRinging()) { 2236 // Drop pending MO. We should address incoming call first 2237 mPendingMO = null; 2238 } else if (mPendingMO != null) { 2239 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 2240 } 2241 } 2242 2243 if (mSwitchingFgAndBgCalls) { 2244 if (DBG) { 2245 log("onCallTerminated: Call terminated in the midst of Switching " + 2246 "Fg and Bg calls."); 2247 } 2248 // If we are the in midst of swapping FG and BG calls and the call that was 2249 // terminated was the one that we expected to resume, we need to swap the FG and 2250 // BG calls back. 2251 if (imsCall == mCallExpectedToResume) { 2252 if (DBG) { 2253 log("onCallTerminated: switching " + mForegroundCall + " with " 2254 + mBackgroundCall); 2255 } 2256 mForegroundCall.switchWith(mBackgroundCall); 2257 } 2258 // This call terminated in the midst of a switch after the other call was held, so 2259 // resume it back to ACTIVE state since the switch failed. 2260 log("onCallTerminated: foreground call in state " + mForegroundCall.getState() + 2261 " and ringing call in state " + (mRingingCall == null ? "null" : 2262 mRingingCall.getState().toString())); 2263 2264 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING || 2265 mRingingCall.getState() == ImsPhoneCall.State.WAITING) { 2266 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 2267 mSwitchingFgAndBgCalls = false; 2268 mCallExpectedToResume = null; 2269 } 2270 } 2271 2272 if (mShouldUpdateImsConfigOnDisconnect) { 2273 // Ensure we update the IMS config when the call is disconnected; we delayed this 2274 // because a video call was paused. 2275 if (mImsManager != null) { 2276 mImsManager.updateImsServiceConfig(true); 2277 } 2278 mShouldUpdateImsConfigOnDisconnect = false; 2279 } 2280 } 2281 2282 @Override 2283 public void onCallHeld(ImsCall imsCall) { 2284 if (DBG) { 2285 if (mForegroundCall.getImsCall() == imsCall) { 2286 log("onCallHeld (fg) " + imsCall); 2287 } else if (mBackgroundCall.getImsCall() == imsCall) { 2288 log("onCallHeld (bg) " + imsCall); 2289 } 2290 } 2291 2292 synchronized (mSyncHold) { 2293 ImsPhoneCall.State oldState = mBackgroundCall.getState(); 2294 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING, 2295 DisconnectCause.NOT_DISCONNECTED); 2296 2297 // Note: If we're performing a switchWaitingOrHoldingAndActive, the call to 2298 // processCallStateChange above may have caused the mBackgroundCall and 2299 // mForegroundCall references below to change meaning. Watch out for this if you 2300 // are reading through this code. 2301 if (oldState == ImsPhoneCall.State.ACTIVE) { 2302 // Note: This case comes up when we have just held a call in response to a 2303 // switchWaitingOrHoldingAndActive. We now need to resume the background call. 2304 // The EVENT_RESUME_BACKGROUND causes resumeWaitingOrHolding to be called. 2305 if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) 2306 || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) { 2307 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 2308 } else { 2309 //when multiple connections belong to background call, 2310 //only the first callback reaches here 2311 //otherwise the oldState is already HOLDING 2312 if (mPendingMO != null) { 2313 dialPendingMO(); 2314 } 2315 2316 // In this case there will be no call resumed, so we can assume that we 2317 // are done switching fg and bg calls now. 2318 // This may happen if there is no BG call and we are holding a call so that 2319 // we can dial another one. 2320 mSwitchingFgAndBgCalls = false; 2321 } 2322 } else if (oldState == ImsPhoneCall.State.IDLE && mSwitchingFgAndBgCalls) { 2323 // The other call terminated in the midst of a switch before this call was held, 2324 // so resume the foreground call back to ACTIVE state since the switch failed. 2325 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 2326 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 2327 mSwitchingFgAndBgCalls = false; 2328 mCallExpectedToResume = null; 2329 } 2330 } 2331 } 2332 mMetrics.writeOnImsCallHeld(mPhone.getPhoneId(), imsCall.getCallSession()); 2333 } 2334 2335 @Override 2336 public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2337 if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); 2338 2339 synchronized (mSyncHold) { 2340 ImsPhoneCall.State bgState = mBackgroundCall.getState(); 2341 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { 2342 // disconnected while processing hold 2343 if (mPendingMO != null) { 2344 dialPendingMO(); 2345 } 2346 } else if (bgState == ImsPhoneCall.State.ACTIVE) { 2347 mForegroundCall.switchWith(mBackgroundCall); 2348 2349 if (mPendingMO != null) { 2350 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 2351 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 2352 } 2353 } 2354 mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD); 2355 } 2356 mMetrics.writeOnImsCallHoldFailed(mPhone.getPhoneId(), imsCall.getCallSession(), 2357 reasonInfo); 2358 } 2359 2360 @Override 2361 public void onCallResumed(ImsCall imsCall) { 2362 if (DBG) log("onCallResumed"); 2363 2364 // If we are the in midst of swapping FG and BG calls and the call we end up resuming 2365 // is not the one we expected, we likely had a resume failure and we need to swap the 2366 // FG and BG calls back. 2367 if (mSwitchingFgAndBgCalls) { 2368 if (imsCall != mCallExpectedToResume) { 2369 // If the call which resumed isn't as expected, we need to swap back to the 2370 // previous configuration; the swap has failed. 2371 if (DBG) { 2372 log("onCallResumed : switching " + mForegroundCall + " with " 2373 + mBackgroundCall); 2374 } 2375 mForegroundCall.switchWith(mBackgroundCall); 2376 } else { 2377 // The call which resumed is the one we expected to resume, so we can clear out 2378 // the mSwitchingFgAndBgCalls flag. 2379 if (DBG) { 2380 log("onCallResumed : expected call resumed."); 2381 } 2382 } 2383 mSwitchingFgAndBgCalls = false; 2384 mCallExpectedToResume = null; 2385 } 2386 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 2387 DisconnectCause.NOT_DISCONNECTED); 2388 mMetrics.writeOnImsCallResumed(mPhone.getPhoneId(), imsCall.getCallSession()); 2389 } 2390 2391 @Override 2392 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2393 if (mSwitchingFgAndBgCalls) { 2394 // If we are in the midst of swapping the FG and BG calls and 2395 // we got a resume fail, we need to swap back the FG and BG calls. 2396 // Since the FG call was held, will also try to resume the same. 2397 if (imsCall == mCallExpectedToResume) { 2398 if (DBG) { 2399 log("onCallResumeFailed : switching " + mForegroundCall + " with " 2400 + mBackgroundCall); 2401 } 2402 mForegroundCall.switchWith(mBackgroundCall); 2403 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 2404 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 2405 } 2406 } 2407 2408 //Call swap is done, reset the relevant variables 2409 mCallExpectedToResume = null; 2410 mSwitchingFgAndBgCalls = false; 2411 } 2412 mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME); 2413 mMetrics.writeOnImsCallResumeFailed(mPhone.getPhoneId(), imsCall.getCallSession(), 2414 reasonInfo); 2415 } 2416 2417 @Override 2418 public void onCallResumeReceived(ImsCall imsCall) { 2419 if (DBG) log("onCallResumeReceived"); 2420 ImsPhoneConnection conn = findConnection(imsCall); 2421 if (conn != null) { 2422 if (mOnHoldToneStarted) { 2423 mPhone.stopOnHoldTone(conn); 2424 mOnHoldToneStarted = false; 2425 } 2426 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null); 2427 } 2428 2429 boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean( 2430 com.android.internal.R.bool.config_useVideoPauseWorkaround); 2431 if (useVideoPauseWorkaround && mSupportPauseVideo && 2432 VideoProfile.isVideo(conn.getVideoState())) { 2433 // If we are using the video pause workaround, the vendor IMS code has issues 2434 // with video pause signalling. In this case, when a call is remotely 2435 // held, the modem does not reliably change the video state of the call to be 2436 // paused. 2437 // As a workaround, we will turn on that bit now. 2438 conn.changeToUnPausedState(); 2439 } 2440 2441 SuppServiceNotification supp = new SuppServiceNotification(); 2442 // Type of notification: 0 = MO; 1 = MT 2443 // Refer SuppServiceNotification class documentation. 2444 supp.notificationType = 1; 2445 supp.code = SuppServiceNotification.MT_CODE_CALL_RETRIEVED; 2446 mPhone.notifySuppSvcNotification(supp); 2447 mMetrics.writeOnImsCallResumeReceived(mPhone.getPhoneId(), imsCall.getCallSession()); 2448 } 2449 2450 @Override 2451 public void onCallHoldReceived(ImsCall imsCall) { 2452 ImsPhoneCallTracker.this.onCallHoldReceived(imsCall); 2453 } 2454 2455 @Override 2456 public void onCallSuppServiceReceived(ImsCall call, 2457 ImsSuppServiceNotification suppServiceInfo) { 2458 if (DBG) log("onCallSuppServiceReceived: suppServiceInfo=" + suppServiceInfo); 2459 2460 SuppServiceNotification supp = new SuppServiceNotification(); 2461 supp.notificationType = suppServiceInfo.notificationType; 2462 supp.code = suppServiceInfo.code; 2463 supp.index = suppServiceInfo.index; 2464 supp.number = suppServiceInfo.number; 2465 supp.history = suppServiceInfo.history; 2466 2467 mPhone.notifySuppSvcNotification(supp); 2468 } 2469 2470 @Override 2471 public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) { 2472 if (DBG) log("onCallMerged"); 2473 2474 ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall(); 2475 ImsPhoneConnection peerConnection = findConnection(peerCall); 2476 ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null 2477 : peerConnection.getCall(); 2478 2479 if (swapCalls) { 2480 switchAfterConferenceSuccess(); 2481 } 2482 foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE); 2483 2484 try { 2485 final ImsPhoneConnection conn = findConnection(call); 2486 log("onCallMerged: ImsPhoneConnection=" + conn); 2487 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider()); 2488 setVideoCallProvider(conn, call); 2489 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider()); 2490 } catch (Exception e) { 2491 loge("onCallMerged: exception " + e); 2492 } 2493 2494 // After merge complete, update foreground as Active 2495 // and background call as Held, if background call exists 2496 processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE, 2497 DisconnectCause.NOT_DISCONNECTED); 2498 if (peerConnection != null) { 2499 processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING, 2500 DisconnectCause.NOT_DISCONNECTED); 2501 } 2502 2503 // Check if the merge was requested by an existing conference call. In that 2504 // case, no further action is required. 2505 if (!call.isMergeRequestedByConf()) { 2506 log("onCallMerged :: calling onMultipartyStateChanged()"); 2507 onMultipartyStateChanged(call, true); 2508 } else { 2509 log("onCallMerged :: Merge requested by existing conference."); 2510 // Reset the flag. 2511 call.resetIsMergeRequestedByConf(false); 2512 } 2513 logState(); 2514 } 2515 2516 @Override 2517 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 2518 if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo); 2519 2520 // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog 2521 // We should move this into the InCallService so that it is handled appropriately 2522 // based on the user facing UI. 2523 mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE); 2524 2525 // Start plumbing this even through Telecom so other components can take 2526 // appropriate action. 2527 ImsPhoneConnection conn = findConnection(call); 2528 if (conn != null) { 2529 conn.onConferenceMergeFailed(); 2530 conn.handleMergeComplete(); 2531 } 2532 } 2533 2534 /** 2535 * Called when the state of IMS conference participant(s) has changed. 2536 * 2537 * @param call the call object that carries out the IMS call. 2538 * @param participants the participant(s) and their new state information. 2539 */ 2540 @Override 2541 public void onConferenceParticipantsStateChanged(ImsCall call, 2542 List<ConferenceParticipant> participants) { 2543 if (DBG) log("onConferenceParticipantsStateChanged"); 2544 2545 ImsPhoneConnection conn = findConnection(call); 2546 if (conn != null) { 2547 conn.updateConferenceParticipants(participants); 2548 } 2549 } 2550 2551 @Override 2552 public void onCallSessionTtyModeReceived(ImsCall call, int mode) { 2553 mPhone.onTtyModeReceived(mode); 2554 } 2555 2556 @Override 2557 public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 2558 ImsReasonInfo reasonInfo) { 2559 if (DBG) { 2560 log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech=" + 2561 targetAccessTech + ", reasonInfo=" + reasonInfo); 2562 } 2563 2564 // Only consider it a valid handover to WIFI if the source radio tech is known. 2565 boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN 2566 && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN 2567 && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2568 if (isHandoverToWifi) { 2569 // If we handed over to wifi successfully, don't check for failure in the future. 2570 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); 2571 } 2572 2573 ImsPhoneConnection conn = findConnection(imsCall); 2574 if (conn != null) { 2575 // Only consider it a handover from WIFI if the source and target radio tech is known. 2576 boolean isHandoverFromWifi = 2577 srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN 2578 && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN 2579 && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2580 if (isHandoverFromWifi && imsCall.isVideoCall()) { 2581 if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) { 2582 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 2583 log("onCallHandover :: notifying of WIFI to LTE handover."); 2584 conn.onConnectionEvent( 2585 TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null); 2586 } else { 2587 // Call has already had a disconnect request issued by the user or is 2588 // in the process of disconnecting; do not inform the UI of this as it 2589 // is not relevant. 2590 log("onCallHandover :: skip notify of WIFI to LTE handover for " 2591 + "disconnected call."); 2592 } 2593 } 2594 2595 if (!mIsDataEnabled && mIsViLteDataMetered) { 2596 // Call was downgraded from WIFI to LTE and data is metered; downgrade the 2597 // call now. 2598 downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn); 2599 } 2600 } 2601 } else { 2602 loge("onCallHandover :: connection null."); 2603 } 2604 2605 mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(), 2606 TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(), 2607 srcAccessTech, targetAccessTech, reasonInfo); 2608 } 2609 2610 @Override 2611 public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 2612 ImsReasonInfo reasonInfo) { 2613 if (DBG) { 2614 log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech + 2615 ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo); 2616 } 2617 mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(), 2618 TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED, 2619 imsCall.getCallSession(), srcAccessTech, targetAccessTech, reasonInfo); 2620 2621 boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN && 2622 targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2623 ImsPhoneConnection conn = findConnection(imsCall); 2624 if (conn != null && isHandoverToWifi) { 2625 log("onCallHandoverFailed - handover to WIFI Failed"); 2626 2627 // If we know we failed to handover, don't check for failure in the future. 2628 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); 2629 2630 if (mNotifyVtHandoverToWifiFail) { 2631 // Only notify others if carrier config indicates to do so. 2632 conn.onHandoverToWifiFailed(); 2633 } 2634 } 2635 } 2636 2637 @Override 2638 public void onRttModifyRequestReceived(ImsCall imsCall) { 2639 ImsPhoneConnection conn = findConnection(imsCall); 2640 if (conn != null) { 2641 conn.onRttModifyRequestReceived(); 2642 } 2643 } 2644 2645 @Override 2646 public void onRttModifyResponseReceived(ImsCall imsCall, int status) { 2647 ImsPhoneConnection conn = findConnection(imsCall); 2648 if (conn != null) { 2649 conn.onRttModifyResponseReceived(status); 2650 if (status == 2651 android.telecom.Connection.RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) { 2652 conn.startRttTextProcessing(); 2653 } 2654 } 2655 } 2656 2657 @Override 2658 public void onRttMessageReceived(ImsCall imsCall, String message) { 2659 ImsPhoneConnection conn = findConnection(imsCall); 2660 if (conn != null) { 2661 conn.onRttMessageReceived(message); 2662 } 2663 } 2664 2665 /** 2666 * Handles a change to the multiparty state for an {@code ImsCall}. Notifies the associated 2667 * {@link ImsPhoneConnection} of the change. 2668 * 2669 * @param imsCall The IMS call. 2670 * @param isMultiParty {@code true} if the call became multiparty, {@code false} 2671 * otherwise. 2672 */ 2673 @Override 2674 public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) { 2675 if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N")); 2676 2677 ImsPhoneConnection conn = findConnection(imsCall); 2678 if (conn != null) { 2679 conn.updateMultipartyState(isMultiParty); 2680 } 2681 } 2682 }; 2683 2684 /** 2685 * Listen to the IMS call state change 2686 */ 2687 private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() { 2688 @Override 2689 public void onCallStarted(ImsCall imsCall) { 2690 if (DBG) log("mImsUssdListener onCallStarted"); 2691 2692 if (imsCall == mUssdSession) { 2693 if (mPendingUssd != null) { 2694 AsyncResult.forMessage(mPendingUssd); 2695 mPendingUssd.sendToTarget(); 2696 mPendingUssd = null; 2697 } 2698 } 2699 } 2700 2701 @Override 2702 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2703 if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode()); 2704 2705 onCallTerminated(imsCall, reasonInfo); 2706 } 2707 2708 @Override 2709 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2710 if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); 2711 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); 2712 2713 if (imsCall == mUssdSession) { 2714 mUssdSession = null; 2715 if (mPendingUssd != null) { 2716 CommandException ex = 2717 new CommandException(CommandException.Error.GENERIC_FAILURE); 2718 AsyncResult.forMessage(mPendingUssd, null, ex); 2719 mPendingUssd.sendToTarget(); 2720 mPendingUssd = null; 2721 } 2722 } 2723 imsCall.close(); 2724 } 2725 2726 @Override 2727 public void onCallUssdMessageReceived(ImsCall call, 2728 int mode, String ussdMessage) { 2729 if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode); 2730 2731 int ussdMode = -1; 2732 2733 switch(mode) { 2734 case ImsCall.USSD_MODE_REQUEST: 2735 ussdMode = CommandsInterface.USSD_MODE_REQUEST; 2736 break; 2737 2738 case ImsCall.USSD_MODE_NOTIFY: 2739 ussdMode = CommandsInterface.USSD_MODE_NOTIFY; 2740 break; 2741 } 2742 2743 mPhone.onIncomingUSSD(ussdMode, ussdMessage); 2744 } 2745 }; 2746 2747 /** 2748 * Listen to the IMS service state change 2749 * 2750 */ 2751 private ImsConnectionStateListener mImsConnectionStateListener = 2752 new ImsConnectionStateListener() { 2753 @Override 2754 public void onImsConnected(int imsRadioTech) { 2755 if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech); 2756 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 2757 mPhone.setImsRegistered(true); 2758 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 2759 ImsConnectionState.State.CONNECTED, null); 2760 } 2761 2762 @Override 2763 public void onImsDisconnected(ImsReasonInfo imsReasonInfo) { 2764 if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo); 2765 resetImsCapabilities(); 2766 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 2767 mPhone.setImsRegistered(false); 2768 mPhone.processDisconnectReason(imsReasonInfo); 2769 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 2770 ImsConnectionState.State.DISCONNECTED, imsReasonInfo); 2771 } 2772 2773 @Override 2774 public void onImsProgressing(int imsRadioTech) { 2775 if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech); 2776 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 2777 mPhone.setImsRegistered(false); 2778 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 2779 ImsConnectionState.State.PROGRESSING, null); 2780 } 2781 2782 @Override 2783 public void onImsResumed() { 2784 if (DBG) log("onImsResumed"); 2785 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 2786 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 2787 ImsConnectionState.State.RESUMED, null); 2788 } 2789 2790 @Override 2791 public void onImsSuspended() { 2792 if (DBG) log("onImsSuspended"); 2793 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 2794 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 2795 ImsConnectionState.State.SUSPENDED, null); 2796 2797 } 2798 2799 @Override 2800 public void onFeatureCapabilityChanged(int serviceClass, 2801 int[] enabledFeatures, int[] disabledFeatures) { 2802 if (DBG) log("onFeatureCapabilityChanged"); 2803 SomeArgs args = SomeArgs.obtain(); 2804 args.argi1 = serviceClass; 2805 args.arg1 = enabledFeatures; 2806 args.arg2 = disabledFeatures; 2807 // Remove any pending updates; they're already stale, so no need to process them. 2808 removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED); 2809 obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget(); 2810 } 2811 2812 @Override 2813 public void onVoiceMessageCountChanged(int count) { 2814 if (DBG) log("onVoiceMessageCountChanged :: count=" + count); 2815 mPhone.mDefaultPhone.setVoiceMessageCount(count); 2816 } 2817 2818 @Override 2819 public void registrationAssociatedUriChanged(Uri[] uris) { 2820 if (DBG) log("registrationAssociatedUriChanged"); 2821 mPhone.setCurrentSubscriberUris(uris); 2822 } 2823 }; 2824 2825 private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() { 2826 @Override 2827 public void onGetFeatureResponse(int feature, int network, int value, int status) {} 2828 2829 @Override 2830 public void onSetFeatureResponse(int feature, int network, int value, int status) { 2831 mMetrics.writeImsSetFeatureValue( 2832 mPhone.getPhoneId(), feature, network, value, status); 2833 } 2834 2835 @Override 2836 public void onGetVideoQuality(int status, int quality) {} 2837 2838 @Override 2839 public void onSetVideoQuality(int status) {} 2840 2841 }; 2842 2843 public ImsUtInterface getUtInterface() throws ImsException { 2844 if (mImsManager == null) { 2845 throw getImsManagerIsNullException(); 2846 } 2847 2848 ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(); 2849 return ut; 2850 } 2851 2852 private void transferHandoverConnections(ImsPhoneCall call) { 2853 if (call.mConnections != null) { 2854 for (Connection c : call.mConnections) { 2855 c.mPreHandoverState = call.mState; 2856 log ("Connection state before handover is " + c.getStateBeforeHandover()); 2857 } 2858 } 2859 if (mHandoverCall.mConnections == null ) { 2860 mHandoverCall.mConnections = call.mConnections; 2861 } else { // Multi-call SRVCC 2862 mHandoverCall.mConnections.addAll(call.mConnections); 2863 } 2864 if (mHandoverCall.mConnections != null) { 2865 if (call.getImsCall() != null) { 2866 call.getImsCall().close(); 2867 } 2868 for (Connection c : mHandoverCall.mConnections) { 2869 ((ImsPhoneConnection)c).changeParent(mHandoverCall); 2870 ((ImsPhoneConnection)c).releaseWakeLock(); 2871 } 2872 } 2873 if (call.getState().isAlive()) { 2874 log ("Call is alive and state is " + call.mState); 2875 mHandoverCall.mState = call.mState; 2876 } 2877 call.mConnections.clear(); 2878 call.mState = ImsPhoneCall.State.IDLE; 2879 } 2880 2881 /* package */ 2882 void notifySrvccState(Call.SrvccState state) { 2883 if (DBG) log("notifySrvccState state=" + state); 2884 2885 mSrvccState = state; 2886 2887 if (mSrvccState == Call.SrvccState.COMPLETED) { 2888 transferHandoverConnections(mForegroundCall); 2889 transferHandoverConnections(mBackgroundCall); 2890 transferHandoverConnections(mRingingCall); 2891 } 2892 } 2893 2894 //****** Overridden from Handler 2895 2896 @Override 2897 public void 2898 handleMessage (Message msg) { 2899 AsyncResult ar; 2900 if (DBG) log("handleMessage what=" + msg.what); 2901 2902 switch (msg.what) { 2903 case EVENT_HANGUP_PENDINGMO: 2904 if (mPendingMO != null) { 2905 mPendingMO.onDisconnect(); 2906 removeConnection(mPendingMO); 2907 mPendingMO = null; 2908 } 2909 mPendingIntentExtras = null; 2910 updatePhoneState(); 2911 mPhone.notifyPreciseCallStateChanged(); 2912 break; 2913 case EVENT_RESUME_BACKGROUND: 2914 try { 2915 resumeWaitingOrHolding(); 2916 } catch (CallStateException e) { 2917 if (Phone.DEBUG_PHONE) { 2918 loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e); 2919 } 2920 } 2921 break; 2922 case EVENT_DIAL_PENDINGMO: 2923 dialInternal(mPendingMO, mClirMode, mPendingCallVideoState, mPendingIntentExtras); 2924 mPendingIntentExtras = null; 2925 break; 2926 2927 case EVENT_EXIT_ECBM_BEFORE_PENDINGMO: 2928 if (mPendingMO != null) { 2929 //Send ECBM exit request 2930 try { 2931 getEcbmInterface().exitEmergencyCallbackMode(); 2932 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 2933 pendingCallClirMode = mClirMode; 2934 pendingCallInEcm = true; 2935 } catch (ImsException e) { 2936 e.printStackTrace(); 2937 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 2938 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 2939 } 2940 } 2941 break; 2942 2943 case EVENT_EXIT_ECM_RESPONSE_CDMA: 2944 // no matter the result, we still do the same here 2945 if (pendingCallInEcm) { 2946 dialInternal(mPendingMO, pendingCallClirMode, 2947 mPendingCallVideoState, mPendingIntentExtras); 2948 mPendingIntentExtras = null; 2949 pendingCallInEcm = false; 2950 } 2951 mPhone.unsetOnEcbModeExitResponse(this); 2952 break; 2953 case EVENT_VT_DATA_USAGE_UPDATE: 2954 ar = (AsyncResult) msg.obj; 2955 ImsCall call = (ImsCall) ar.userObj; 2956 Long usage = (long) ar.result; 2957 log("VT data usage update. usage = " + usage + ", imsCall = " + call); 2958 if (usage > 0) { 2959 updateVtDataUsage(call, usage); 2960 } 2961 break; 2962 case EVENT_DATA_ENABLED_CHANGED: 2963 ar = (AsyncResult) msg.obj; 2964 if (ar.result instanceof Pair) { 2965 Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result; 2966 onDataEnabledChanged(p.first, p.second); 2967 } 2968 break; 2969 case EVENT_GET_IMS_SERVICE: 2970 try { 2971 getImsService(); 2972 } catch (ImsException e) { 2973 loge("getImsService: " + e); 2974 retryGetImsService(); 2975 } 2976 break; 2977 case EVENT_CHECK_FOR_WIFI_HANDOVER: 2978 if (msg.obj instanceof ImsCall) { 2979 ImsCall imsCall = (ImsCall) msg.obj; 2980 if (!imsCall.isWifiCall()) { 2981 // Call did not handover to wifi, notify of handover failure. 2982 ImsPhoneConnection conn = findConnection(imsCall); 2983 if (conn != null) { 2984 conn.onHandoverToWifiFailed(); 2985 } 2986 } 2987 } 2988 break; 2989 case EVENT_ON_FEATURE_CAPABILITY_CHANGED: { 2990 SomeArgs args = (SomeArgs) msg.obj; 2991 try { 2992 int serviceClass = args.argi1; 2993 int[] enabledFeatures = (int[]) args.arg1; 2994 int[] disabledFeatures = (int[]) args.arg2; 2995 handleFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures); 2996 } finally { 2997 args.recycle(); 2998 } 2999 break; 3000 } 3001 case EVENT_SUPP_SERVICE_INDICATION: { 3002 ar = (AsyncResult) msg.obj; 3003 ImsPhoneMmiCode mmiCode = new ImsPhoneMmiCode(mPhone); 3004 try { 3005 mmiCode.setIsSsInfo(true); 3006 mmiCode.processImsSsData(ar); 3007 } catch (ImsException e) { 3008 Rlog.e(LOG_TAG, "Exception in parsing SS Data: " + e); 3009 } 3010 break; 3011 } 3012 } 3013 } 3014 3015 /** 3016 * Update video call data usage 3017 * 3018 * @param call The IMS call 3019 * @param dataUsage The aggregated data usage for the call 3020 */ 3021 private void updateVtDataUsage(ImsCall call, long dataUsage) { 3022 long oldUsage = 0L; 3023 if (mVtDataUsageMap.containsKey(call.uniqueId)) { 3024 oldUsage = mVtDataUsageMap.get(call.uniqueId); 3025 } 3026 3027 long delta = dataUsage - oldUsage; 3028 mVtDataUsageMap.put(call.uniqueId, dataUsage); 3029 3030 log("updateVtDataUsage: call=" + call + ", delta=" + delta); 3031 3032 long currentTime = SystemClock.elapsedRealtime(); 3033 int isRoaming = mPhone.getServiceState().getDataRoaming() ? 1 : 0; 3034 3035 // Create the snapshot of total video call data usage. 3036 NetworkStats vtDataUsageSnapshot = new NetworkStats(currentTime, 1); 3037 vtDataUsageSnapshot.combineAllValues(mVtDataUsageSnapshot); 3038 // Since the modem only reports the total vt data usage rather than rx/tx separately, 3039 // the only thing we can do here is splitting the usage into half rx and half tx. 3040 // Uid -1 indicates this is for the overall device data usage. 3041 vtDataUsageSnapshot.combineValues(new NetworkStats.Entry( 3042 NetworkStatsService.VT_INTERFACE, -1, NetworkStats.SET_FOREGROUND, 3043 NetworkStats.TAG_NONE, 1, isRoaming, delta / 2, 0, delta / 2, 0, 0)); 3044 mVtDataUsageSnapshot = vtDataUsageSnapshot; 3045 3046 // Create the snapshot of video call data usage per dialer. combineValues will create 3047 // a separate entry if uid is different from the previous snapshot. 3048 NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1); 3049 vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot); 3050 3051 // The dialer uid might not be initialized correctly during boot up due to telecom service 3052 // not ready or its default dialer cache not ready. So we double check again here to see if 3053 // default dialer uid is really not available. 3054 if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) { 3055 final TelecomManager telecomManager = 3056 (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE); 3057 mDefaultDialerUid.set( 3058 getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage())); 3059 } 3060 3061 // Since the modem only reports the total vt data usage rather than rx/tx separately, 3062 // the only thing we can do here is splitting the usage into half rx and half tx. 3063 vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry( 3064 NetworkStatsService.VT_INTERFACE, mDefaultDialerUid.get(), 3065 NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, 1, isRoaming, delta / 2, 3066 0, delta / 2, 0, 0)); 3067 mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot; 3068 } 3069 3070 @Override 3071 protected void log(String msg) { 3072 Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 3073 } 3074 3075 protected void loge(String msg) { 3076 Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 3077 } 3078 3079 /** 3080 * Logs the current state of the ImsPhoneCallTracker. Useful for debugging issues with 3081 * call tracking. 3082 */ 3083 /* package */ 3084 void logState() { 3085 if (!VERBOSE_STATE_LOGGING) { 3086 return; 3087 } 3088 3089 StringBuilder sb = new StringBuilder(); 3090 sb.append("Current IMS PhoneCall State:\n"); 3091 sb.append(" Foreground: "); 3092 sb.append(mForegroundCall); 3093 sb.append("\n"); 3094 sb.append(" Background: "); 3095 sb.append(mBackgroundCall); 3096 sb.append("\n"); 3097 sb.append(" Ringing: "); 3098 sb.append(mRingingCall); 3099 sb.append("\n"); 3100 sb.append(" Handover: "); 3101 sb.append(mHandoverCall); 3102 sb.append("\n"); 3103 Rlog.v(LOG_TAG, sb.toString()); 3104 } 3105 3106 @Override 3107 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3108 pw.println("ImsPhoneCallTracker extends:"); 3109 super.dump(fd, pw, args); 3110 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 3111 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 3112 pw.println(" mRingingCall=" + mRingingCall); 3113 pw.println(" mForegroundCall=" + mForegroundCall); 3114 pw.println(" mBackgroundCall=" + mBackgroundCall); 3115 pw.println(" mHandoverCall=" + mHandoverCall); 3116 pw.println(" mPendingMO=" + mPendingMO); 3117 //pw.println(" mHangupPendingMO=" + mHangupPendingMO); 3118 pw.println(" mPhone=" + mPhone); 3119 pw.println(" mDesiredMute=" + mDesiredMute); 3120 pw.println(" mState=" + mState); 3121 for (int i = 0; i < mImsFeatureEnabled.length; i++) { 3122 pw.println(" " + mImsFeatureStrings[i] + ": " 3123 + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled")); 3124 } 3125 pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get()); 3126 pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot); 3127 pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot); 3128 3129 pw.flush(); 3130 pw.println("++++++++++++++++++++++++++++++++"); 3131 3132 try { 3133 if (mImsManager != null) { 3134 mImsManager.dump(fd, pw, args); 3135 } 3136 } catch (Exception e) { 3137 e.printStackTrace(); 3138 } 3139 3140 if (mConnections != null && mConnections.size() > 0) { 3141 pw.println("mConnections:"); 3142 for (int i = 0; i < mConnections.size(); i++) { 3143 pw.println(" [" + i + "]: " + mConnections.get(i)); 3144 } 3145 } 3146 } 3147 3148 @Override 3149 protected void handlePollCalls(AsyncResult ar) { 3150 } 3151 3152 /* package */ 3153 ImsEcbm getEcbmInterface() throws ImsException { 3154 if (mImsManager == null) { 3155 throw getImsManagerIsNullException(); 3156 } 3157 3158 ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId); 3159 return ecbm; 3160 } 3161 3162 /* package */ 3163 ImsMultiEndpoint getMultiEndpointInterface() throws ImsException { 3164 if (mImsManager == null) { 3165 throw getImsManagerIsNullException(); 3166 } 3167 3168 try { 3169 return mImsManager.getMultiEndpointInterface(mServiceId); 3170 } catch (ImsException e) { 3171 if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) { 3172 return null; 3173 } else { 3174 throw e; 3175 } 3176 3177 } 3178 } 3179 3180 public boolean isInEmergencyCall() { 3181 return mIsInEmergencyCall; 3182 } 3183 3184 public boolean isVolteEnabled() { 3185 return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE]; 3186 } 3187 3188 public boolean isVowifiEnabled() { 3189 return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI]; 3190 } 3191 3192 public boolean isVideoCallEnabled() { 3193 return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] 3194 || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]); 3195 } 3196 3197 @Override 3198 public PhoneConstants.State getState() { 3199 return mState; 3200 } 3201 3202 private void retryGetImsService() { 3203 // The binder connection is already up. Do not try to get it again. 3204 if (mImsManager.isServiceAvailable()) { 3205 return; 3206 } 3207 //Leave mImsManager as null, then CallStateException will be thrown when dialing 3208 mImsManager = null; 3209 // Exponential backoff during retry, limited to 32 seconds. 3210 loge("getImsService: Retrying getting ImsService..."); 3211 removeMessages(EVENT_GET_IMS_SERVICE); 3212 sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get()); 3213 } 3214 3215 private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) 3216 throws RemoteException { 3217 IImsVideoCallProvider imsVideoCallProvider = 3218 imsCall.getCallSession().getVideoCallProvider(); 3219 if (imsVideoCallProvider != null) { 3220 // TODO: Remove this when we can better formalize the format of session modify requests. 3221 boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean( 3222 com.android.internal.R.bool.config_useVideoPauseWorkaround); 3223 3224 ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = 3225 new ImsVideoCallProviderWrapper(imsVideoCallProvider); 3226 if (useVideoPauseWorkaround) { 3227 imsVideoCallProviderWrapper.setUseVideoPauseWorkaround(useVideoPauseWorkaround); 3228 } 3229 conn.setVideoProvider(imsVideoCallProviderWrapper); 3230 imsVideoCallProviderWrapper.registerForDataUsageUpdate 3231 (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall); 3232 imsVideoCallProviderWrapper.addImsVideoProviderCallback(conn); 3233 } 3234 } 3235 3236 public boolean isUtEnabled() { 3237 return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE] 3238 || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI]); 3239 } 3240 3241 /** 3242 * Given a call subject, removes any characters considered by the current carrier to be 3243 * invalid, as well as escaping (using \) any characters which the carrier requires to be 3244 * escaped. 3245 * 3246 * @param callSubject The call subject. 3247 * @return The call subject with invalid characters removed and escaping applied as required. 3248 */ 3249 private String cleanseInstantLetteringMessage(String callSubject) { 3250 if (TextUtils.isEmpty(callSubject)) { 3251 return callSubject; 3252 } 3253 3254 // Get the carrier config for the current sub. 3255 CarrierConfigManager configMgr = (CarrierConfigManager) 3256 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 3257 // Bail if we can't find the carrier config service. 3258 if (configMgr == null) { 3259 return callSubject; 3260 } 3261 3262 PersistableBundle carrierConfig = configMgr.getConfigForSubId(mPhone.getSubId()); 3263 // Bail if no carrier config found. 3264 if (carrierConfig == null) { 3265 return callSubject; 3266 } 3267 3268 // Try to replace invalid characters 3269 String invalidCharacters = carrierConfig.getString( 3270 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING); 3271 if (!TextUtils.isEmpty(invalidCharacters)) { 3272 callSubject = callSubject.replaceAll(invalidCharacters, ""); 3273 } 3274 3275 // Try to escape characters which need to be escaped. 3276 String escapedCharacters = carrierConfig.getString( 3277 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING); 3278 if (!TextUtils.isEmpty(escapedCharacters)) { 3279 callSubject = escapeChars(escapedCharacters, callSubject); 3280 } 3281 return callSubject; 3282 } 3283 3284 /** 3285 * Given a source string, return a string where a set of characters are escaped using the 3286 * backslash character. 3287 * 3288 * @param toEscape The characters to escape with a backslash. 3289 * @param source The source string. 3290 * @return The source string with characters escaped. 3291 */ 3292 private String escapeChars(String toEscape, String source) { 3293 StringBuilder escaped = new StringBuilder(); 3294 for (char c : source.toCharArray()) { 3295 if (toEscape.contains(Character.toString(c))) { 3296 escaped.append("\\"); 3297 } 3298 escaped.append(c); 3299 } 3300 3301 return escaped.toString(); 3302 } 3303 3304 /** 3305 * Initiates a pull of an external call. 3306 * 3307 * Initiates a pull by making a dial request with the {@link ImsCallProfile#EXTRA_IS_CALL_PULL} 3308 * extra specified. We call {@link ImsPhone#notifyUnknownConnection(Connection)} which notifies 3309 * Telecom of the new dialed connection. The 3310 * {@code PstnIncomingCallNotifier#maybeSwapWithUnknownConnection} logic ensures that the new 3311 * {@link ImsPhoneConnection} resulting from the dial gets swapped with the 3312 * {@link ImsExternalConnection}, which effectively makes the external call become a regular 3313 * call. Magic! 3314 * 3315 * @param number The phone number of the call to be pulled. 3316 * @param videoState The desired video state of the pulled call. 3317 * @param dialogId The {@link ImsExternalConnection#getCallId()} dialog id associated with the 3318 * call which is being pulled. 3319 */ 3320 @Override 3321 public void pullExternalCall(String number, int videoState, int dialogId) { 3322 Bundle extras = new Bundle(); 3323 extras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, true); 3324 extras.putInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID, dialogId); 3325 try { 3326 Connection connection = dial(number, videoState, extras); 3327 mPhone.notifyUnknownConnection(connection); 3328 } catch (CallStateException e) { 3329 loge("pullExternalCall failed - " + e); 3330 } 3331 } 3332 3333 private ImsException getImsManagerIsNullException() { 3334 return new ImsException("no ims manager", ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 3335 } 3336 3337 /** 3338 * Determines if answering an incoming call will cause the active call to be disconnected. 3339 * <p> 3340 * This will be the case if 3341 * {@link CarrierConfigManager#KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL} is 3342 * {@code true} for the carrier, the active call is a video call over WIFI, and the incoming 3343 * call is an audio call. 3344 * 3345 * @param activeCall The active call. 3346 * @param incomingCall The incoming call. 3347 * @return {@code true} if answering the incoming call will cause the active call to be 3348 * disconnected, {@code false} otherwise. 3349 */ 3350 private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall, 3351 ImsCall incomingCall) { 3352 3353 if (activeCall == null || incomingCall == null) { 3354 return false; 3355 } 3356 3357 if (!mDropVideoCallWhenAnsweringAudioCall) { 3358 return false; 3359 } 3360 3361 boolean isActiveCallVideo = activeCall.isVideoCall() || 3362 (mTreatDowngradedVideoCallsAsVideoCalls && activeCall.wasVideoCall()); 3363 boolean isActiveCallOnWifi = activeCall.isWifiCall(); 3364 boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform() 3365 && mImsManager.isWfcEnabledByUser(); 3366 boolean isIncomingCallAudio = !incomingCall.isVideoCall(); 3367 log("shouldDisconnectActiveCallOnAnswer : isActiveCallVideo=" + isActiveCallVideo + 3368 " isActiveCallOnWifi=" + isActiveCallOnWifi + " isIncomingCallAudio=" + 3369 isIncomingCallAudio + " isVowifiEnabled=" + isVoWifiEnabled); 3370 3371 return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio && !isVoWifiEnabled; 3372 } 3373 3374 /** 3375 * Get aggregated video call data usage since boot. 3376 * 3377 * @param perUidStats True if requesting data usage per uid, otherwise overall usage. 3378 * @return Snapshot of video call data usage 3379 */ 3380 public NetworkStats getVtDataUsage(boolean perUidStats) { 3381 3382 // If there is an ongoing VT call, request the latest VT usage from the modem. The latest 3383 // usage will return asynchronously so it won't be counted in this round, but it will be 3384 // eventually counted when next getVtDataUsage is called. 3385 if (mState != PhoneConstants.State.IDLE) { 3386 for (ImsPhoneConnection conn : mConnections) { 3387 android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider(); 3388 if (videoProvider != null) { 3389 videoProvider.onRequestConnectionDataUsage(); 3390 } 3391 } 3392 } 3393 3394 return perUidStats ? mVtDataUsageUidSnapshot : mVtDataUsageSnapshot; 3395 } 3396 3397 public void registerPhoneStateListener(PhoneStateListener listener) { 3398 mPhoneStateListeners.add(listener); 3399 } 3400 3401 public void unregisterPhoneStateListener(PhoneStateListener listener) { 3402 mPhoneStateListeners.remove(listener); 3403 } 3404 3405 /** 3406 * Notifies local telephony listeners of changes to the IMS phone state. 3407 * 3408 * @param oldState The old state. 3409 * @param newState The new state. 3410 */ 3411 private void notifyPhoneStateChanged(PhoneConstants.State oldState, 3412 PhoneConstants.State newState) { 3413 3414 for (PhoneStateListener listener : mPhoneStateListeners) { 3415 listener.onPhoneStateChanged(oldState, newState); 3416 } 3417 } 3418 3419 /** Modify video call to a new video state. 3420 * 3421 * @param imsCall IMS call to be modified 3422 * @param newVideoState New video state. (Refer to VideoProfile) 3423 */ 3424 private void modifyVideoCall(ImsCall imsCall, int newVideoState) { 3425 ImsPhoneConnection conn = findConnection(imsCall); 3426 if (conn != null) { 3427 int oldVideoState = conn.getVideoState(); 3428 if (conn.getVideoProvider() != null) { 3429 conn.getVideoProvider().onSendSessionModifyRequest( 3430 new VideoProfile(oldVideoState), new VideoProfile(newVideoState)); 3431 } 3432 } 3433 } 3434 3435 /** 3436 * Handler of data enabled changed event 3437 * @param enabled True if data is enabled, otherwise disabled. 3438 * @param reason Reason for data enabled/disabled (see {@code REASON_*} in 3439 * {@link DataEnabledSettings}. 3440 */ 3441 private void onDataEnabledChanged(boolean enabled, int reason) { 3442 3443 log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason); 3444 3445 ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled); 3446 3447 mIsDataEnabled = enabled; 3448 3449 if (!mIsViLteDataMetered) { 3450 log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " - carrier policy " 3451 + "indicates that data is not metered for ViLTE calls."); 3452 return; 3453 } 3454 3455 // Inform connections that data has been disabled to ensure we turn off video capability 3456 // if this is an LTE call. 3457 for (ImsPhoneConnection conn : mConnections) { 3458 conn.handleDataEnabledChange(enabled); 3459 } 3460 3461 int reasonCode; 3462 if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) { 3463 reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED; 3464 } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) { 3465 reasonCode = ImsReasonInfo.CODE_DATA_DISABLED; 3466 } else { 3467 // Unexpected code, default to data disabled. 3468 reasonCode = ImsReasonInfo.CODE_DATA_DISABLED; 3469 } 3470 3471 // Potentially send connection events so the InCall UI knows that video calls are being 3472 // downgraded due to data being enabled/disabled. 3473 maybeNotifyDataDisabled(enabled, reasonCode); 3474 // Handle video state changes required as a result of data being enabled/disabled. 3475 handleDataEnabledChange(enabled, reasonCode); 3476 3477 // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before 3478 // the carrier config has loaded and will deregister IMS. 3479 if (!mShouldUpdateImsConfigOnDisconnect 3480 && reason != DataEnabledSettings.REASON_REGISTERED) { 3481 // This will call into updateVideoCallFeatureValue and eventually all clients will be 3482 // asynchronously notified that the availability of VT over LTE has changed. 3483 if (mImsManager != null) { 3484 mImsManager.updateImsServiceConfig(true); 3485 } 3486 } 3487 } 3488 3489 private void maybeNotifyDataDisabled(boolean enabled, int reasonCode) { 3490 if (!enabled) { 3491 // If data is disabled while there are ongoing VT calls which are not taking place over 3492 // wifi, then they should be disconnected to prevent the user from incurring further 3493 // data charges. 3494 for (ImsPhoneConnection conn : mConnections) { 3495 ImsCall imsCall = conn.getImsCall(); 3496 if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) { 3497 if (conn.hasCapabilities( 3498 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL | 3499 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) { 3500 3501 // If the carrier supports downgrading to voice, then we can simply issue a 3502 // downgrade to voice instead of terminating the call. 3503 if (reasonCode == ImsReasonInfo.CODE_DATA_DISABLED) { 3504 conn.onConnectionEvent(TelephonyManager.EVENT_DOWNGRADE_DATA_DISABLED, 3505 null); 3506 } else if (reasonCode == ImsReasonInfo.CODE_DATA_LIMIT_REACHED) { 3507 conn.onConnectionEvent( 3508 TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null); 3509 } 3510 } 3511 } 3512 } 3513 } 3514 } 3515 3516 /** 3517 * Handles changes to the enabled state of mobile data. 3518 * When data is disabled, handles auto-downgrade of video calls over LTE. 3519 * When data is enabled, handled resuming of video calls paused when data was disabled. 3520 * @param enabled {@code true} if mobile data is enabled, {@code false} if mobile data is 3521 * disabled. 3522 * @param reasonCode The {@link ImsReasonInfo} code for the data enabled state change. 3523 */ 3524 private void handleDataEnabledChange(boolean enabled, int reasonCode) { 3525 if (!enabled) { 3526 // If data is disabled while there are ongoing VT calls which are not taking place over 3527 // wifi, then they should be disconnected to prevent the user from incurring further 3528 // data charges. 3529 for (ImsPhoneConnection conn : mConnections) { 3530 ImsCall imsCall = conn.getImsCall(); 3531 if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) { 3532 log("handleDataEnabledChange - downgrading " + conn); 3533 downgradeVideoCall(reasonCode, conn); 3534 } 3535 } 3536 } else if (mSupportPauseVideo) { 3537 // Data was re-enabled, so un-pause previously paused video calls. 3538 for (ImsPhoneConnection conn : mConnections) { 3539 // If video is paused, check to see if there are any pending pauses due to enabled 3540 // state of data changing. 3541 log("handleDataEnabledChange - resuming " + conn); 3542 if (VideoProfile.isPaused(conn.getVideoState()) && 3543 conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) { 3544 // The data enabled state was a cause of a pending pause, so potentially 3545 // resume the video now. 3546 conn.resumeVideo(VideoPauseTracker.SOURCE_DATA_ENABLED); 3547 } 3548 } 3549 mShouldUpdateImsConfigOnDisconnect = false; 3550 } 3551 } 3552 3553 /** 3554 * Handles downgrading a video call. The behavior depends on carrier capabilities; we will 3555 * attempt to take one of the following actions (in order of precedence): 3556 * 1. If supported by the carrier, the call will be downgraded to an audio-only call. 3557 * 2. If the carrier supports video pause signalling, the video will be paused. 3558 * 3. The call will be disconnected. 3559 * @param reasonCode The {@link ImsReasonInfo} reason code for the downgrade. 3560 * @param conn The {@link ImsPhoneConnection} to downgrade. 3561 */ 3562 private void downgradeVideoCall(int reasonCode, ImsPhoneConnection conn) { 3563 ImsCall imsCall = conn.getImsCall(); 3564 if (imsCall != null) { 3565 if (conn.hasCapabilities( 3566 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL | 3567 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) { 3568 3569 // If the carrier supports downgrading to voice, then we can simply issue a 3570 // downgrade to voice instead of terminating the call. 3571 modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY); 3572 } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) { 3573 // The carrier supports video pause signalling, so pause the video if we didn't just 3574 // lose wifi; in that case just disconnect. 3575 mShouldUpdateImsConfigOnDisconnect = true; 3576 conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED); 3577 } else { 3578 // At this point the only choice we have is to terminate the call. 3579 try { 3580 imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode); 3581 } catch (ImsException ie) { 3582 loge("Couldn't terminate call " + imsCall); 3583 } 3584 } 3585 } 3586 } 3587 3588 private void resetImsCapabilities() { 3589 log("Resetting Capabilities..."); 3590 for (int i = 0; i < mImsFeatureEnabled.length; i++) { 3591 mImsFeatureEnabled[i] = false; 3592 } 3593 } 3594 3595 /** 3596 * @return {@code true} if the device is connected to a WIFI network, {@code false} otherwise. 3597 */ 3598 private boolean isWifiConnected() { 3599 ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() 3600 .getSystemService(Context.CONNECTIVITY_SERVICE); 3601 if (cm != null) { 3602 NetworkInfo ni = cm.getActiveNetworkInfo(); 3603 if (ni != null && ni.isConnected()) { 3604 return ni.getType() == ConnectivityManager.TYPE_WIFI; 3605 } 3606 } 3607 return false; 3608 } 3609 3610 /** 3611 * @return {@code true} if downgrading of a video call to audio is supported. 3612 */ 3613 public boolean isCarrierDowngradeOfVtCallSupported() { 3614 return mSupportDowngradeVtToAudio; 3615 } 3616 3617 private void handleFeatureCapabilityChanged(int serviceClass, 3618 int[] enabledFeatures, int[] disabledFeatures) { 3619 if (serviceClass == ImsServiceClass.MMTEL) { 3620 boolean tmpIsVideoCallEnabled = isVideoCallEnabled(); 3621 // Check enabledFeatures to determine capabilities. We ignore disabledFeatures. 3622 StringBuilder sb; 3623 if (DBG) { 3624 sb = new StringBuilder(120); 3625 sb.append("handleFeatureCapabilityChanged: "); 3626 } 3627 for (int i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE; 3628 i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI && 3629 i < enabledFeatures.length; i++) { 3630 if (enabledFeatures[i] == i) { 3631 // If the feature is set to its own integer value it is enabled. 3632 if (DBG) { 3633 sb.append(mImsFeatureStrings[i]); 3634 sb.append(":true "); 3635 } 3636 3637 mImsFeatureEnabled[i] = true; 3638 } else if (enabledFeatures[i] 3639 == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) { 3640 // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled. 3641 if (DBG) { 3642 sb.append(mImsFeatureStrings[i]); 3643 sb.append(":false "); 3644 } 3645 3646 mImsFeatureEnabled[i] = false; 3647 } else { 3648 // Feature has unknown state; it is not its own value or -1. 3649 if (DBG) { 3650 loge("handleFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i] 3651 + "): unexpectedValue=" + enabledFeatures[i]); 3652 } 3653 } 3654 } 3655 boolean isVideoEnabled = isVideoCallEnabled(); 3656 boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled; 3657 if (DBG) { 3658 sb.append(" isVideoEnabledStateChanged="); 3659 sb.append(isVideoEnabledStatechanged); 3660 } 3661 3662 if (isVideoEnabledStatechanged) { 3663 log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged=" + 3664 isVideoEnabled); 3665 mPhone.notifyForVideoCapabilityChanged(isVideoEnabled); 3666 } 3667 3668 if (DBG) { 3669 log(sb.toString()); 3670 } 3671 3672 if (DBG) log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled() 3673 + ", isVideoCallEnabled=" + isVideoCallEnabled() 3674 + ", isVowifiEnabled=" + isVowifiEnabled() 3675 + ", isUtEnabled=" + isUtEnabled()); 3676 3677 mPhone.onFeatureCapabilityChanged(); 3678 3679 mMetrics.writeOnImsCapabilities( 3680 mPhone.getPhoneId(), mImsFeatureEnabled); 3681 } 3682 } 3683 3684 @VisibleForTesting 3685 public void onCallHoldReceived(ImsCall imsCall) { 3686 if (DBG) log("onCallHoldReceived"); 3687 3688 ImsPhoneConnection conn = findConnection(imsCall); 3689 if (conn != null) { 3690 if (!mOnHoldToneStarted && (ImsPhoneCall.isLocalTone(imsCall) 3691 || mAlwaysPlayRemoteHoldTone) && 3692 conn.getState() == ImsPhoneCall.State.ACTIVE) { 3693 mPhone.startOnHoldTone(conn); 3694 mOnHoldToneStarted = true; 3695 mOnHoldToneId = System.identityHashCode(conn); 3696 } 3697 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null); 3698 3699 boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean( 3700 com.android.internal.R.bool.config_useVideoPauseWorkaround); 3701 if (useVideoPauseWorkaround && mSupportPauseVideo && 3702 VideoProfile.isVideo(conn.getVideoState())) { 3703 // If we are using the video pause workaround, the vendor IMS code has issues 3704 // with video pause signalling. In this case, when a call is remotely 3705 // held, the modem does not reliably change the video state of the call to be 3706 // paused. 3707 // As a workaround, we will turn on that bit now. 3708 conn.changeToPausedState(); 3709 } 3710 } 3711 3712 SuppServiceNotification supp = new SuppServiceNotification(); 3713 // Type of notification: 0 = MO; 1 = MT 3714 // Refer SuppServiceNotification class documentation. 3715 supp.notificationType = 1; 3716 supp.code = SuppServiceNotification.MT_CODE_CALL_ON_HOLD; 3717 mPhone.notifySuppSvcNotification(supp); 3718 mMetrics.writeOnImsCallHoldReceived(mPhone.getPhoneId(), imsCall.getCallSession()); 3719 } 3720 3721 @VisibleForTesting 3722 public void setAlwaysPlayRemoteHoldTone(boolean shouldPlayRemoteHoldTone) { 3723 mAlwaysPlayRemoteHoldTone = shouldPlayRemoteHoldTone; 3724 } 3725} 3726