Loading src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +175 −15 Original line number Diff line number Diff line Loading @@ -24,7 +24,10 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkStats; import android.net.Uri; import android.os.AsyncResult; Loading Loading @@ -229,6 +232,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } }; /** * Tracks whether we are currently monitoring network connectivity for the purpose of warning * the user of an inability to handover from LTE to WIFI for video calls. */ private boolean mIsMonitoringConnectivity = false; /** * Network callback used to schedule the handover check when a wireless network connects. */ private ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { Rlog.i(LOG_TAG, "Network available: " + network); scheduleHandoverCheck(); } }; //***** Constants static final int MAX_CONNECTIONS = 7; Loading Loading @@ -584,6 +605,22 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { */ private boolean mNotifyHandoverVideoFromWifiToLTE = false; /** * Carrier configuration option which determines whether the carrier wants to inform the user * when a video call is handed over from LTE to WIFI. * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL} for more * information. */ private boolean mNotifyHandoverVideoFromLTEToWifi = false; /** * When {@code} false, indicates that no handover from LTE to WIFI has occurred during the start * of the call. * When {@code true}, indicates that the start of call handover from LTE to WIFI has been * attempted (it may have suceeded or failed). */ private boolean mHasPerformedStartOfCallHandover = false; /** * Carrier configuration option which determines whether the carrier supports the * {@link VideoProfile#STATE_PAUSED} signalling. Loading Loading @@ -1007,6 +1044,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { return; } updateCarrierConfigCache(carrierConfig); } /** * Updates the local carrier config cache from a bundle obtained from the carrier config * manager. Also supports unit testing by injecting configuration at test time. * @param carrierConfig The config bundle. */ @VisibleForTesting public void updateCarrierConfigCache(PersistableBundle carrierConfig) { mAllowEmergencyVideoCalls = carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL); mTreatDowngradedVideoCallsAsVideoCalls = Loading @@ -1024,6 +1071,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL); mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean( CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL); mNotifyHandoverVideoFromLTEToWifi = carrierConfig.getBoolean( CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL); mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean( CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS); mIsViLteDataMetered = carrierConfig.getBoolean( Loading Loading @@ -2073,13 +2122,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, DisconnectCause.NOT_DISCONNECTED); if (mNotifyVtHandoverToWifiFail && !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) { if (mNotifyVtHandoverToWifiFail && imsCall.isVideoCall() && !imsCall.isWifiCall()) { if (isWifiConnected()) { // Schedule check to see if handover succeeded. sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall), HANDOVER_TO_WIFI_TIMEOUT_MS); } else { // No wifi connectivity, so keep track of network availability for potential // handover. registerForConnectivityChanges(); } } mHasPerformedStartOfCallHandover = false; mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession()); } Loading Loading @@ -2564,18 +2618,35 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; if (isHandoverToWifi) { // If we handed over to wifi successfully, don't check for failure in the future. removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); } ImsPhoneConnection conn = findConnection(imsCall); if (conn != null) { // Only consider it a handover from WIFI if the source and target radio tech is known. boolean isHandoverFromWifi = srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; ImsPhoneConnection conn = findConnection(imsCall); if (conn != null) { if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { if (isHandoverToWifi) { removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); if (mNotifyHandoverVideoFromLTEToWifi && mHasPerformedStartOfCallHandover) { // This is a handover which happened mid-call (ie not the start of call // handover from LTE to WIFI), so we'll notify the InCall UI. conn.onConnectionEvent( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI, null); } // We are on WIFI now so no need to get notified of network availability. unregisterForConnectivityChanges(); } else if (isHandoverFromWifi && imsCall.isVideoCall()) { // A video call just dropped from WIFI to LTE; we want to be informed if a // new WIFI // network comes into range. registerForConnectivityChanges(); } } if (isHandoverFromWifi && imsCall.isVideoCall()) { if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) { if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { Loading @@ -2601,6 +2672,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { loge("onCallHandover :: connection null."); } if (!mHasPerformedStartOfCallHandover) { mHasPerformedStartOfCallHandover = true; } mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(), TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(), srcAccessTech, targetAccessTech, reasonInfo); Loading @@ -2626,11 +2700,20 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { // If we know we failed to handover, don't check for failure in the future. removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); if (imsCall.isVideoCall() && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { // Start listening for a WIFI network to come into range for potential handover. registerForConnectivityChanges(); } if (mNotifyVtHandoverToWifiFail) { // Only notify others if carrier config indicates to do so. conn.onHandoverToWifiFailed(); } } if (!mHasPerformedStartOfCallHandover) { mHasPerformedStartOfCallHandover = true; } } @Override Loading Loading @@ -2708,6 +2791,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); mHasPerformedStartOfCallHandover = false; unregisterForConnectivityChanges(); if (imsCall == mUssdSession) { mUssdSession = null; Loading Loading @@ -2976,12 +3061,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { case EVENT_CHECK_FOR_WIFI_HANDOVER: if (msg.obj instanceof ImsCall) { ImsCall imsCall = (ImsCall) msg.obj; if (imsCall != mForegroundCall.getImsCall()) { Rlog.i(LOG_TAG, "handoverCheck: no longer FG; check skipped."); unregisterForConnectivityChanges(); // Handover check and its not the foreground call any more. return; } if (!imsCall.isWifiCall()) { // Call did not handover to wifi, notify of handover failure. ImsPhoneConnection conn = findConnection(imsCall); if (conn != null) { Rlog.i(LOG_TAG, "handoverCheck: handover failed."); conn.onHandoverToWifiFailed(); } if (imsCall.isVideoCall() && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { registerForConnectivityChanges(); } } } break; Loading Loading @@ -3603,6 +3700,64 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { return false; } /** * Registers for changes to network connectivity. Specifically requests the availability of new * WIFI networks which an IMS video call could potentially hand over to. */ private void registerForConnectivityChanges() { if (mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) { return; } ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() .getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null) { Rlog.i(LOG_TAG, "registerForConnectivityChanges"); NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); NetworkRequest.Builder builder = new NetworkRequest.Builder(); builder.setCapabilities(capabilities); cm.registerNetworkCallback(builder.build(), mNetworkCallback); mIsMonitoringConnectivity = true; } } /** * Unregister for connectivity changes. Will be called when a call disconnects or if the call * ends up handing over to WIFI. */ private void unregisterForConnectivityChanges() { if (!mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) { return; } ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() .getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null) { Rlog.i(LOG_TAG, "unregisterForConnectivityChanges"); cm.unregisterNetworkCallback(mNetworkCallback); mIsMonitoringConnectivity = false; } } /** * If the foreground call is a video call, schedule a handover check if one is not already * scheduled. This method is intended ONLY for use when scheduling to watch for mid-call * handovers. */ private void scheduleHandoverCheck() { ImsCall fgCall = mForegroundCall.getImsCall(); ImsPhoneConnection conn = mForegroundCall.getFirstConnection(); if (!mNotifyVtHandoverToWifiFail || fgCall == null || !fgCall.isVideoCall() || conn == null || conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) { return; } if (!hasMessages(EVENT_CHECK_FOR_WIFI_HANDOVER)) { Rlog.i(LOG_TAG, "scheduleHandoverCheck: schedule"); sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, fgCall), HANDOVER_TO_WIFI_TIMEOUT_MS); } } /** * @return {@code true} if downgrading of a video call to audio is supported. */ Loading @@ -3610,6 +3765,11 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { return mSupportDowngradeVtToAudio; } @VisibleForTesting public void setDataEnabled(boolean isDataEnabled) { mIsDataEnabled = isDataEnabled; } private void handleFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, int[] disabledFeatures) { if (serviceClass == ImsServiceClass.MMTEL) { Loading tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +64 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; Loading @@ -42,10 +43,14 @@ import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.PersistableBundle; import android.support.test.filters.FlakyTest; import android.telecom.VideoProfile; import android.telephony.CarrierConfigManager; import android.telephony.DisconnectCause; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.ims.feature.ImsFeature; import android.test.suitebuilder.annotation.SmallTest; Loading Loading @@ -89,6 +94,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { private ImsCallSession mImsCallSession; @Mock private SharedPreferences mSharedPreferences; @Mock private ImsPhoneConnection.Listener mImsPhoneConnectionListener; private Handler mCTHander; private class ImsCTHandlerThread extends HandlerThread { Loading @@ -104,6 +111,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.", ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTUT.setDataEnabled(true); mCTHander = new Handler(mCTUT.getLooper()); setReady(true); } Loading Loading @@ -157,7 +165,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { } }).when(mImsCall).hold(); doReturn(mImsCallSession).when(mImsCall).getCallSession(); mImsCall.attachSession(mImsCallSession); } @Before Loading @@ -168,6 +176,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager); mImsCall = spy(new ImsCall(mContext, mImsCallProfile)); mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile)); mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class); imsCallMocking(mImsCall); imsCallMocking(mSecondImsCall); doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus(); Loading @@ -190,6 +199,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { public ImsCall answer(InvocationOnMock invocation) throws Throwable { mImsCallListener = (ImsCall.Listener) invocation.getArguments()[2]; mImsCall.setListener(mImsCallListener); return mImsCall; } }).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(), (ImsCall.Listener) any()); Loading @@ -199,6 +209,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { public ImsCall answer(InvocationOnMock invocation) throws Throwable { mImsCallListener = (ImsCall.Listener) invocation.getArguments()[3]; mSecondImsCall.setListener(mImsCallListener); return mSecondImsCall; } }).when(mImsManager).makeCall(eq(mServiceId), eq(mImsCallProfile), (String []) any(), Loading Loading @@ -263,6 +274,9 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { assertEquals(PhoneConstants.State.RINGING, mCTUT.getState()); assertTrue(mCTUT.mRingingCall.isRinging()); assertEquals(1, mCTUT.mRingingCall.getConnections().size()); ImsPhoneConnection connection = (ImsPhoneConnection) mCTUT.mRingingCall.getConnections().get(0); connection.addListener(mImsPhoneConnectionListener); } @Test Loading Loading @@ -571,6 +585,55 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { nullable(ImsConnectionStateListener.class)); } /** * Test notification of handover from LTE to WIFI and WIFI to LTE and ensure that the expected * connection events are sent. */ @Test @SmallTest public void testNotifyHandovers() { setupCarrierConfig(); //establish a MT call testImsMTCallAccept(); ImsPhoneConnection connection = (ImsPhoneConnection) mCTUT.mForegroundCall.getConnections().get(0); ImsCall call = connection.getImsCall(); // Needs to be a video call to see this signalling. doReturn(true).when(mImsCallProfile).isVideoCall(); // First handover from LTE to WIFI; this takes us into a mid-call state. call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, new ImsReasonInfo()); // Handover back to LTE. call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.RIL_RADIO_TECHNOLOGY_LTE, new ImsReasonInfo()); verify(mImsPhoneConnectionListener).onConnectionEvent(eq( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE), isNull()); // Finally hand back to WIFI call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, new ImsReasonInfo()); verify(mImsPhoneConnectionListener).onConnectionEvent(eq( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI), isNull()); } /** * Configure carrier config options relevant to the unit test. */ public void setupCarrierConfig() { PersistableBundle bundle = new PersistableBundle(); bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, true); bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, true); bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, true); mCTUT.updateCarrierConfigCache(bundle); } @Test @SmallTest public void testLowBatteryDisconnectMidCall() { Loading Loading
src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +175 −15 Original line number Diff line number Diff line Loading @@ -24,7 +24,10 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkRequest; import android.net.NetworkStats; import android.net.Uri; import android.os.AsyncResult; Loading Loading @@ -229,6 +232,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } }; /** * Tracks whether we are currently monitoring network connectivity for the purpose of warning * the user of an inability to handover from LTE to WIFI for video calls. */ private boolean mIsMonitoringConnectivity = false; /** * Network callback used to schedule the handover check when a wireless network connects. */ private ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { Rlog.i(LOG_TAG, "Network available: " + network); scheduleHandoverCheck(); } }; //***** Constants static final int MAX_CONNECTIONS = 7; Loading Loading @@ -584,6 +605,22 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { */ private boolean mNotifyHandoverVideoFromWifiToLTE = false; /** * Carrier configuration option which determines whether the carrier wants to inform the user * when a video call is handed over from LTE to WIFI. * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL} for more * information. */ private boolean mNotifyHandoverVideoFromLTEToWifi = false; /** * When {@code} false, indicates that no handover from LTE to WIFI has occurred during the start * of the call. * When {@code true}, indicates that the start of call handover from LTE to WIFI has been * attempted (it may have suceeded or failed). */ private boolean mHasPerformedStartOfCallHandover = false; /** * Carrier configuration option which determines whether the carrier supports the * {@link VideoProfile#STATE_PAUSED} signalling. Loading Loading @@ -1007,6 +1044,16 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { return; } updateCarrierConfigCache(carrierConfig); } /** * Updates the local carrier config cache from a bundle obtained from the carrier config * manager. Also supports unit testing by injecting configuration at test time. * @param carrierConfig The config bundle. */ @VisibleForTesting public void updateCarrierConfigCache(PersistableBundle carrierConfig) { mAllowEmergencyVideoCalls = carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL); mTreatDowngradedVideoCallsAsVideoCalls = Loading @@ -1024,6 +1071,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL); mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean( CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL); mNotifyHandoverVideoFromLTEToWifi = carrierConfig.getBoolean( CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL); mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean( CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS); mIsViLteDataMetered = carrierConfig.getBoolean( Loading Loading @@ -2073,13 +2122,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, DisconnectCause.NOT_DISCONNECTED); if (mNotifyVtHandoverToWifiFail && !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) { if (mNotifyVtHandoverToWifiFail && imsCall.isVideoCall() && !imsCall.isWifiCall()) { if (isWifiConnected()) { // Schedule check to see if handover succeeded. sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall), HANDOVER_TO_WIFI_TIMEOUT_MS); } else { // No wifi connectivity, so keep track of network availability for potential // handover. registerForConnectivityChanges(); } } mHasPerformedStartOfCallHandover = false; mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession()); } Loading Loading @@ -2564,18 +2618,35 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; if (isHandoverToWifi) { // If we handed over to wifi successfully, don't check for failure in the future. removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); } ImsPhoneConnection conn = findConnection(imsCall); if (conn != null) { // Only consider it a handover from WIFI if the source and target radio tech is known. boolean isHandoverFromWifi = srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; ImsPhoneConnection conn = findConnection(imsCall); if (conn != null) { if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { if (isHandoverToWifi) { removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); if (mNotifyHandoverVideoFromLTEToWifi && mHasPerformedStartOfCallHandover) { // This is a handover which happened mid-call (ie not the start of call // handover from LTE to WIFI), so we'll notify the InCall UI. conn.onConnectionEvent( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI, null); } // We are on WIFI now so no need to get notified of network availability. unregisterForConnectivityChanges(); } else if (isHandoverFromWifi && imsCall.isVideoCall()) { // A video call just dropped from WIFI to LTE; we want to be informed if a // new WIFI // network comes into range. registerForConnectivityChanges(); } } if (isHandoverFromWifi && imsCall.isVideoCall()) { if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) { if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { Loading @@ -2601,6 +2672,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { loge("onCallHandover :: connection null."); } if (!mHasPerformedStartOfCallHandover) { mHasPerformedStartOfCallHandover = true; } mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(), TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(), srcAccessTech, targetAccessTech, reasonInfo); Loading @@ -2626,11 +2700,20 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { // If we know we failed to handover, don't check for failure in the future. removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); if (imsCall.isVideoCall() && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { // Start listening for a WIFI network to come into range for potential handover. registerForConnectivityChanges(); } if (mNotifyVtHandoverToWifiFail) { // Only notify others if carrier config indicates to do so. conn.onHandoverToWifiFailed(); } } if (!mHasPerformedStartOfCallHandover) { mHasPerformedStartOfCallHandover = true; } } @Override Loading Loading @@ -2708,6 +2791,8 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); mHasPerformedStartOfCallHandover = false; unregisterForConnectivityChanges(); if (imsCall == mUssdSession) { mUssdSession = null; Loading Loading @@ -2976,12 +3061,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { case EVENT_CHECK_FOR_WIFI_HANDOVER: if (msg.obj instanceof ImsCall) { ImsCall imsCall = (ImsCall) msg.obj; if (imsCall != mForegroundCall.getImsCall()) { Rlog.i(LOG_TAG, "handoverCheck: no longer FG; check skipped."); unregisterForConnectivityChanges(); // Handover check and its not the foreground call any more. return; } if (!imsCall.isWifiCall()) { // Call did not handover to wifi, notify of handover failure. ImsPhoneConnection conn = findConnection(imsCall); if (conn != null) { Rlog.i(LOG_TAG, "handoverCheck: handover failed."); conn.onHandoverToWifiFailed(); } if (imsCall.isVideoCall() && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { registerForConnectivityChanges(); } } } break; Loading Loading @@ -3603,6 +3700,64 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { return false; } /** * Registers for changes to network connectivity. Specifically requests the availability of new * WIFI networks which an IMS video call could potentially hand over to. */ private void registerForConnectivityChanges() { if (mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) { return; } ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() .getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null) { Rlog.i(LOG_TAG, "registerForConnectivityChanges"); NetworkCapabilities capabilities = new NetworkCapabilities(); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); NetworkRequest.Builder builder = new NetworkRequest.Builder(); builder.setCapabilities(capabilities); cm.registerNetworkCallback(builder.build(), mNetworkCallback); mIsMonitoringConnectivity = true; } } /** * Unregister for connectivity changes. Will be called when a call disconnects or if the call * ends up handing over to WIFI. */ private void unregisterForConnectivityChanges() { if (!mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) { return; } ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() .getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null) { Rlog.i(LOG_TAG, "unregisterForConnectivityChanges"); cm.unregisterNetworkCallback(mNetworkCallback); mIsMonitoringConnectivity = false; } } /** * If the foreground call is a video call, schedule a handover check if one is not already * scheduled. This method is intended ONLY for use when scheduling to watch for mid-call * handovers. */ private void scheduleHandoverCheck() { ImsCall fgCall = mForegroundCall.getImsCall(); ImsPhoneConnection conn = mForegroundCall.getFirstConnection(); if (!mNotifyVtHandoverToWifiFail || fgCall == null || !fgCall.isVideoCall() || conn == null || conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) { return; } if (!hasMessages(EVENT_CHECK_FOR_WIFI_HANDOVER)) { Rlog.i(LOG_TAG, "scheduleHandoverCheck: schedule"); sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, fgCall), HANDOVER_TO_WIFI_TIMEOUT_MS); } } /** * @return {@code true} if downgrading of a video call to audio is supported. */ Loading @@ -3610,6 +3765,11 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { return mSupportDowngradeVtToAudio; } @VisibleForTesting public void setDataEnabled(boolean isDataEnabled) { mIsDataEnabled = isDataEnabled; } private void handleFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, int[] disabledFeatures) { if (serviceClass == ImsServiceClass.MMTEL) { Loading
tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +64 −1 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; Loading @@ -42,10 +43,14 @@ import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.PersistableBundle; import android.support.test.filters.FlakyTest; import android.telecom.VideoProfile; import android.telephony.CarrierConfigManager; import android.telephony.DisconnectCause; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.telephony.ims.feature.ImsFeature; import android.test.suitebuilder.annotation.SmallTest; Loading Loading @@ -89,6 +94,8 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { private ImsCallSession mImsCallSession; @Mock private SharedPreferences mSharedPreferences; @Mock private ImsPhoneConnection.Listener mImsPhoneConnectionListener; private Handler mCTHander; private class ImsCTHandlerThread extends HandlerThread { Loading @@ -104,6 +111,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.", ImsReasonInfo.CODE_ANSWERED_ELSEWHERE); mCTUT.setDataEnabled(true); mCTHander = new Handler(mCTUT.getLooper()); setReady(true); } Loading Loading @@ -157,7 +165,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { } }).when(mImsCall).hold(); doReturn(mImsCallSession).when(mImsCall).getCallSession(); mImsCall.attachSession(mImsCallSession); } @Before Loading @@ -168,6 +176,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager); mImsCall = spy(new ImsCall(mContext, mImsCallProfile)); mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile)); mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class); imsCallMocking(mImsCall); imsCallMocking(mSecondImsCall); doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus(); Loading @@ -190,6 +199,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { public ImsCall answer(InvocationOnMock invocation) throws Throwable { mImsCallListener = (ImsCall.Listener) invocation.getArguments()[2]; mImsCall.setListener(mImsCallListener); return mImsCall; } }).when(mImsManager).takeCall(eq(mServiceId), (Intent) any(), (ImsCall.Listener) any()); Loading @@ -199,6 +209,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { public ImsCall answer(InvocationOnMock invocation) throws Throwable { mImsCallListener = (ImsCall.Listener) invocation.getArguments()[3]; mSecondImsCall.setListener(mImsCallListener); return mSecondImsCall; } }).when(mImsManager).makeCall(eq(mServiceId), eq(mImsCallProfile), (String []) any(), Loading Loading @@ -263,6 +274,9 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { assertEquals(PhoneConstants.State.RINGING, mCTUT.getState()); assertTrue(mCTUT.mRingingCall.isRinging()); assertEquals(1, mCTUT.mRingingCall.getConnections().size()); ImsPhoneConnection connection = (ImsPhoneConnection) mCTUT.mRingingCall.getConnections().get(0); connection.addListener(mImsPhoneConnectionListener); } @Test Loading Loading @@ -571,6 +585,55 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { nullable(ImsConnectionStateListener.class)); } /** * Test notification of handover from LTE to WIFI and WIFI to LTE and ensure that the expected * connection events are sent. */ @Test @SmallTest public void testNotifyHandovers() { setupCarrierConfig(); //establish a MT call testImsMTCallAccept(); ImsPhoneConnection connection = (ImsPhoneConnection) mCTUT.mForegroundCall.getConnections().get(0); ImsCall call = connection.getImsCall(); // Needs to be a video call to see this signalling. doReturn(true).when(mImsCallProfile).isVideoCall(); // First handover from LTE to WIFI; this takes us into a mid-call state. call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, new ImsReasonInfo()); // Handover back to LTE. call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.RIL_RADIO_TECHNOLOGY_LTE, new ImsReasonInfo()); verify(mImsPhoneConnectionListener).onConnectionEvent(eq( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE), isNull()); // Finally hand back to WIFI call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(), ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, new ImsReasonInfo()); verify(mImsPhoneConnectionListener).onConnectionEvent(eq( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI), isNull()); } /** * Configure carrier config options relevant to the unit test. */ public void setupCarrierConfig() { PersistableBundle bundle = new PersistableBundle(); bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, true); bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, true); bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, true); mCTUT.updateCarrierConfigCache(bundle); } @Test @SmallTest public void testLowBatteryDisconnectMidCall() { Loading