Implement extra key filtering for non-system dialers.

Add ability to filter some extra keys from calls which are destined for
a dialer other than the OEM/system dialer.

Test: Added unit tests.
Bug: 124232210
Change-Id: I8055d1428a31ff03c96221d1d65adfcc305f23c3
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 514e5f3..e110be8 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -2065,7 +2065,7 @@
      * @param source The source of the extras addition.
      * @param extras The extras.
      */
-    void putExtras(int source, Bundle extras) {
+    public void putExtras(int source, Bundle extras) {
         if (extras == null) {
             return;
         }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 142cf3a..7d4ee13 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -797,7 +797,8 @@
 
                 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
                         true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
-                        info.isExternalCallsSupported(), includeRttCall);
+                        info.isExternalCallsSupported(), includeRttCall,
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 try {
                     inCallService.addCall(parcelableCall);
                 } catch (RemoteException ignored) {
@@ -861,7 +862,8 @@
 
                 ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(call,
                         true /* includeVideoProvider */, mCallsManager.getPhoneAccountRegistrar(),
-                        info.isExternalCallsSupported(), includeRttCall);
+                        info.isExternalCallsSupported(), includeRttCall,
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 try {
                     inCallService.addCall(parcelableCall);
                 } catch (RemoteException ignored) {
@@ -872,15 +874,7 @@
             // The call was regular but it is now external.  We must now remove it from any
             // InCallServices which do not support external calls.
             // Remove the call by sending a call update indicating the call was disconnected.
-            ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
-                    call,
-                    false /* includeVideoProvider */,
-                    mCallsManager.getPhoneAccountRegistrar(),
-                    false /* supportsExternalCalls */,
-                    android.telecom.Call.STATE_DISCONNECTED /* overrideState */,
-                    false /* includeRttCall */);
-
-            Log.i(this, "Removing external call %s ==> %s", call, parcelableCall);
+            Log.i(this, "Removing external call %", call);
             for (Map.Entry<InCallServiceInfo, IInCallService> entry : mInCallServices.entrySet()) {
                 InCallServiceInfo info = entry.getKey();
                 if (info.isExternalCallsSupported()) {
@@ -892,6 +886,16 @@
                 componentsUpdated.add(info.getComponentName());
                 IInCallService inCallService = entry.getValue();
 
+                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
+                        call,
+                        false /* includeVideoProvider */,
+                        mCallsManager.getPhoneAccountRegistrar(),
+                        false /* supportsExternalCalls */,
+                        android.telecom.Call.STATE_DISCONNECTED /* overrideState */,
+                        false /* includeRttCall */,
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+                        );
+
                 try {
                     inCallService.updateCall(parcelableCall);
                 } catch (RemoteException ignored) {
@@ -1360,7 +1364,8 @@
                         true /* includeVideoProvider */,
                         mCallsManager.getPhoneAccountRegistrar(),
                         info.isExternalCallsSupported(),
-                        includeRttCall));
+                        includeRttCall,
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI));
             } catch (RemoteException ignored) {
             }
         }
@@ -1422,7 +1427,8 @@
                         videoProviderChanged /* includeVideoProvider */,
                         mCallsManager.getPhoneAccountRegistrar(),
                         info.isExternalCallsSupported(),
-                        rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()));
+                        rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()),
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 ComponentName componentName = info.getComponentName();
                 IInCallService inCallService = entry.getValue();
                 componentsUpdated.add(componentName);
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 8dab6a6..3e3ce3d 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -21,14 +21,17 @@
 import static android.telecom.Call.Details.DIRECTION_UNKNOWN;
 
 import android.net.Uri;
+import android.os.Bundle;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.ParcelableCall;
 import android.telecom.ParcelableRttCall;
 import android.telecom.TelecomManager;
+import android.text.TextUtils;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -37,11 +40,23 @@
 public class ParcelableCallUtils {
     private static final int CALL_STATE_OVERRIDE_NONE = -1;
 
+    /**
+     * A list of extra keys which should be removed from a {@link ParcelableCall} when it is being
+     * generated for the purpose of sending to a dialer other than the system dialer.
+     * By convention we only pass keys namespaced with android.*, however there are some keys which
+     * should not be passed to non-system dialer apps either.
+     */
+    private static List<String> EXTRA_KEYS_TO_SANITIZE;
+    static {
+        EXTRA_KEYS_TO_SANITIZE = new ArrayList<>();
+        EXTRA_KEYS_TO_SANITIZE.add(android.telecom.Connection.EXTRA_SIP_INVITE);
+    }
+
     public static class Converter {
         public ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider,
                 PhoneAccountRegistrar phoneAccountRegistrar) {
             return ParcelableCallUtils.toParcelableCall(
-                    call, includeVideoProvider, phoneAccountRegistrar, false, false);
+                    call, includeVideoProvider, phoneAccountRegistrar, false, false, false);
         }
 
         public ParcelableCall toParcelableCallForScreening(Call call) {
@@ -60,16 +75,23 @@
      * @param phoneAccountRegistrar The {@link PhoneAccountRegistrar}.
      * @param supportsExternalCalls Indicates whether the call should be parcelled for an
      *      {@link InCallService} which supports external calls or not.
+     * @param includeRttCall {@code true} if the RTT call should be included, {@code false}
+     *      otherwise.
+     * @param isForSystemDialer {@code true} if this call is being parcelled for the system dialer,
+     *      {@code false} otherwise.  When parceling for the system dialer, the entire call extras
+     *      is included.  When parceling for anything other than the system dialer, some extra key
+     *      values will be stripped for privacy sake.
      */
     public static ParcelableCall toParcelableCall(
             Call call,
             boolean includeVideoProvider,
             PhoneAccountRegistrar phoneAccountRegistrar,
             boolean supportsExternalCalls,
-            boolean includeRttCall) {
+            boolean includeRttCall,
+            boolean isForSystemDialer) {
         return toParcelableCall(call, includeVideoProvider, phoneAccountRegistrar,
                 supportsExternalCalls, CALL_STATE_OVERRIDE_NONE /* overrideState */,
-                includeRttCall);
+                includeRttCall, isForSystemDialer);
     }
 
     /**
@@ -85,6 +107,10 @@
      *      {@link InCallService} which supports external calls or not.
      * @param overrideState When not {@link #CALL_STATE_OVERRIDE_NONE}, use the provided state as an
      *      override to whatever is defined in the call.
+     * @param isForSystemDialer {@code true} if this call is being parcelled for the system dialer,
+     *      {@code false} otherwise.  When parceling for the system dialer, the entire call extras
+     *      is included.  When parceling for anything other than the system dialer, some extra key
+     *      values will be stripped for privacy sake.
      * @return The {@link ParcelableCall} containing all call information from the {@link Call}.
      */
     public static ParcelableCall toParcelableCall(
@@ -93,7 +119,8 @@
             PhoneAccountRegistrar phoneAccountRegistrar,
             boolean supportsExternalCalls,
             int overrideState,
-            boolean includeRttCall) {
+            boolean includeRttCall,
+            boolean isForSystemDialer) {
         int state;
         if (overrideState == CALL_STATE_OVERRIDE_NONE) {
             state = getParcelableState(call, supportsExternalCalls);
@@ -180,6 +207,13 @@
             callDirection = DIRECTION_OUTGOING;
         }
 
+        Bundle extras;
+        if (isForSystemDialer) {
+            extras = call.getExtras();
+        } else {
+            extras = sanitizeExtras(call.getExtras());
+        }
+
         return new ParcelableCall(
                 call.getId(),
                 state,
@@ -205,7 +239,7 @@
                 call.getVideoState(),
                 conferenceableCallIds,
                 call.getIntentExtras(),
-                call.getExtras(),
+                extras,
                 call.getCreationTimeMillis(),
                 call.getCallIdentification(),
                 callDirection);
@@ -268,6 +302,32 @@
                 callDirection);
     }
 
+    /**
+     * Sanitize the extras bundle passed in, removing keys which should not be sent to non-system
+     * dialer apps.
+     * @param extras Extras bundle to sanitize.
+     * @return The sanitized extras bundle.
+     */
+    private static Bundle sanitizeExtras(Bundle oldExtras) {
+        if (oldExtras == null) {
+            return new Bundle();
+        }
+        Bundle extras = new Bundle(oldExtras);
+        for (String key : EXTRA_KEYS_TO_SANITIZE) {
+            extras.remove(key);
+        }
+
+        // As a catch-all remove any that don't start with android namespace.
+        Iterator<String> toCheck = extras.keySet().iterator();
+        while (toCheck.hasNext()) {
+            String extraKey = toCheck.next();
+            if (TextUtils.isEmpty(extraKey) || !extraKey.startsWith("android.")) {
+                toCheck.remove();
+            }
+        }
+        return extras;
+    }
+
     private static int getParcelableState(Call call, boolean supportsExternalCalls) {
         int state = CallState.NEW;
         switch (call.getState()) {
diff --git a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
new file mode 100644
index 0000000..753b635
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
@@ -0,0 +1,122 @@
+package com.android.server.telecom.tests;
+
+import static com.android.server.telecom.TelecomSystem.*;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.telecom.Connection;
+import android.telecom.GatewayInfo;
+import android.telecom.ParcelableCall;
+import android.telecom.PhoneAccountHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallerInfoLookupHelper;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.ConnectionServiceRepository;
+import com.android.server.telecom.ParcelableCallUtils;
+import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.TelecomSystem;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class ParcelableCallUtilsTest extends TelecomTestCase {
+
+    private SyncRoot mLock = new SyncRoot() {};
+    @Mock private ClockProxy mClockProxy;
+    @Mock private CallsManager mCallsManager;
+    @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper;
+    @Mock private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
+    @Mock private PhoneAccountRegistrar mPhoneAccountRegistrar;
+    private Call mCall;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
+        when(mClockProxy.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+        when(mCallsManager.getCallerInfoLookupHelper()).thenReturn(mCallerInfoLookupHelper);
+        when(mCallsManager.getPhoneAccountRegistrar()).thenReturn(mPhoneAccountRegistrar);
+        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(any())).thenReturn(null);
+        when(mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(any(), any())).thenReturn(false);
+        mCall = new Call("1",
+                null /* context */,
+                mCallsManager,
+                mLock,
+                null /* ConnectionServiceRepository */,
+                mPhoneNumberUtilsAdapter,
+                Uri.fromParts("tel", "6505551212", null),
+                null /* GatewayInfo */,
+                null /* connectionMgr */,
+                new PhoneAccountHandle(
+                        ComponentName.unflattenFromString("com.test/Class"), "test"),
+                Call.CALL_DIRECTION_INCOMING,
+                false /* shouldAttachToExistingConnection */,
+                false /* isConference */,
+                mClockProxy /* ClockProxy */);
+    }
+
+    @SmallTest
+    @Test
+    public void testParcelForNonSystemDialer() {
+        mCall.putExtras(Call.SOURCE_CONNECTION_SERVICE, getSomeExtras());
+        ParcelableCall call = ParcelableCallUtils.toParcelableCall(mCall,
+                false /* includevideoProvider */,
+                null /* phoneAccountRegistrar */,
+                false /* supportsExternalCalls */,
+                false /* includeRttCall */,
+                false /* isForSystemDialer */);
+
+        Bundle parceledExtras = call.getExtras();
+        assertFalse(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE));
+        assertFalse(parceledExtras.containsKey("SomeExtra"));
+        assertTrue(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
+    }
+
+    @SmallTest
+    @Test
+    public void testParcelForSystemDialer() {
+        mCall.putExtras(Call.SOURCE_CONNECTION_SERVICE, getSomeExtras());
+        ParcelableCall call = ParcelableCallUtils.toParcelableCall(mCall,
+                false /* includevideoProvider */,
+                null /* phoneAccountRegistrar */,
+                false /* supportsExternalCalls */,
+                false /* includeRttCall */,
+                true /* isForSystemDialer */);
+
+        Bundle parceledExtras = call.getExtras();
+        assertTrue(parceledExtras.containsKey(Connection.EXTRA_SIP_INVITE));
+        assertTrue(parceledExtras.containsKey("SomeExtra"));
+        assertTrue(parceledExtras.containsKey(Connection.EXTRA_CALL_SUBJECT));
+    }
+
+    private Bundle getSomeExtras() {
+        Bundle extras = new Bundle();
+        extras.putString(Connection.EXTRA_SIP_INVITE, "scary data");
+        extras.putString("SomeExtra", "Extra Extra");
+        extras.putString(Connection.EXTRA_CALL_SUBJECT, "Blah");
+        return extras;
+    }
+}