Merge "Update call log from nuisance report"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..8ee7754
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,77 @@
+// Build the Telecom service.
+android_app {
+    name: "Telecom",
+    libs: ["telephony-common"],
+    srcs: [
+        "src/**/*.java",
+        "proto/**/*.proto",
+    ],
+    resource_dirs: ["res"],
+    proto: {
+        type: "nano",
+        local_include_dirs: ["proto/"],
+        output_params: ["optional_field_style=accessors"],
+    },
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+    defaults: ["SettingsLibDefaults"],
+}
+
+android_test {
+    name: "TelecomUnitTests",
+    static_libs: [
+        "android-ex-camera2",
+        "guava",
+        "mockito-target-inline",
+        "android-support-test",
+        "platform-test-annotations",
+        "androidx.legacy_legacy-support-core-ui",
+        "androidx.legacy_legacy-support-core-utils",
+        "androidx.core_core",
+        "androidx.fragment_fragment",
+    ],
+    srcs: [
+        "tests/src/**/*.java",
+        "src/**/*.java",
+        "proto/**/*.proto",
+    ],
+    proto: {
+        type: "nano",
+        local_include_dirs: ["proto/"],
+        output_params: ["optional_field_style=accessors"],
+    },
+    resource_dirs: [
+        "tests/res",
+        "res",
+    ],
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+        "telephony-common",
+    ],
+
+    jni_libs: ["libdexmakerjvmtiagent"],
+
+    aaptflags: [
+        "--auto-add-overlay",
+        "--extra-packages",
+        "com.android.server.telecom",
+    ],
+    manifest: "tests/AndroidManifest.xml",
+    optimize: {
+        enabled: false,
+    },
+    platform_apis: true,
+    certificate: "platform",
+    jacoco: {
+        include_filter: ["com.android.server.telecom.*"],
+        exclude_filter: ["com.android.server.telecom.tests.*"],
+    },
+    test_suites: ["device-tests"],
+    defaults: ["SettingsLibDefaults"],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 4e5eeff..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Telecom service.
-include $(CLEAR_VARS)
-
-LOCAL_JAVA_LIBRARIES := telephony-common
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under, proto)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_USE_AAPT2 := true
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
-
-LOCAL_PACKAGE_NAME := Telecom
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include frameworks/base/packages/SettingsLib/common.mk
-
-include $(BUILD_PACKAGE)
-
-# Build the test package.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 349b789..4545ba2 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1513,8 +1513,10 @@
                             false)))) {
                         Log.d(this, "Outgoing call requesting RTT, rtt setting is %b",
                                 isRttSettingOn());
-                        if (accountToUse != null
-                                && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
+                        if (callToUse.isEmergencyCall() || (accountToUse != null
+                                && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT))) {
+                            // If the call requested RTT and it's an emergency call, ignore the
+                            // capability and hope that the modem will deal with it somehow.
                             callToUse.createRttStreams();
                         }
                         // Even if the phone account doesn't support RTT yet,
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index e53488c..df7896c 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -450,6 +450,7 @@
     // Tracks the active devices in the BT stack (HFP or hearing aid).
     private BluetoothDevice mHfpActiveDeviceCache = null;
     private BluetoothDevice mHearingAidActiveDeviceCache = null;
+    private BluetoothDevice mMostRecentlyReportedActiveDevice = null;
 
     public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock,
             BluetoothDeviceManager deviceManager, Timeouts.Adapter timeoutsAdapter) {
@@ -588,6 +589,9 @@
         } else {
             mHfpActiveDeviceCache = device;
         }
+
+        if (device != null) mMostRecentlyReportedActiveDevice = device;
+
         boolean isActiveDevicePresent = mHearingAidActiveDeviceCache != null
                 || mHfpActiveDeviceCache != null;
 
@@ -690,30 +694,38 @@
         BluetoothHeadsetProxy bluetoothHeadset = mDeviceManager.getHeadsetService();
         BluetoothHearingAid bluetoothHearingAid = mDeviceManager.getHearingAidService();
 
+        BluetoothDevice hfpActiveDevice = null;
+        BluetoothDevice hearingAidActiveDevice = null;
+
         if (bluetoothHeadset == null && bluetoothHearingAid == null) {
             Log.i(this, "getBluetoothAudioConnectedDevice: no service available.");
             return null;
         }
 
         if (bluetoothHeadset != null) {
-            for (BluetoothDevice device : bluetoothHeadset.getConnectedDevices()) {
-                boolean isAudioOn = bluetoothHeadset.getAudioState(device)
-                        != BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
-                Log.v(this, "isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn
-                        + "for headset: " + device);
-                if (isAudioOn) {
-                    return device;
-                }
-            }
+            hfpActiveDevice = bluetoothHeadset.getActiveDevice();
         }
+
         if (bluetoothHearingAid != null) {
             for (BluetoothDevice device : bluetoothHearingAid.getActiveDevices()) {
                 if (device != null) {
-                    return device;
+                    hearingAidActiveDevice = device;
+                    break;
                 }
             }
         }
-        return null;
+
+        // Return the active device reported by either HFP or hearing aid. If both are reporting
+        // active devices, go with the most recent one as reported by the receiver.
+        if (hfpActiveDevice != null) {
+            if (hearingAidActiveDevice != null) {
+                Log.i(this, "Both HFP and hearing aid are reporting active devices. Going with"
+                        + " the most recently reported active device: %s");
+                return mMostRecentlyReportedActiveDevice;
+            }
+            return hfpActiveDevice;
+        }
+        return hearingAidActiveDevice;
     }
 
     /**
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 0176a43..da2b8f1 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -136,7 +136,7 @@
         boolean isHearingAid =
                 BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction());
         Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device,
-                isHearingAid ? "heading aid" : "HFP");
+                isHearingAid ? "hearing aid" : "HFP");
 
         mBluetoothRouteManager.onActiveDeviceChanged(device, isHearingAid);
         if (isHearingAid) {
diff --git a/testapps/Android.bp b/testapps/Android.bp
new file mode 100644
index 0000000..c647b1e
--- /dev/null
+++ b/testapps/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2013 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.
+//
+
+android_test {
+    name: "TelecomTestApps",
+    static_libs: [
+        "android-support-v4",
+        "android-ex-camera2",
+        "guava",
+    ],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/testapps/Android.mk b/testapps/Android.mk
deleted file mode 100644
index b600dca..0000000
--- a/testapps/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-support-v4 \
-        android-ex-camera2 \
-        guava
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := TelecomTestApps
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 5abf999..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,75 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-ex-camera2 \
-    guava \
-    mockito-target-inline \
-    android-support-test \
-    platform-test-annotations
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.legacy_legacy-support-core-ui \
-    androidx.legacy_legacy-support-core-utils \
-    androidx.core_core \
-    androidx.fragment_fragment
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src) \
-    $(call all-java-files-under, ../src) \
-    $(call all-proto-files-under, ../proto)
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../proto/
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
-
-LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/res \
-    $(LOCAL_PATH)/../res
-
-LOCAL_JAVA_LIBRARIES := \
-    android.test.mock \
-    android.test.base \
-    android.test.runner \
-    telephony-common
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_JNI_SHARED_LIBRARIES := \
-    libdexmakerjvmtiagent \
-
-LOCAL_AAPT_FLAGS := \
-    --auto-add-overlay \
-    --extra-packages com.android.server.telecom
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PACKAGE_NAME := TelecomUnitTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.server.telecom.*
-LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := com.android.server.telecom.tests.*
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-include frameworks/base/packages/SettingsLib/common.mk
-
-include $(BUILD_PACKAGE)
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 42626d9..060031d 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -38,7 +38,9 @@
 import org.mockito.Mock;
 
 import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -55,6 +57,7 @@
     static final BluetoothDevice DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01");
     static final BluetoothDevice DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02");
     static final BluetoothDevice DEVICE3 = makeBluetoothDevice("00:00:00:00:00:03");
+    static final BluetoothDevice HEARING_AID_DEVICE = makeBluetoothDevice("00:00:00:00:00:04");
 
     @Mock private BluetoothDeviceManager mDeviceManager;
     @Mock private BluetoothHeadsetProxy mHeadsetProxy;
@@ -73,7 +76,7 @@
     public void testConnectHfpRetryWhileNotConnected() {
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
-        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null, null, null);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(0L);
         when(mHeadsetProxy.connectAudio()).thenReturn(false);
@@ -92,10 +95,29 @@
 
     @SmallTest
     @Test
+    public void testAmbiguousActiveDevice() {
+        BluetoothRouteManager sm = setupStateMachine(
+                BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1},
+                new BluetoothDevice[]{HEARING_AID_DEVICE}, DEVICE1, HEARING_AID_DEVICE);
+        sm.onActiveDeviceChanged(DEVICE1, false);
+        sm.onActiveDeviceChanged(HEARING_AID_DEVICE, true);
+        executeRoutingAction(sm, BluetoothRouteManager.BT_AUDIO_LOST, DEVICE1.getAddress());
+
+        verifyConnectionAttempt(HEARING_AID_DEVICE, 0);
+        verifyConnectionAttempt(DEVICE1, 0);
+        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+                        + ":" + HEARING_AID_DEVICE.getAddress(),
+                sm.getCurrentState().getName());
+        sm.quitNow();
+    }
+
+    @SmallTest
+    @Test
     public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
-        setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(0L);
         when(mHeadsetProxy.connectAudio()).thenReturn(false);
@@ -127,18 +149,26 @@
         return sm;
     }
 
-    private void setupConnectedDevices(BluetoothDevice[] devices, BluetoothDevice activeDevice) {
-        when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length);
-        when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
-        when(mHeadsetProxy.getAudioState(any(BluetoothDevice.class)))
-                .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
-        when(mBluetoothHearingAid.getConnectedDevices()).thenReturn(Collections.emptyList());
-        when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(null, null));
-        if (activeDevice != null) {
-            when(mHeadsetProxy.getAudioState(eq(activeDevice)))
-                    .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
-        }
+    private void setupConnectedDevices(BluetoothDevice[] hfpDevices,
+            BluetoothDevice[] hearingAidDevices,
+            BluetoothDevice hfpActiveDevice, BluetoothDevice hearingAidActiveDevice) {
+        if (hfpDevices == null) hfpDevices = new BluetoothDevice[]{};
+        if (hearingAidDevices == null) hearingAidDevices = new BluetoothDevice[]{};
+
+        when(mDeviceManager.getNumConnectedDevices()).thenReturn(
+                hfpDevices.length + hearingAidDevices.length);
+        List<BluetoothDevice> allDevices = Stream.concat(
+                Arrays.stream(hfpDevices), Arrays.stream(hearingAidDevices))
+                .collect(Collectors.toList());
+
+        when(mDeviceManager.getConnectedDevices()).thenReturn(allDevices);
+        when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(hfpDevices));
+        when(mHeadsetProxy.getActiveDevice()).thenReturn(hfpActiveDevice);
+
+        when(mBluetoothHearingAid.getConnectedDevices())
+                .thenReturn(Arrays.asList(hearingAidDevices));
+        when(mBluetoothHearingAid.getActiveDevices())
+                .thenReturn(Arrays.asList(hearingAidActiveDevice, null));
     }
 
     static void executeRoutingAction(BluetoothRouteManager brm, int message, String
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index f87da3c..2f68ac2 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -265,9 +265,8 @@
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = Log.createSubsession();
                 args.arg2 = mParams.initialDevice.getAddress();
+                when(mHeadsetProxy.getActiveDevice()).thenReturn(null);
                 sm.sendMessage(BluetoothRouteManager.BT_AUDIO_LOST, args);
-                when(mHeadsetProxy.getAudioState(eq(mParams.initialDevice)))
-                        .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                 return true;
             }).when(mDeviceManager).disconnectAudio();
         }
@@ -278,9 +277,14 @@
             sm.onActiveDeviceChanged(mParams.messageDevice,
                     mParams.hearingAidBtDevices.contains(mParams.messageDevice));
         } else if (mParams.messageType == BluetoothRouteManager.LOST_DEVICE) {
-            sm.onDeviceLost(mParams.messageDevice.getAddress());
             sm.onActiveDeviceChanged(null,
                     mParams.hearingAidBtDevices.contains(mParams.messageDevice));
+            if (mParams.hearingAidBtDevices.contains(mParams.messageDevice)) {
+                when(mBluetoothHearingAid.getActiveDevices()).thenReturn(Arrays.asList(null, null));
+            } else {
+                when(mHeadsetProxy.getActiveDevice()).thenReturn(null);
+            }
+            sm.onDeviceLost(mParams.messageDevice.getAddress());
         } else {
             executeRoutingAction(sm, mParams.messageType,
                     mParams.messageDevice == null ? null : mParams.messageDevice.getAddress());
@@ -335,11 +339,8 @@
         when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         when(mHeadsetProxy.getActiveDevice()).thenReturn(activeDevice);
-        when(mHeadsetProxy.getAudioState(any(BluetoothDevice.class)))
-                .thenReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
         if (audioOnDevice != null) {
-            when(mHeadsetProxy.getAudioState(eq(audioOnDevice)))
-                    .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
+            when(mHeadsetProxy.getActiveDevice()).thenReturn(audioOnDevice);
         }
     }