Merge "List baseline_filename on modules that are implititly using it" into main am: be44be6391 am: b4ac4187d8

Original change: https://android-review.googlesource.com/c/platform/packages/services/Telephony/+/2886451

Change-Id: I22ccbdf39d56575db497584113d6c46aaad4b7d4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 88fdf3a..fbad8e9 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -50,6 +50,7 @@
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyLocalConnection;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
@@ -576,6 +577,13 @@
             intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
             intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
             registerReceiver(mReceiver, intentFilter);
+            int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+            if (SubscriptionManager.isValidSubscriptionId(defaultDataSubId)) {
+                if (VDBG) Log.v(LOG_TAG, "Loaded initial default data sub: " + defaultDataSubId);
+                mDefaultDataSubId = defaultDataSubId;
+                registerSettingsObserver();
+                updateDataRoamingStatus();
+            }
 
             PhoneConfigurationManager.registerForMultiSimConfigChange(
                     mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
@@ -962,10 +970,10 @@
             msg.arg1 = mDefaultDataSubId;
             msg.sendToTarget();
         } else if (dataAllowed && dataIsNowRoaming(mDefaultDataSubId)) {
-            boolean isShowRoamingNotificationEnabled = getCarrierConfigForSubId(mDefaultDataSubId)
-                    .getBoolean(CarrierConfigManager
-                            .KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
-            if (!isShowRoamingNotificationEnabled) return;
+            if (!shouldShowRoamingNotification(roamingOperatorNumeric)) {
+                Log.d(LOG_TAG, "Skip showing roaming connected notification.");
+                return;
+            }
             // Don't show roaming notification if we've already shown for this MccMnc
             if (roamingOperatorNumeric != null
                     && !mPrevRoamingOperatorNumerics.add(roamingOperatorNumeric)) {
@@ -1002,6 +1010,45 @@
         return getPhone(subId).getServiceState().getDataRoaming();
     }
 
+    private boolean shouldShowRoamingNotification(String roamingNumeric) {
+        PersistableBundle config = getCarrierConfigForSubId(mDefaultDataSubId);
+        boolean showRoamingNotification = config.getBoolean(
+                CarrierConfigManager.KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
+
+        if (TextUtils.isEmpty(roamingNumeric)) {
+            Log.d(LOG_TAG, "shouldShowRoamingNotification: roamingNumeric=" + roamingNumeric);
+            return showRoamingNotification;
+        }
+
+        String[] includedMccMncs = config.getStringArray(CarrierConfigManager
+                .KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY);
+        if (includedMccMncs != null) {
+            for (String mccMnc : includedMccMncs) {
+                if (roamingNumeric.equals(mccMnc)) {
+                    Log.d(LOG_TAG, "shouldShowRoamingNotification: show for MCC/MNC " + mccMnc);
+                    return showRoamingNotification;
+                }
+            }
+        }
+
+        String[] excludedMccs = config.getStringArray(CarrierConfigManager
+                .KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY);
+        String roamingMcc = roamingNumeric.length() < 3 ? "" : roamingNumeric.substring(0, 3);
+        if (excludedMccs != null && !TextUtils.isEmpty(roamingMcc)) {
+            for (String mcc : excludedMccs) {
+                if (roamingMcc.equals(mcc)) {
+                    Log.d(LOG_TAG, "shouldShowRoamingNotification: ignore for MCC " + mcc);
+                    return false;
+                }
+            }
+        }
+
+        if (showRoamingNotification) {
+            Log.d(LOG_TAG, "shouldShowRoamingNotification: show for numeric " + roamingNumeric);
+        }
+        return showRoamingNotification;
+    }
+
     private void updateLimitedSimFunctionForDualSim() {
         if (DBG) Log.d(LOG_TAG, "updateLimitedSimFunctionForDualSim");
         // check conditions to display limited SIM function notification under dual SIM
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 7aa0e7b..e048c0a 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -1304,12 +1304,20 @@
         originalConnection.sendRttModifyResponse(textStream);
     }
 
+    private boolean answeringDropsFgCalls() {
+        Bundle extras = getExtras();
+        if (extras != null) {
+            return extras.getBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
+        }
+        return false;
+    }
+
     public void performAnswer(int videoState) {
         Log.v(this, "performAnswer");
         if (isValidRingingCall() && getPhone() != null) {
             try {
                 mTelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                        getPhoneAccountHandle());
+                        getPhoneAccountHandle(), answeringDropsFgCalls());
                 getPhone().acceptCall(videoState);
             } catch (CallStateException e) {
                 Log.e(this, e, "Failed to accept call.");
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index fcf9531..f37b8a1 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -3819,10 +3819,31 @@
         return origAccountHandle;
     }
 
+    /*
+     * Returns true if both existing connections on-device and the incoming connection support HOLD,
+     * false otherwise. Assumes that a TelephonyConference supports HOLD.
+     */
+    private boolean allCallsSupportHold(@NonNull TelephonyConnection incomingConnection) {
+        if (getAllConnections().stream()
+                .filter(c ->
+                        // Exclude multiendpoint calls as they're not on this device.
+                        (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+                                && (c.getConnectionCapabilities()
+                                & Connection.CAPABILITY_SUPPORT_HOLD) != 0).count() == 0) {
+            return false;
+        }
+        if ((incomingConnection.getConnectionCapabilities()
+                & Connection.CAPABILITY_SUPPORT_HOLD) == 0) {
+            return false;
+        }
+        return true;
+    }
+
     /**
-     * For the passed in incoming {@link TelephonyConnection}, for non- dual active voice devices,
+     * For the passed in incoming {@link TelephonyConnection}, for non-dual active voice devices,
      * adds {@link Connection#EXTRA_ANSWERING_DROPS_FG_CALL} if there are ongoing calls on another
-     * subscription (ie phone account handle) than the one passed in.
+     * subscription (ie phone account handle) than the one passed in. For dual active voice devices,
+     * still sets the EXTRA if either subscription has connections that don't support hold.
      * @param connection The connection.
      * @param phoneAccountHandle The {@link PhoneAccountHandle} the incoming call originated on;
      *                           this is passed in because
@@ -3832,10 +3853,11 @@
      */
     public void maybeIndicateAnsweringWillDisconnect(@NonNull TelephonyConnection connection,
             @NonNull PhoneAccountHandle phoneAccountHandle) {
-        if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
-            return;
-        }
         if (isCallPresentOnOtherSub(phoneAccountHandle)) {
+            if (mTelephonyManagerProxy.isConcurrentCallsPossible()
+                    && allCallsSupportHold(connection)) {
+                return;
+            }
             Log.i(this, "maybeIndicateAnsweringWillDisconnect; answering call %s will cause a call "
                     + "on another subscription to drop.", connection.getTelecomCallId());
             Bundle extras = new Bundle();
@@ -3860,13 +3882,16 @@
 
     /**
      * Where there are ongoing calls on another subscription other than the one specified,
-     * disconnect these calls for non-DSDA devices. This is used where there is an incoming call on
-     * one sub, but there are ongoing calls on another sub which need to be disconnected.
+     * disconnect these calls. This is used where there is an incoming call on one sub, but there
+     * are ongoing calls on another sub which need to be disconnected.
      * @param incomingHandle The incoming {@link PhoneAccountHandle}.
+     * @param answeringDropsFgCall Whether for dual-SIM dual active devices, answering the incoming
+     *                            call should drop the second call.
      */
-    public void maybeDisconnectCallsOnOtherSubs(@NonNull PhoneAccountHandle incomingHandle) {
+    public void maybeDisconnectCallsOnOtherSubs(
+            @NonNull PhoneAccountHandle incomingHandle, boolean answeringDropsFgCall) {
         Log.i(this, "maybeDisconnectCallsOnOtherSubs: check for calls not on %s", incomingHandle);
-        maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle,
+        maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle, answeringDropsFgCall,
                 mTelephonyManagerProxy);
     }
 
@@ -3876,13 +3901,16 @@
      * the core functionality.
      * @param connections the calls to check.
      * @param incomingHandle the incoming handle.
+     * @param answeringDropsFgCall Whether for dual-SIM dual active devices, answering the incoming
+     *                            call should drop the second call.
      * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
      */
     @VisibleForTesting
     public static void maybeDisconnectCallsOnOtherSubs(@NonNull Collection<Connection> connections,
             @NonNull PhoneAccountHandle incomingHandle,
+            boolean answeringDropsFgCall,
             TelephonyManagerProxy telephonyManagerProxy) {
-        if (telephonyManagerProxy.isConcurrentCallsPossible()) {
+        if (telephonyManagerProxy.isConcurrentCallsPossible() && !answeringDropsFgCall) {
             return;
         }
         connections.stream()
@@ -3909,6 +3937,19 @@
                 });
     }
 
+    static boolean isStateActive(Conferenceable conferenceable) {
+        if (conferenceable instanceof Connection) {
+            Connection connection = (Connection) conferenceable;
+            return connection.getState() == Connection.STATE_ACTIVE;
+        } else if (conferenceable instanceof Conference) {
+            Conference conference = (Conference) conferenceable;
+            return conference.getState() == Connection.STATE_ACTIVE;
+        } else {
+            throw new IllegalArgumentException(
+                    "isStateActive(): Unexpected conferenceable! " + conferenceable);
+        }
+    }
+
     static void onHold(Conferenceable conferenceable) {
         if (conferenceable instanceof Connection) {
             Connection connection = (Connection) conferenceable;
@@ -4068,7 +4109,7 @@
             TelephonyManagerProxy telephonyManagerProxy) {
         Conferenceable c = maybeGetFirstConferenceableFromOtherSubscription(
                 connections, conferences, outgoingHandle, telephonyManagerProxy);
-        if (c != null) {
+        if (c != null && isStateActive(c)) {
             onHold(c);
             return c;
         }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 090fc6e..0bfcb5c 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -1709,7 +1709,7 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB1_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB1_HANDLE, false, mTelephonyManagerProxy);
         // Would've preferred to use mockito, but can't mock out TelephonyConnection/Connection
         // easily.
         assertFalse(tc1.wasDisconnected);
@@ -1722,7 +1722,7 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, true);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         // Other call is an emergency call, so don't disconnect it.
         assertFalse(tc1.wasDisconnected);
     }
@@ -1735,7 +1735,7 @@
                 android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         // Other call is an external call, so don't disconnect it.
         assertFalse(tc1.wasDisconnected);
     }
@@ -1747,7 +1747,7 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         assertTrue(tc1.wasDisconnected);
     }
 
@@ -1761,14 +1761,14 @@
         tcs.add(tc1);
         tcs.add(tc2);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         assertTrue(tc1.wasDisconnected);
         assertTrue(tc2.wasDisconnected);
     }
 
     /**
      * Verifies that DSDA or virtual DSDA-enabled devices can support active non-emergency calls on
-     * separate subs.
+     * separate subs, when the extra EXTRA_ANSWERING_DROPS_FG_CALL is not set on the incoming call.
      */
     @Test
     @SmallTest
@@ -1779,10 +1779,26 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         assertFalse(tc1.wasDisconnected);
     }
 
+    /**
+     * Verifies that DSDA or virtual DSDA-enabled devices will disconnect the existing call when the
+     * call extra EXTRA_ANSWERING_DROPS_FG_CALL is set on the incoming call on a different sub.
+     */
+    @Test
+    @SmallTest
+    public void testDisconnectDifferentSubForVirtualDsdaDevice_ifCallExtraSet() {
+        when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tcs.add(tc1);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+                tcs, SUB2_HANDLE, true, mTelephonyManagerProxy);
+        assertTrue(tc1.wasDisconnected);
+    }
 
     /**
      * For calls on the same sub, the Dialer implements the 'swap' functionality to perform hold and
@@ -1845,8 +1861,8 @@
     }
 
     /**
-     * For DSDA devices, placing an outgoing call on a 2nd sub will hold the existing connection on
-     * the first sub.
+     * For DSDA devices, placing an outgoing call on a 2nd sub will hold the existing ACTIVE
+     * connection on the first sub.
      */
     @Test
     @SmallTest
@@ -1855,13 +1871,34 @@
 
         ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tc1.setTelephonyConnectionActive();
         tcs.add(tc1);
+
         Conferenceable c = TelephonyConnectionService.maybeHoldCallsOnOtherSubs(
                 tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
         assertTrue(c.equals(tc1));
         assertTrue(tc1.wasHeld);
     }
 
+    /**
+     * For DSDA devices, if the existing connection was already held, placing an outgoing call on a
+     * 2nd sub will not attempt to hold the existing connection on the first sub.
+     */
+    @Test
+    @SmallTest
+    public void testNoHold_ifExistingConnectionAlreadyHeld_ForVirtualDsdaDevice() {
+        when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tc1.setTelephonyConnectionOnHold();
+        tcs.add(tc1);
+
+        Conferenceable c = TelephonyConnectionService.maybeHoldCallsOnOtherSubs(
+                tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
+        assertNull(c);
+    }
+
     // For 'Virtual DSDA' devices, if there is an existing call on sub1, an outgoing call on sub2
     // will place the sub1 call on hold.
     @Test