aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Gunn <tgunn@google.com>2017-12-14 14:20:19 -0800
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-02-08 04:12:05 +0000
commit8d9cccc1c22f8404bf7780c1d965a8ba7afae1e2 (patch)
tree737615ab1b94828e539a9a7fbb0c75669229cd1c
parente7db19e88f3b1ff3f1d555dc8f7eeb0eb8c78155 (diff)
downloadtelephony-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.java190
-rw-r--r--tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java65
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() {