diff options
author | Tyler Gunn <tgunn@google.com> | 2017-12-14 14:20:19 -0800 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-02-08 04:12:05 +0000 |
commit | 8d9cccc1c22f8404bf7780c1d965a8ba7afae1e2 (patch) | |
tree | 737615ab1b94828e539a9a7fbb0c75669229cd1c | |
parent | e7db19e88f3b1ff3f1d555dc8f7eeb0eb8c78155 (diff) | |
download | telephony-8d9cccc1c22f8404bf7780c1d965a8ba7afae1e2.tar.gz |
Add support for notification of midcall video call radio handovers.
Adding support for video calls to:
- Notification of successful LTE to WIFI handover
- Notification of failure to handover from LTE to WIFI.
If there is a handover from WIFI to LTE (or the initial handover from
lte to WIFI fails at the start of a call), we enable a
connectivity listener to track when a new WIFI network becomes available.
If a new wifi network becomes available and there is no handover to WIFI
before a 1 min time expires, we warn the user (existing connection event)
that we couldn't handover to wifi.
If the handover to WIFI is successful, we send a connection event which
dialer will use to show a toast for the handover.
Test: Manual, added unit tests.
Bug: 65490850
Change-Id: I7fa16003e7309d40df0654c2992c823ed4d12e28
(cherry picked from commit 02761d2406c0197cebd255b575f161f5145efbb3)
-rw-r--r-- | src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java | 190 | ||||
-rw-r--r-- | tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java | 65 |
2 files changed, 239 insertions, 16 deletions
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java index a5020ab97f..bdb1e11863 100644 --- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java @@ -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; @@ -227,6 +230,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; @@ -580,6 +601,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. * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information. @@ -986,6 +1023,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 = @@ -1003,6 +1050,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( @@ -2016,13 +2065,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, DisconnectCause.NOT_DISCONNECTED); - if (mNotifyVtHandoverToWifiFail && - !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) { - // Schedule check to see if handover succeeded. - sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall), - HANDOVER_TO_WIFI_TIMEOUT_MS); + 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()); } @@ -2538,18 +2592,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); - } + // 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) { - // 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; + 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) { @@ -2575,6 +2646,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); @@ -2600,11 +2674,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 @@ -2682,6 +2765,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; @@ -2950,12 +3035,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; @@ -3567,12 +3664,75 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { } /** + * 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. */ public boolean isCarrierDowngradeOfVtCallSupported() { return mSupportDowngradeVtToAudio; } + @VisibleForTesting + public void setDataEnabled(boolean isDataEnabled) { + mIsDataEnabled = isDataEnabled; + } + private void handleFeatureCapabilityChanged(int serviceClass, int[] enabledFeatures, int[] disabledFeatures) { if (serviceClass == ImsServiceClass.MMTEL) { diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java index 989cd64c32..71f4cc4f2e 100644 --- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java +++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java @@ -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; @@ -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; @@ -88,6 +93,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 { @@ -103,6 +110,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); } @@ -156,7 +164,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest { } }).when(mImsCall).hold(); - doReturn(mImsCallSession).when(mImsCall).getCallSession(); + mImsCall.attachSession(mImsCallSession); } @Before @@ -167,6 +175,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(); @@ -189,6 +198,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()); @@ -198,6 +208,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(), @@ -262,6 +273,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 @@ -570,6 +584,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() { |