Merge "Ims: Add support for Adhoc Conference calls"
diff --git a/src/com/android/server/telecom/BluetoothAdapterProxy.java b/src/com/android/server/telecom/BluetoothAdapterProxy.java
index 72962b8..41b4faa 100644
--- a/src/com/android/server/telecom/BluetoothAdapterProxy.java
+++ b/src/com/android/server/telecom/BluetoothAdapterProxy.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
@@ -37,4 +38,11 @@
         }
         return mBluetoothAdapter.getProfileProxy(context, listener, profile);
     }
+
+    public boolean setActiveDevice(BluetoothDevice device, int profiles) {
+        if (mBluetoothAdapter == null) {
+            return false;
+        }
+        return mBluetoothAdapter.setActiveDevice(device, profiles);
+    }
 }
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index a52f662..e158230 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -137,6 +137,7 @@
         void onPhoneAccountChanged(Call call);
         void onConferenceableCallsChanged(Call call);
         void onConferenceStateChanged(Call call, boolean isConference);
+        void onCdmaConferenceSwap(Call call);
         boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout);
         void onHoldToneRequested(Call call);
         void onCallHoldFailed(Call call);
@@ -209,6 +210,8 @@
         @Override
         public void onConferenceStateChanged(Call call, boolean isConference) {}
         @Override
+        public void onCdmaConferenceSwap(Call call) {}
+        @Override
         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
             return false;
         }
@@ -2507,6 +2510,9 @@
                     mConferenceLevelActiveCall = null;
                     break;
             }
+            for (Listener l : mListeners) {
+                l.onCdmaConferenceSwap(this);
+            }
         }
     }
 
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 8d79ef2..024a863 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -53,7 +53,6 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.SystemVibrator;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -62,6 +61,7 @@
 import android.provider.BlockedNumberContract.SystemContract;
 import android.provider.CallLog.Calls;
 import android.provider.Settings;
+import android.sysprop.TelephonyProperties;
 import android.telecom.CallAudioState;
 import android.telecom.Conference;
 import android.telecom.Connection;
@@ -85,8 +85,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import android.telecom.CallerInfo;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
@@ -167,6 +165,7 @@
         void onDisconnectedTonePlaying(boolean isTonePlaying);
         void onConnectionTimeChanged(Call call);
         void onConferenceStateChanged(Call call, boolean isConference);
+        void onCdmaConferenceSwap(Call call);
     }
 
     /** Interface used to define the action which is executed delay under some condition. */
@@ -242,15 +241,15 @@
 
     public static final String TELECOM_CALL_ID_PREFIX = "TC@";
 
-    // Maps call technologies in PhoneConstants to those in Analytics.
+    // Maps call technologies in TelephonyManager to those in Analytics.
     private static final Map<Integer, Integer> sAnalyticsTechnologyMap;
     static {
         sAnalyticsTechnologyMap = new HashMap<>(5);
-        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE);
-        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE);
-        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE);
-        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE);
-        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY,
+        sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE);
+        sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_GSM, Analytics.GSM_PHONE);
+        sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_IMS, Analytics.IMS_PHONE);
+        sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_SIP, Analytics.SIP_PHONE);
+        sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_THIRD_PARTY,
                 Analytics.THIRD_PARTY_PHONE);
     }
 
@@ -930,6 +929,14 @@
     }
 
     @Override
+    public void onCdmaConferenceSwap(Call call) {
+        // SWAP was executed on a CDMA conference
+        for (CallsManagerListener listener : mListeners) {
+            listener.onCdmaConferenceSwap(call);
+        }
+    }
+
+    @Override
     public void onIsVoipAudioModeChanged(Call call) {
         for (CallsManagerListener listener : mListeners) {
             listener.onIsVoipAudioModeChanged(call);
@@ -1225,8 +1232,10 @@
             }
         }
 
-        if (extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
-            Log.i(this, "Incoming call requesting RTT");
+        boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle);
+        if (isRttSettingOn ||
+                extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+            Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn);
             call.createRttStreams();
             // Even if the phone account doesn't support RTT yet, the connection manager might
             // change that. Set this to check it later.
@@ -1700,9 +1709,12 @@
 
                     boolean isVoicemail = isVoicemail(callToUse.getHandle(), accountToUse);
 
-                    if (!isVoicemail && extras != null
-                            && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
-                        Log.d(this, "Outgoing call requesting RTT");
+                    boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle);
+                    if (!isVoicemail && (isRttSettingOn || (extras != null
+                            && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT,
+                            false)))) {
+                        Log.d(this, "Outgoing call requesting RTT, rtt setting is %b",
+                                isRttSettingOn);
                         if (callToUse.isEmergencyCall() || (accountToUse != null
                                 && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT))) {
                             // If the call requested RTT and it's an emergency call, ignore the
@@ -2092,7 +2104,10 @@
 
         // Auto-enable speakerphone if the originating intent specified to do so, if the call
         // is a video call, of if using speaker when docked
-        call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
+        PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(
+                call.getTargetPhoneAccount(), call.getInitiatingUser());
+        boolean allowVideo = account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING);
+        call.setStartWithSpeakerphoneOn(speakerphoneOn || (useSpeakerForVideoCall && allowVideo)
                 || (useSpeakerWhenDocked && useSpeakerForDock));
         call.setVideoState(videoState);
 
@@ -2326,9 +2341,9 @@
      * @return {@code true} if the speakerphone should automatically be enabled.
      */
     private static boolean isSpeakerEnabledForVideoCalls() {
-        return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
-                PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
-                PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
+        return TelephonyProperties.videocall_audio_output()
+                .orElse(TelecomManager.AUDIO_OUTPUT_DEFAULT)
+                == TelecomManager.AUDIO_OUTPUT_ENABLE_SPEAKER;
     }
 
     /**
@@ -2641,6 +2656,24 @@
         mProximitySensorManager.turnOff(screenOnImmediately);
     }
 
+    private boolean isRttSettingOn(PhoneAccountHandle handle) {
+        boolean isRttModeSettingOn = Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+        // If the carrier config says that we should ignore the RTT mode setting from the user,
+        // assume that it's off (i.e. only make an RTT call if it's requested through the extra).
+        boolean shouldIgnoreRttModeSetting = getCarrierConfigForPhoneAccount(handle)
+                .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
+        return isRttModeSettingOn && !shouldIgnoreRttModeSetting;
+    }
+
+    private PersistableBundle getCarrierConfigForPhoneAccount(PhoneAccountHandle handle) {
+        int subscriptionId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle);
+        CarrierConfigManager carrierConfigManager =
+                mContext.getSystemService(CarrierConfigManager.class);
+        PersistableBundle result = carrierConfigManager.getConfigForSubId(subscriptionId);
+        return result == null ? new PersistableBundle() : result;
+    }
+
     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Attempted to add account to unknown call %s", call);
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index 4fa2ee5..e0d2831 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -100,4 +100,8 @@
     @Override
     public void onConferenceStateChanged(Call call, boolean isConference) {
     }
+
+    @Override
+    public void onCdmaConferenceSwap(Call call) {
+    }
 }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 9fc06e4..b18c6ff 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -865,7 +865,7 @@
                         info.isExternalCallsSupported(), includeRttCall,
                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 try {
-                    inCallService.addCall(parcelableCall);
+                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -929,7 +929,7 @@
                         info.isExternalCallsSupported(), includeRttCall,
                         info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
                 try {
-                    inCallService.addCall(parcelableCall);
+                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -961,7 +961,8 @@
                         );
 
                 try {
-                    inCallService.updateCall(parcelableCall);
+                    inCallService.updateCall(
+                            sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -1046,6 +1047,12 @@
         updateCall(call);
     }
 
+    @Override
+    public void onCdmaConferenceSwap(Call call) {
+        Log.d(this, "onCdmaConferenceSwap %s", call);
+        updateCall(call);
+    }
+
     void bringToForeground(boolean showDialpad) {
         if (!mInCallServices.isEmpty()) {
             for (IInCallService inCallService : mInCallServices.values()) {
@@ -1446,13 +1453,14 @@
                 // Track the call if we don't already know about it.
                 addCall(call);
                 numCallsSent += 1;
-                inCallService.addCall(ParcelableCallUtils.toParcelableCall(
+                ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
                         call,
                         true /* includeVideoProvider */,
                         mCallsManager.getPhoneAccountRegistrar(),
                         info.isExternalCallsSupported(),
                         includeRttCall,
-                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI));
+                        info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI);
+                inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
             } catch (RemoteException ignored) {
             }
         }
@@ -1522,7 +1530,8 @@
                 componentsUpdated.add(componentName);
 
                 try {
-                    inCallService.updateCall(parcelableCall);
+                    inCallService.updateCall(
+                            sanitizeParcelableCallForService(info, parcelableCall));
                 } catch (RemoteException ignored) {
                 }
             }
@@ -1654,6 +1663,21 @@
         return childCalls;
     }
 
+    private ParcelableCall sanitizeParcelableCallForService(
+            InCallServiceInfo info, ParcelableCall parcelableCall) {
+        ParcelableCall.ParcelableCallBuilder builder =
+                ParcelableCall.ParcelableCallBuilder.fromParcelableCall(parcelableCall);
+        // Check for contacts permission. If it's not there, remove the contactsDisplayName.
+        PackageManager pm = mContext.getPackageManager();
+        if (pm.checkPermission(Manifest.permission.READ_CONTACTS,
+                info.getComponentName().getPackageName()) != PackageManager.PERMISSION_GRANTED) {
+            builder.setContactDisplayName(null);
+        }
+
+        // TODO: move all the other service-specific sanitizations in here
+        return builder.createParcelableCall();
+    }
+
     @VisibleForTesting
     public Handler getHandler() {
         return mHandler;
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 3a366aa..d1d8f0d 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -29,14 +29,10 @@
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 /**
  * Utilities dealing with {@link ParcelableCall}.
@@ -224,6 +220,11 @@
             callDirection = DIRECTION_OUTGOING;
         }
 
+        String activeChildCallId = null;
+        if (call.getConferenceLevelActiveCall() != null) {
+            activeChildCallId = call.getConferenceLevelActiveCall().getId();
+        }
+
         Bundle extras;
         if (isForSystemDialer) {
             extras = call.getExtras();
@@ -231,35 +232,38 @@
             extras = sanitizeExtras(call.getExtras());
         }
 
-        return new ParcelableCall(
-                call.getId(),
-                state,
-                call.getDisconnectCause(),
-                call.getCannedSmsResponses(),
-                capabilities,
-                properties,
-                supportedAudioRoutes,
-                connectTimeMillis,
-                handle,
-                call.getHandlePresentation(),
-                callerDisplayName,
-                call.getCallerDisplayNamePresentation(),
-                call.getGatewayInfo(),
-                call.getTargetPhoneAccount(),
-                includeVideoProvider,
-                includeVideoProvider ? call.getVideoProvider() : null,
-                includeRttCall,
-                rttCall,
-                parentCallId,
-                childCallIds,
-                call.getStatusHints(),
-                call.getVideoState(),
-                conferenceableCallIds,
-                call.getIntentExtras(),
-                extras,
-                call.getCreationTimeMillis(),
-                callDirection,
-                call.getCallerNumberVerificationStatus());
+        return new ParcelableCall.ParcelableCallBuilder()
+                .setId(call.getId())
+                .setState(state)
+                .setDisconnectCause(call.getDisconnectCause())
+                .setCannedSmsResponses(call.getCannedSmsResponses())
+                .setCapabilities(capabilities)
+                .setProperties(properties)
+                .setSupportedAudioRoutes(supportedAudioRoutes)
+                .setConnectTimeMillis(connectTimeMillis)
+                .setHandle(handle)
+                .setHandlePresentation(call.getHandlePresentation())
+                .setCallerDisplayName(callerDisplayName)
+                .setCallerDisplayNamePresentation(call.getCallerDisplayNamePresentation())
+                .setGatewayInfo(call.getGatewayInfo())
+                .setAccountHandle(call.getTargetPhoneAccount())
+                .setIsVideoCallProviderChanged(includeVideoProvider)
+                .setVideoCallProvider(includeVideoProvider ? call.getVideoProvider() : null)
+                .setIsRttCallChanged(includeRttCall)
+                .setRttCall(rttCall)
+                .setParentCallId(parentCallId)
+                .setChildCallIds(childCallIds)
+                .setStatusHints(call.getStatusHints())
+                .setVideoState(call.getVideoState())
+                .setConferenceableCallIds(conferenceableCallIds)
+                .setIntentExtras(call.getIntentExtras())
+                .setExtras(extras)
+                .setCreationTimeMillis(call.getCreationTimeMillis())
+                .setCallDirection(callDirection)
+                .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
+                .setContactDisplayName(call.getName())
+                .setActiveChildCallId(activeChildCallId)
+                .createParcelableCall();
     }
 
     /**
@@ -303,35 +307,38 @@
             callExtras = new Bundle();
         }
 
-        return new ParcelableCall(
-                call.getId(),
-                getParcelableState(call, false /* supportsExternalCalls */),
-                new DisconnectCause(DisconnectCause.UNKNOWN),
-                null, /* cannedSmsResponses */
-                0, /* capabilities */
-                0, /* properties */
-                0, /* supportedAudioRoutes */
-                call.getConnectTimeMillis(),
-                handle,
-                call.getHandlePresentation(),
-                null, /* callerDisplayName */
-                0 /* callerDisplayNamePresentation */,
-                null, /* gatewayInfo */
-                null, /* targetPhoneAccount */
-                false, /* includeVideoProvider */
-                null, /* videoProvider */
-                false, /* includeRttCall */
-                null, /* rttCall */
-                null, /* parentCallId */
-                null, /* childCallIds */
-                null, /* statusHints */
-                0, /* videoState */
-                Collections.emptyList(), /* conferenceableCallIds */
-                null, /* intentExtras */
-                callExtras, /* callExtras */
-                call.getCreationTimeMillis(),
-                callDirection,
-                call.getCallerNumberVerificationStatus());
+        return new ParcelableCall.ParcelableCallBuilder()
+                .setId(call.getId())
+                .setState(getParcelableState(call, false /* supportsExternalCalls */))
+                .setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN))
+                .setCannedSmsResponses(null)
+                .setCapabilities(0)
+                .setProperties(0)
+                .setSupportedAudioRoutes(0)
+                .setConnectTimeMillis(call.getConnectTimeMillis())
+                .setHandle(handle)
+                .setHandlePresentation(call.getHandlePresentation())
+                .setCallerDisplayName(null)
+                .setCallerDisplayNamePresentation(0)
+                .setGatewayInfo(null)
+                .setAccountHandle(null)
+                .setIsVideoCallProviderChanged(false)
+                .setVideoCallProvider(null)
+                .setIsRttCallChanged(false)
+                .setRttCall(null)
+                .setParentCallId(null)
+                .setChildCallIds(null)
+                .setStatusHints(null)
+                .setVideoState(0)
+                .setConferenceableCallIds(Collections.emptyList())
+                .setIntentExtras(null)
+                .setExtras(callExtras)
+                .setCreationTimeMillis(call.getCreationTimeMillis())
+                .setCallDirection(callDirection)
+                .setCallerNumberVerificationStatus(call.getCallerNumberVerificationStatus())
+                .setContactDisplayName(null)
+                .setActiveChildCallId(null)
+                .createParcelableCall();
     }
 
     /**
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 92de536..7fd600c 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom.bluetooth;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
@@ -119,9 +120,11 @@
     private BluetoothHeadsetProxy mBluetoothHeadsetService;
     private BluetoothHearingAid mBluetoothHearingAidService;
     private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;
+    private BluetoothAdapterProxy mBluetoothAdapterProxy;
 
     public BluetoothDeviceManager(Context context, BluetoothAdapterProxy bluetoothAdapter) {
         if (bluetoothAdapter != null) {
+            mBluetoothAdapterProxy = bluetoothAdapter;
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                     BluetoothProfile.HEADSET);
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
@@ -246,7 +249,8 @@
         } else {
             for (BluetoothDevice device : mBluetoothHearingAidService.getActiveDevices()) {
                 if (device != null) {
-                    mBluetoothHearingAidService.setActiveDevice(null);
+                    mBluetoothAdapterProxy.setActiveDevice(null,
+                        BluetoothAdapter.ACTIVE_DEVICE_ALL);
                 }
             }
         }
@@ -269,15 +273,17 @@
                 Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
                 return false;
             }
-            return mBluetoothHearingAidService.setActiveDevice(
-                    mHearingAidDevicesByAddress.get(address));
+            return mBluetoothAdapterProxy.setActiveDevice(
+                    mHearingAidDevicesByAddress.get(address),
+                    BluetoothAdapter.ACTIVE_DEVICE_ALL);
         } else if (mHfpDevicesByAddress.containsKey(address)) {
             BluetoothDevice device = mHfpDevicesByAddress.get(address);
             if (mBluetoothHeadsetService == null) {
                 Log.w(this, "Attempting to turn on audio when the headset service is null");
                 return false;
             }
-            boolean success = mBluetoothHeadsetService.setActiveDevice(device);
+            boolean success = mBluetoothAdapterProxy.setActiveDevice(device,
+                BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
             if (!success) {
                 Log.w(this, "Couldn't set active device to %s", address);
                 return false;
@@ -304,7 +310,9 @@
 
     public void restoreHearingAidDevice() {
         if (mBluetoothHearingAidActiveDeviceCache != null && mBluetoothHearingAidService != null) {
-            mBluetoothHearingAidService.setActiveDevice(mBluetoothHearingAidActiveDeviceCache);
+            mBluetoothAdapterProxy.setActiveDevice(
+                mBluetoothHearingAidActiveDeviceCache,
+                BluetoothAdapter.ACTIVE_DEVICE_ALL);
             mBluetoothHearingAidActiveDeviceCache = null;
         }
     }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 7671abd..8a14cbd 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -146,6 +146,10 @@
             if (device == null) {
                 mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args);
             } else {
+                if (!mIsInCall) {
+                    Log.i(LOG_TAG, "Ignoring hearing aid audio on since we're not in a call");
+                    return;
+                }
                 args.arg2 = device.getAddress();
                 mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args);
             }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 63ff962..bfa7a75 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom.tests;
 
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
@@ -59,7 +60,7 @@
 
     BluetoothDeviceManager mBluetoothDeviceManager;
     BluetoothProfile.ServiceListener serviceListenerUnderTest;
-    BroadcastReceiver receiverUnderTest;
+    BluetoothStateReceiver receiverUnderTest;
 
     private BluetoothDevice device1;
     private BluetoothDevice device2;
@@ -90,8 +91,7 @@
                 serviceCaptor.capture(), eq(BluetoothProfile.HEADSET));
         serviceListenerUnderTest = serviceCaptor.getValue();
 
-        receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager,
-                null /* route mgr not needed here */);
+        receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager, mRouteManager);
 
         mBluetoothDeviceManager.setHeadsetServiceForTesting(mHeadsetProxy);
         mBluetoothDeviceManager.setHearingAidServiceForTesting(mBluetoothHearingAid);
@@ -198,15 +198,28 @@
 
     @SmallTest
     @Test
+    public void testHearingAidChangesIgnoredWhenNotInCall() {
+        receiverUnderTest.setIsInCall(false);
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
+        Intent activeDeviceChangedIntent =
+                new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
+        activeDeviceChangedIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device2);
+        receiverUnderTest.onReceive(mContext, activeDeviceChangedIntent);
+
+        verify(mRouteManager).onActiveDeviceChanged(device2, true);
+        verify(mRouteManager, never()).sendMessage(BluetoothRouteManager.BT_AUDIO_IS_ON);
+    }
+
+    @SmallTest
+    @Test
     public void testConnectDisconnectAudioHeadset() {
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1, false));
-        when(mHeadsetProxy.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true);
+        when(mAdapterProxy.setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_ALL))).thenReturn(true);
         mBluetoothDeviceManager.connectAudio(device1.getAddress());
-        verify(mHeadsetProxy).setActiveDevice(device1);
-        verify(mHeadsetProxy).connectAudio();
-        verify(mBluetoothHearingAid, never()).setActiveDevice(nullable(BluetoothDevice.class));
-
+        verify(mAdapterProxy).setActiveDevice(device1, BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
+        verify(mAdapterProxy, never()).setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_ALL));
         mBluetoothDeviceManager.disconnectAudio();
         verify(mHeadsetProxy).disconnectAudio();
     }
@@ -217,14 +230,14 @@
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2, true));
         mBluetoothDeviceManager.connectAudio(device2.getAddress());
-        verify(mBluetoothHearingAid).setActiveDevice(device2);
+        verify(mAdapterProxy).setActiveDevice(device2, BluetoothAdapter.ACTIVE_DEVICE_ALL);
         verify(mHeadsetProxy, never()).connectAudio();
-        verify(mHeadsetProxy, never()).setActiveDevice(nullable(BluetoothDevice.class));
+        verify(mAdapterProxy, never()).setActiveDevice(nullable(BluetoothDevice.class), eq(BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL));
 
         when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(device2, null));
 
         mBluetoothDeviceManager.disconnectAudio();
-        verify(mBluetoothHearingAid).setActiveDevice(null);
+        verify(mAdapterProxy).setActiveDevice(null, BluetoothAdapter.ACTIVE_DEVICE_ALL);
         verify(mHeadsetProxy).disconnectAudio();
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index ef294bd..e6fa1d0 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -42,9 +42,9 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.telecom.CallerInfo;
 import android.telecom.Connection;
-import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -1195,6 +1195,22 @@
         assertTrue(ongoingCall.isVideoCallingSupportedByPhoneAccount());
     }
 
+    /**
+     * Verifies that speakers is disabled when there's no video capabilities, even if a video call
+     * tried to place.
+     * @throws Exception
+     */
+    @SmallTest
+    @Test
+    public void testSpeakerDisabledWhenNoVideoCapabilities() throws Exception {
+        Call outgoingCall = addSpyCall(CallState.NEW);
+        when(mPhoneAccountRegistrar.getPhoneAccount(
+                any(PhoneAccountHandle.class), any(UserHandle.class))).thenReturn(SIM_1_ACCOUNT);
+        mCallsManager.placeOutgoingCall(outgoingCall, TEST_ADDRESS, null, true,
+                VideoProfile.STATE_TX_ENABLED);
+        assertFalse(outgoingCall.getStartWithSpeakerphoneOn());
+    }
+
 
     private Call addSpyCall() {
         return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index ed36d29..23d22e9 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -625,6 +625,44 @@
         verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS);
     }
 
+    @MediumTest
+    @Test
+    public void testSanitizeContactName() throws Exception {
+        setupMocks(false /* isExternalCall */);
+        setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
+        when(mMockPackageManager.checkPermission(
+                matches(Manifest.permission.READ_CONTACTS),
+                matches(DEF_PKG))).thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mMockCall.getName()).thenReturn("evil");
+
+        mInCallController.bindToServices(mMockCall);
+
+        // Bind InCallServices
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                serviceConnectionCaptor.capture(),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
+                eq(UserHandle.CURRENT));
+        assertEquals(1, bindIntentCaptor.getAllValues().size());
+        verifyBinding(bindIntentCaptor, 0, DEF_PKG, DEF_CLASS);
+
+        IInCallService.Stub mockInCallServiceStub = mock(IInCallService.Stub.class);
+        IInCallService mockInCallService = mock(IInCallService.class);
+        when(mockInCallServiceStub.queryLocalInterface(anyString())).thenReturn(mockInCallService);
+        serviceConnectionCaptor.getValue().onServiceConnected(new ComponentName(DEF_PKG, DEF_CLASS),
+                mockInCallServiceStub);
+
+        mInCallController.onCallAdded(mMockCall);
+        ArgumentCaptor<ParcelableCall> parcelableCallCaptor =
+                ArgumentCaptor.forClass(ParcelableCall.class);
+        verify(mockInCallService).addCall(parcelableCallCaptor.capture());
+        assertTrue(TextUtils.isEmpty(parcelableCallCaptor.getValue().getContactDisplayName()));
+    }
+
     /**
      * Ensures that the {@link InCallController} will bind to a higher priority car mode service
      * when one becomes available.