Add ability to generate remote call recording tone.
am: 5d66e1d540

Change-Id: Id0333c08abc76ad6b7d12af827ec378686694631
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4f1cb8b..2e3bc37 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -36,6 +36,8 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <!-- Required to determine source of ongoing audio recordings. -->
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CALL_LOG" />
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
diff --git a/res/raw/record.ogg b/res/raw/record.ogg
new file mode 100644
index 0000000..a023e6d
--- /dev/null
+++ b/res/raw/record.ogg
Binary files differ
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index c682848..420b71d 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -434,6 +434,12 @@
 
     private boolean mIsWorkCall;
 
+    /**
+     * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has
+     * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set.
+     */
+    private boolean mUseCallRecordingTone;
+
     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
 
@@ -1077,7 +1083,7 @@
             for (Listener l : mListeners) {
                 l.onTargetPhoneAccountChanged(this);
             }
-            configureIsWorkCall();
+            configureCallAttributes();
         }
         checkIfVideoCapable();
     }
@@ -1134,6 +1140,10 @@
         return mIsWorkCall;
     }
 
+    public boolean isUsingCallRecordingTone() {
+        return mUseCallRecordingTone;
+    }
+
     public boolean isVideoCallingSupported() {
         return mIsVideoCallingSupported;
     }
@@ -1201,9 +1211,10 @@
         return mHandoverState;
     }
 
-    private void configureIsWorkCall() {
+    private void configureCallAttributes() {
         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
         boolean isWorkCall = false;
+        boolean isCallRecordingToneSupported = false;
         PhoneAccount phoneAccount =
                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
         if (phoneAccount != null) {
@@ -1216,8 +1227,14 @@
             if (userHandle != null) {
                 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
             }
+
+            isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
+                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
+                    && phoneAccount.getExtras().getBoolean(
+                    PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
         }
         mIsWorkCall = isWorkCall;
+        mUseCallRecordingTone = isCallRecordingToneSupported;
     }
 
     /**
diff --git a/src/com/android/server/telecom/CallRecordingTonePlayer.java b/src/com/android/server/telecom/CallRecordingTonePlayer.java
new file mode 100644
index 0000000..ffa1b04
--- /dev/null
+++ b/src/com/android/server/telecom/CallRecordingTonePlayer.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2018 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.server.telecom;
+
+import android.content.Context;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioRecordingConfiguration;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.Looper;
+import android.telecom.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Plays a periodic, repeating tone to the remote party when an app on the device is recording
+ * a call.  A call recording tone is played on the called party's audio if an app begins recording.
+ * This ensures that the remote party is aware of the fact call recording is in progress.
+ */
+public class CallRecordingTonePlayer extends CallsManagerListenerBase {
+    /**
+     * Callback registered with {@link AudioManager} to track apps which are recording audio.
+     * Registered when a SIM call is added and unregistered when it ends.
+     */
+    private AudioManager.AudioRecordingCallback mAudioRecordingCallback =
+            new AudioManager.AudioRecordingCallback() {
+                @Override
+                public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
+                    synchronized (mLock) {
+                        try {
+                            Log.startSession("CRTP.oRCC");
+                            handleRecordingConfigurationChange(configs);
+                            maybeStartCallAudioTone();
+                            maybeStopCallAudioTone();
+                        } finally {
+                            Log.endSession();
+                        }
+                    }
+                }
+    };
+
+    private final AudioManager mAudioManager;
+    private final Context mContext;
+    private final TelecomSystem.SyncRoot mLock;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private boolean mIsRecording = false;
+    private MediaPlayer mRecordingTonePlayer = null;
+    private List<Call> mCalls = new ArrayList<>();
+
+    public CallRecordingTonePlayer(Context context, AudioManager audioManager,
+            TelecomSystem.SyncRoot lock) {
+        mContext = context;
+        mAudioManager = audioManager;
+        mLock = lock;
+    }
+
+    @Override
+    public void onCallAdded(Call call) {
+        if (!shouldUseRecordingTone(call)) {
+            return; // Ignore calls which don't use the recording tone.
+        }
+
+        addCall(call);
+    }
+
+    @Override
+    public void onCallRemoved(Call call) {
+        if (!shouldUseRecordingTone(call)) {
+            return; // Ignore calls which don't use the recording tone.
+        }
+
+        removeCall(call);
+    }
+
+    @Override
+    public void onCallStateChanged(Call call, int oldState, int newState) {
+        if (!shouldUseRecordingTone(call)) {
+            return; // Ignore calls which don't use the recording tone.
+        }
+
+        if (mIsRecording) {
+            // Handle start and stop now; could be stopping if we held a call.
+            maybeStartCallAudioTone();
+            maybeStopCallAudioTone();
+        }
+    }
+
+    /**
+     * Handles addition of a new call by:
+     * 1. Registering an audio manager listener to track changes to recording state.
+     * 2. Checking if there is recording in progress.
+     * 3. Potentially starting the call recording tone.
+     *
+     * @param toAdd The call to start tracking.
+     */
+    private void addCall(Call toAdd) {
+        boolean isFirstCall = mCalls.isEmpty();
+
+        mCalls.add(toAdd);
+        if (isFirstCall) {
+            // First call, so register the recording callback.  Also check for recordings which
+            // started before we registered the callback (we don't receive a callback for those).
+            handleRecordingConfigurationChange(mAudioManager.getActiveRecordingConfigurations());
+            mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback,
+                    mMainThreadHandler);
+        }
+
+        maybeStartCallAudioTone();
+    }
+
+    /**
+     * Handles removal of tracked call by unregistering the audio recording callback and stopping
+     * the recording tone if this is the last call.
+     * @param toRemove The call to stop tracking.
+     */
+    private void removeCall(Call toRemove) {
+        mCalls.remove(toRemove);
+        boolean isLastCall = mCalls.isEmpty();
+
+        if (isLastCall) {
+            mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+            maybeStopCallAudioTone();
+        }
+    }
+
+    /**
+     * Determines whether a call is applicable for call recording tone generation.
+     * Only top level sim calls are considered which have
+     * {@link android.telecom.PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set on their target
+     * {@link android.telecom.PhoneAccount}.
+     * @param call The call to check.
+     * @return {@code true} if the call is should use the recording tone, {@code false} otherwise.
+     */
+    private boolean shouldUseRecordingTone(Call call) {
+        return call.getParentCall() == null && !call.isExternalCall() &&
+                !call.isEmergencyCall() && call.isUsingCallRecordingTone();
+    }
+
+    /**
+     * Starts the call recording tone if recording has started and there are calls.
+     */
+    private void maybeStartCallAudioTone() {
+        if (mIsRecording && hasActiveCall()) {
+            startCallRecordingTone(mContext);
+        }
+    }
+
+    /**
+     * Stops the call recording tone if recording has stopped or there are no longer any calls.
+     */
+    private void maybeStopCallAudioTone() {
+        if (!mIsRecording || !hasActiveCall()) {
+            stopCallRecordingTone();
+        }
+    }
+
+    /**
+     * Determines if any of the calls tracked are active.
+     * @return {@code true} if there is an active call, {@code false} otherwise.
+     */
+    private boolean hasActiveCall() {
+        return !mCalls.isEmpty() && mCalls.stream()
+                .filter(call -> call.isActive())
+                .count() > 0;
+    }
+
+    /**
+     * Handles changes to recording configuration changes.
+     * @param configs the recording configurations.
+     */
+    private void handleRecordingConfigurationChange(List<AudioRecordingConfiguration> configs) {
+        if (configs == null) {
+            configs = Collections.emptyList();
+        }
+        boolean wasRecording = mIsRecording;
+        boolean isRecording = isRecordingInProgress(configs);
+        if (wasRecording != isRecording) {
+            mIsRecording = isRecording;
+            if (isRecording) {
+                Log.i(this, "handleRecordingConfigurationChange: recording started");
+            } else {
+                Log.i(this, "handleRecordingConfigurationChange: recording stopped");
+            }
+        }
+    }
+
+    /**
+     * Determines if call recording is potentially in progress.
+     * Excludes from consideration any recordings from packages which have active calls themselves.
+     * Presumably a call with an active recording session is doing so in order to capture the audio
+     * for the purpose of making a call.  In practice Telephony calls don't show up in the
+     * recording configurations, but it is reasonable to consider Connection Managers which are
+     * using an over the top voip solution for calling.
+     * @param configs the ongoing recording configurations.
+     * @return {@code true} if there are active audio recordings for which we want to generate a
+     * call recording tone, {@code false} otherwise.
+     */
+    private boolean isRecordingInProgress(List<AudioRecordingConfiguration> configs) {
+        String recordingPackages = configs.stream()
+                .map(config -> config.getClientPackageName())
+                .collect(Collectors.joining(", "));
+        Log.i(this, "isRecordingInProgress: recordingPackages=%s", recordingPackages);
+        return configs.stream()
+                .filter(config -> !hasCallForPackage(config.getClientPackageName()))
+                .count() > 0;
+    }
+
+    /**
+     * Begins playing the call recording tone to the remote end of the call.
+     * The call recording tone is played via the telephony audio output device; this means that it
+     * will only be audible to the remote end of the call, not the local side.
+     *
+     * @param context required for obtaining media player.
+     */
+    private void startCallRecordingTone(Context context) {
+        if (mRecordingTonePlayer != null) {
+            return;
+        }
+        AudioDeviceInfo telephonyDevice = getTelephonyDevice(mAudioManager);
+        if (telephonyDevice != null) {
+            Log.i(this ,"startCallRecordingTone: playing call recording tone to remote end.");
+            /**
+             TODO: uncomment this in P release; API dependencies exist which are not met in AOSP.
+            mRecordingTonePlayer = MediaPlayer.create(context, R.raw.record);
+            mRecordingTonePlayer.setLooping(true);
+            mRecordingTonePlayer.setPreferredDevice(telephonyDevice);
+            mRecordingTonePlayer.setVolume(0.1f);
+            mRecordingTonePlayer.start();
+             */
+        } else {
+            Log.w(this ,"startCallRecordingTone: can't find telephony audio device.");
+        }
+    }
+
+    /**
+     * Attempts to stop the call recording tone if it is playing.
+     */
+    private void stopCallRecordingTone() {
+        if (mRecordingTonePlayer != null) {
+            Log.i(this ,"stopCallRecordingTone: stopping call recording tone.");
+            mRecordingTonePlayer.stop();
+            mRecordingTonePlayer = null;
+        }
+    }
+
+    /**
+     * Finds the the output device of type {@link AudioDeviceInfo#TYPE_TELEPHONY}.  This device is
+     * the one on which outgoing audio for SIM calls is played.
+     * @param audioManager the audio manage.
+     * @return the {@link AudioDeviceInfo} corresponding to the telephony device, or {@code null}
+     * if none can be found.
+     */
+    private AudioDeviceInfo getTelephonyDevice(AudioManager audioManager) {
+        AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        for (AudioDeviceInfo device: deviceList) {
+            if (device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
+                return device;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Determines if any of the known calls belongs to a {@link android.telecom.PhoneAccount} with
+     * the specified package name.
+     * @param packageName The package name.
+     * @return {@code true} if a call exists for this package, {@code false} otherwise.
+     */
+    private boolean hasCallForPackage(String packageName) {
+        return mCalls.stream()
+                .filter(call -> (call.getTargetPhoneAccount() != null &&
+                        call.getTargetPhoneAccount()
+                                .getComponentName().getPackageName().equals(packageName)) ||
+                        (call.getConnectionManagerPhoneAccount() != null &&
+                                call.getConnectionManagerPhoneAccount()
+                                        .getComponentName().getPackageName().equals(packageName)))
+                .count() >= 1;
+    }
+
+    @VisibleForTesting
+    public boolean hasCalls() {
+        return mCalls.size() > 0;
+    }
+
+    @VisibleForTesting
+    public boolean isRecording() {
+        return mIsRecording;
+    }
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 4ce0ff9..82e3787 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -238,6 +238,7 @@
     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
     private final InCallController mInCallController;
     private final CallAudioManager mCallAudioManager;
+    private final CallRecordingTonePlayer mCallRecordingTonePlayer;
     private RespondViaSmsManager mRespondViaSmsManager;
     private final Ringer mRinger;
     private final InCallWakeLockController mInCallWakeLockController;
@@ -386,7 +387,8 @@
                 emergencyCallHelper);
         mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
                 ringtoneFactory, systemVibrator, mInCallController);
-
+        mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext,
+                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), mLock);
         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                 this,new CallAudioModeStateMachine((AudioManager)
                         mContext.getSystemService(Context.AUDIO_SERVICE)),
@@ -394,7 +396,6 @@
 
         mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
                 mRequester, Looper.getMainLooper());
-
         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
@@ -411,6 +412,7 @@
         mListeners.add(mPhoneStateBroadcaster);
         mListeners.add(mInCallController);
         mListeners.add(mCallAudioManager);
+        mListeners.add(mCallRecordingTonePlayer);
         mListeners.add(missedCallNotifier);
         mListeners.add(mHeadsetMediaButton);
         mListeners.add(mProximitySensorManager);
diff --git a/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
new file mode 100644
index 0000000..eca374b
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2018 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.server.telecom.tests;
+
+import static com.android.server.telecom.tests.TelecomSystemTest.TEST_TIMEOUT;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecordingConfiguration;
+import android.media.MediaRecorder;
+import android.os.Handler;
+import android.os.Looper;
+import android.telecom.PhoneAccountHandle;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallRecordingTonePlayer;
+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.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for the {@link com.android.server.telecom.CallRecordingTonePlayer} class.
+ */
+@RunWith(JUnit4.class)
+public class CallRecordingTonePlayerTest extends TelecomTestCase {
+
+    private static final String PHONE_ACCOUNT_PACKAGE = "com.android.telecom.test";
+    private static final String PHONE_ACCOUNT_CLASS = "MyFancyConnectionService";
+    private static final String PHONE_ACCOUNT_ID = "1";
+    private static final String RECORDING_APP_PACKAGE = "com.recording.app";
+
+    private static final PhoneAccountHandle TEST_PHONE_ACCOUNT = new PhoneAccountHandle(
+            new ComponentName(PHONE_ACCOUNT_PACKAGE, PHONE_ACCOUNT_CLASS), PHONE_ACCOUNT_ID);
+
+    private CallRecordingTonePlayer mCallRecordingTonePlayer;
+    private TelecomSystem.SyncRoot mSyncRoot = new TelecomSystem.SyncRoot() { };
+    @Mock private AudioManager mAudioManager;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mCallRecordingTonePlayer = new CallRecordingTonePlayer(
+                mComponentContextFixture.getTestDouble().getApplicationContext(),
+                mAudioManager, mSyncRoot);
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn(null);
+    }
+
+    /**
+     * Ensures that child calls are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testChildCall() {
+        Call childCall = Mockito.mock(Call.class);
+        Call parentcall = Mockito.mock(Call.class);
+        when(childCall.getParentCall()).thenReturn(parentcall);
+        mCallRecordingTonePlayer.onCallAdded(childCall);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that external calls are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddExternalCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(true);
+        mCallRecordingTonePlayer.onCallAdded(call);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that emergency calls are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddEmergencyCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(false);
+        when(call.isEmergencyCall()).thenReturn(true);
+        mCallRecordingTonePlayer.onCallAdded(call);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that calls which don't use the recording tone are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddIneligibleCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(false);
+        when(call.isEmergencyCall()).thenReturn(false);
+        when(call.isUsingCallRecordingTone()).thenReturn(false);
+        mCallRecordingTonePlayer.onCallAdded(call);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that an eligible call is tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddEligibleCall() {
+        Call call = addValidCall();
+
+        mCallRecordingTonePlayer.onCallRemoved(call);
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Verifies registration and unregistration of the recording callback.
+     */
+    @MediumTest
+    @Test
+    public void testRecordingCallbackRegistered() {
+        Call call = addValidCall();
+
+        // Ensure we got a request for the first set of recordings.
+        verify(mAudioManager).getActiveRecordingConfigurations();
+
+        // Ensure that we registered an audio recording callback.
+        verify(mAudioManager).registerAudioRecordingCallback(
+                any(AudioManager.AudioRecordingCallback.class), any());
+
+        mCallRecordingTonePlayer.onCallRemoved(call);
+
+        // Ensure we unregistered the audio recording callback after the last call was removed.
+        verify(mAudioManager).unregisterAudioRecordingCallback(
+                any(AudioManager.AudioRecordingCallback.class));
+    }
+
+    /**
+     * Verify that we are in a recording state when we add a call and there is a recording taking
+     * place prior to the call starting.
+     */
+    @MediumTest
+    @Test
+    public void testIsRecordingInitial() {
+        // Return an active recording configuration when we add the first call.
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn(
+                getAudioRecordingConfig(RECORDING_APP_PACKAGE));
+
+        addValidCall();
+
+        // Ensure we got a request for the first set of recordings.
+        verify(mAudioManager).getActiveRecordingConfigurations();
+
+        assertTrue(mCallRecordingTonePlayer.isRecording());
+    }
+
+    /**
+     * Verify that we are in a recording state when we add a call and a recording start after the
+     * call starts.
+     */
+    @MediumTest
+    @Test
+    public void testIsRecordingLater() {
+        // Return no active recording configuration when we add the first call.
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn( null);
+
+        addValidCall();
+
+        // Capture the registered callback so we can pass back test data via it.
+        ArgumentCaptor<AudioManager.AudioRecordingCallback> callbackCaptor =
+                ArgumentCaptor.forClass(AudioManager.AudioRecordingCallback.class);
+        verify(mAudioManager).registerAudioRecordingCallback(callbackCaptor.capture(), any());
+
+        // Pass back some test configuration data.
+        callbackCaptor.getValue().onRecordingConfigChanged(getAudioRecordingConfig(
+                RECORDING_APP_PACKAGE));
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        assertTrue(mCallRecordingTonePlayer.isRecording());
+    }
+
+    /**
+     * Verifies that we are not in a recording state if the PhoneAccount associated with the call is
+     * the recording app.
+     */
+    @MediumTest
+    @Test
+    public void testNotRecordingApp() {
+        // Return no active recording configuration when we add the first call.
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn( null);
+
+        addValidCall();
+
+        // Capture the registered callback so we can pass back test data via it.
+        ArgumentCaptor<AudioManager.AudioRecordingCallback> callbackCaptor =
+                ArgumentCaptor.forClass(AudioManager.AudioRecordingCallback.class);
+        verify(mAudioManager).registerAudioRecordingCallback(callbackCaptor.capture(), any());
+
+        // Report that the recording app is the call's phone account.
+        callbackCaptor.getValue().onRecordingConfigChanged(getAudioRecordingConfig(
+                PHONE_ACCOUNT_PACKAGE));
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        // Since the app which is recording is the phone account of the call, we should not be in
+        // a recording state.
+        assertFalse(mCallRecordingTonePlayer.isRecording());
+    }
+
+    /**
+     * @return Test audio recording configuration.
+     */
+    private List<AudioRecordingConfiguration> getAudioRecordingConfig(String packageName) {
+        List<AudioRecordingConfiguration> configs = new ArrayList<>();
+        configs.add(new AudioRecordingConfiguration(0, 0, MediaRecorder.AudioSource.MIC,
+                new AudioFormat.Builder().build(), new AudioFormat.Builder().build(),
+                0, packageName));
+        return configs;
+    }
+
+    private Call addValidCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(false);
+        when(call.isEmergencyCall()).thenReturn(false);
+        when(call.isUsingCallRecordingTone()).thenReturn(true);
+        when(call.getConnectionManagerPhoneAccount()).thenReturn(null);
+        when(call.getTargetPhoneAccount()).thenReturn(TEST_PHONE_ACCOUNT);
+        mCallRecordingTonePlayer.onCallAdded(call);
+        assertTrue(mCallRecordingTonePlayer.hasCalls());
+        return call;
+    }
+
+
+}