Merge "Fixed handover not working issue"
diff --git a/src/java/com/android/internal/telephony/TransportManager.java b/src/java/com/android/internal/telephony/TransportManager.java
index 9e1379b..aea1007 100644
--- a/src/java/com/android/internal/telephony/TransportManager.java
+++ b/src/java/com/android/internal/telephony/TransportManager.java
@@ -29,6 +29,7 @@
 import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.ApnType;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.dataconnection.AccessNetworksManager.QualifiedNetworks;
 import com.android.internal.util.ArrayUtils;
@@ -122,7 +123,11 @@
      */
     private final RegistrantList mHandoverNeededEventRegistrants;
 
-    static final class HandoverParams {
+    /**
+     * Handover parameters
+     */
+    @VisibleForTesting
+    public static final class HandoverParams {
         public final @ApnType int apnType;
         public final int targetTransport;
         HandoverParams(int apnType, int targetTransport) {
@@ -134,10 +139,6 @@
     public TransportManager(Phone phone) {
         mPhone = phone;
         mAccessNetworksManager = new AccessNetworksManager(phone);
-
-        mAccessNetworksManager.registerForQualifiedNetworksChanged(this,
-                EVENT_QUALIFIED_NETWORKS_CHANGED);
-
         mCurrentAvailableNetworks = new ConcurrentHashMap<>();
         mCurrentTransports = new ConcurrentHashMap<>();
         mHandoverNeededEventRegistrants = new RegistrantList();
@@ -147,6 +148,8 @@
             // the IWLAN ones.
             mAvailableTransports = new int[]{TransportType.WWAN};
         } else {
+            mAccessNetworksManager.registerForQualifiedNetworksChanged(this,
+                    EVENT_QUALIFIED_NETWORKS_CHANGED);
             mAvailableTransports = new int[]{TransportType.WWAN, TransportType.WLAN};
         }
     }
@@ -211,14 +214,17 @@
         log("updateAvailableNetworks: " + networksList);
         for (QualifiedNetworks networks : networksList) {
             if (areNetworksValid(networks)) {
-                mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks);
                 if (isHandoverNeeded(networks)) {
+                    mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks);
                     // If handover is needed, perform the handover works. For now we only pick the
                     // first element because it's the most preferred. In the future we should also
                     // consider the rest in the list, for example, the first one violates
                     // carrier/user policy.
                     int targetTransport = ACCESS_NETWORK_TRANSPORT_TYPE_MAP.get(
                             networks.qualifiedNetworks[0]);
+                    log("Handover needed for APN type: "
+                            + ApnSetting.getApnTypeString(networks.apnType) + ", target transport: "
+                            + TransportType.toString(targetTransport));
                     mHandoverNeededEventRegistrants.notifyResult(
                             new HandoverParams(networks.apnType, targetTransport));
 
@@ -227,6 +233,7 @@
                     // transport.
                     log("Handover not needed for APN type: "
                             + ApnSetting.getApnTypeString(networks.apnType));
+                    mCurrentAvailableNetworks.put(networks.apnType, networks.qualifiedNetworks);
                     int transport = TransportType.WWAN;
                     if (!ArrayUtils.isEmpty(networks.qualifiedNetworks)
                             && ACCESS_NETWORK_TRANSPORT_TYPE_MAP.containsKey(
diff --git a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
index 2e95967..96dabd4 100644
--- a/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/AccessNetworksManager.java
@@ -16,16 +16,17 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.PersistableBundle;
+import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -191,8 +192,7 @@
             }
 
             if (!qualifiedNetworksList.isEmpty()) {
-                mQualifiedNetworksChangedRegistrants.notifyRegistrants(
-                        new AsyncResult(null, qualifiedNetworksList, null));
+                mQualifiedNetworksChangedRegistrants.notifyResult(qualifiedNetworksList);
             }
         }
     }
@@ -290,6 +290,17 @@
         return packageName;
     }
 
+
+    private @NonNull List<QualifiedNetworks> getQualifiedNetworksList() {
+        List<QualifiedNetworks> qualifiedNetworksList = new ArrayList<>();
+        for (int i = 0; i < mAvailableNetworks.size(); i++) {
+            qualifiedNetworksList.add(new QualifiedNetworks(mAvailableNetworks.keyAt(i),
+                    mAvailableNetworks.valueAt(i)));
+        }
+
+        return qualifiedNetworksList;
+    }
+
     /**
      * Register for qualified networks changed event.
      *
@@ -298,7 +309,14 @@
      */
     public void registerForQualifiedNetworksChanged(Handler h, int what) {
         if (h != null) {
-            mQualifiedNetworksChangedRegistrants.addUnique(h, what, null);
+            Registrant r = new Registrant(h, what, null);
+            mQualifiedNetworksChangedRegistrants.add(r);
+
+            // Notify for the first time if there is already something in the available network
+            // list.
+            if (mAvailableNetworks.size() != 0) {
+                r.notifyResult(getQualifiedNetworksList());
+            }
         }
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
new file mode 100644
index 0000000..8a30cde
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TransportManagerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.data.ApnSetting;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.dataconnection.AccessNetworksManager.QualifiedNetworks;
+import com.android.internal.telephony.dataconnection.TransportManager.HandoverParams;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class TransportManagerTest extends TelephonyTest {
+    private static final int EVENT_HANDOVER_NEEDED = 1;
+
+    @Mock
+    private Handler mTestHandler;
+
+    private TransportManager mTransportManager;
+
+    private TransportManagerTestHandler mTransportManagerTestHandler;
+
+    private class TransportManagerTestHandler extends HandlerThread {
+
+        private TransportManagerTestHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mTransportManager = new TransportManager(mPhone);
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mTransportManagerTestHandler = new TransportManagerTestHandler(TAG);
+        mTransportManagerTestHandler.start();
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTransportManagerTestHandler.quit();
+        mTransportManagerTestHandler.join();
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverNeeded() throws Exception {
+        mTransportManager.registerForHandoverNeededEvent(mTestHandler, EVENT_HANDOVER_NEEDED);
+
+        // Initial qualified networks
+        List<QualifiedNetworks> networkList = new ArrayList<>(Arrays.asList(
+                new QualifiedNetworks(ApnSetting.TYPE_IMS,
+                        new int[]{AccessNetworkType.EUTRAN, AccessNetworkType.UTRAN,
+                                AccessNetworkType.IWLAN})));
+        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
+                new AsyncResult(null, networkList, null)).sendToTarget();
+        waitForMs(100);
+        // Verify handover needed event was not sent
+        verify(mTestHandler, never()).sendMessageAtTime(any(Message.class), anyLong());
+
+        // Now change the order of qualified networks by putting IWLAN first
+        networkList = new ArrayList<>(Arrays.asList(
+                new QualifiedNetworks(ApnSetting.TYPE_IMS,
+                        new int[]{AccessNetworkType.IWLAN, AccessNetworkType.UTRAN,
+                                AccessNetworkType.EUTRAN})));
+        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
+                new AsyncResult(null, networkList, null)).sendToTarget();
+        waitForMs(100);
+
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+
+        // Verify handover needed event was sent and the the target transport is WLAN.
+        verify(mTestHandler, times(1)).sendMessageAtTime(messageArgumentCaptor.capture(),
+                anyLong());
+        Message message = messageArgumentCaptor.getValue();
+        assertEquals(EVENT_HANDOVER_NEEDED, message.what);
+        AsyncResult ar = (AsyncResult) message.obj;
+        HandoverParams params = (HandoverParams) ar.result;
+        assertEquals(ApnSetting.TYPE_IMS, params.apnType);
+        assertEquals(TransportType.WLAN, params.targetTransport);
+
+        // Now change the order of qualified networks by putting UTRAN first
+        networkList = new ArrayList<>(Arrays.asList(
+                new QualifiedNetworks(ApnSetting.TYPE_IMS,
+                        new int[]{AccessNetworkType.UTRAN, AccessNetworkType.EUTRAN,
+                                AccessNetworkType.IWLAN})));
+        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
+                new AsyncResult(null, networkList, null)).sendToTarget();
+        waitForMs(100);
+
+        messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+
+        // Verify handover needed event was sent and the the target transport is WWAN.
+        verify(mTestHandler, times(2)).sendMessageAtTime(messageArgumentCaptor.capture(),
+                anyLong());
+        message = messageArgumentCaptor.getValue();
+        assertEquals(EVENT_HANDOVER_NEEDED, message.what);
+        ar = (AsyncResult) message.obj;
+        params = (HandoverParams) ar.result;
+        assertEquals(ApnSetting.TYPE_IMS, params.apnType);
+        assertEquals(TransportType.WWAN, params.targetTransport);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverNotNeeded() throws Exception {
+        mTransportManager.registerForHandoverNeededEvent(mTestHandler, EVENT_HANDOVER_NEEDED);
+
+        // Initial qualified networks
+        List<QualifiedNetworks> networkList = new ArrayList<>(Arrays.asList(
+                new QualifiedNetworks(ApnSetting.TYPE_IMS,
+                        new int[]{AccessNetworkType.EUTRAN, AccessNetworkType.UTRAN,
+                                AccessNetworkType.IWLAN})));
+        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
+                new AsyncResult(null, networkList, null)).sendToTarget();
+        waitForMs(100);
+        // Verify handover needed event was not sent
+        verify(mTestHandler, never()).sendMessageAtTime(any(Message.class), anyLong());
+
+        // Now change the order of qualified networks by swapping EUTRAN and UTRAN.
+        networkList = new ArrayList<>(Arrays.asList(
+                new QualifiedNetworks(ApnSetting.TYPE_IMS,
+                        new int[]{AccessNetworkType.UTRAN, AccessNetworkType.EUTRAN,
+                                AccessNetworkType.IWLAN})));
+        mTransportManager.obtainMessage(1 /* EVENT_QUALIFIED_NETWORKS_CHANGED */,
+                new AsyncResult(null, networkList, null)).sendToTarget();
+        waitForMs(100);
+        // Verify handover needed event was not sent
+        verify(mTestHandler, never()).sendMessageAtTime(any(Message.class), anyLong());
+    }
+}