Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 340dbdab authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "AudioPolicyDeathTest: fix flakiness"

parents 07fc894c 39adc8aa
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2,4 +2,7 @@
<resources>
    <!-- name of the app [CHAR LIMIT=25]-->
    <string name="app_name">Audio Policy APIs Tests</string>
    <string name="capture_duration_key">captureDurationMs</string>
    <string name="callback_key">callback</string>
    <string name="status_key">result</string>
</resources>
+85 −28
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static androidx.test.core.app.ApplicationProvider.getApplicationContext;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeTrue;

import android.content.BroadcastReceiver;
import android.content.Context;
@@ -30,6 +32,8 @@ import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.platform.test.annotations.Presubmit;
import android.util.Log;

@@ -39,33 +43,62 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Presubmit
@RunWith(AndroidJUnit4.class)
public class AudioPolicyDeathTest {
    private static final String TAG = "AudioPolicyDeathTest";

    private static final int SAMPLE_RATE = 48000;
    private static final int PLAYBACK_TIME_MS = 2000;
    private static final int PLAYBACK_TIME_MS = 4000;
    private static final int RECORD_TIME_MS = 1000;
    private static final int ACTIVITY_TIMEOUT_SEC = 5;
    private static final int BROADCAST_TIMEOUT_SEC = 10;
    private static final int MAX_ATTEMPTS = 5;
    private static final int DELAY_BETWEEN_ATTEMPTS_MS = 2000;

    private static final IntentFilter AUDIO_NOISY_INTENT_FILTER =
            new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

    private class MyBroadcastReceiver extends BroadcastReceiver {
        private boolean mReceived = false;
        private CountDownLatch mLatch = new CountDownLatch(1);

        @Override
        public void onReceive(Context context, Intent intent) {
            if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
                synchronized (this) {
                    mReceived = true;
                    notify();
                mLatch.countDown();
            }
        }

        public void reset() {
            mLatch = new CountDownLatch(1);
        }

        public synchronized boolean received() {
            return mReceived;
        public boolean waitForBroadcast() {
            boolean received = false;
            long startTimeMs = System.currentTimeMillis();
            long elapsedTimeMs = 0;

            Log.i(TAG, "waiting for broadcast");

            while (elapsedTimeMs < BROADCAST_TIMEOUT_SEC && !received) {
                try {
                    received = mLatch.await(BROADCAST_TIMEOUT_SEC, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    Log.w(TAG, "wait interrupted");
                }
                elapsedTimeMs = System.currentTimeMillis() - startTimeMs;
            }
            Log.i(TAG, "broadcast " + (received ? "" : "NOT ") + "received");
            return received;
        }
    }

    private final MyBroadcastReceiver mReceiver = new MyBroadcastReceiver();

    private Context mContext;
@@ -85,31 +118,55 @@ public class AudioPolicyDeathTest {
    public void testPolicyClientDeathSendBecomingNoisyIntent() {
        mContext.registerReceiver(mReceiver, AUDIO_NOISY_INTENT_FILTER);

        // Launch process registering a dynamic auido policy and dying after PLAYBACK_TIME_MS/2 ms
        boolean result = false;
        for (int numAttempts = 1; numAttempts <= MAX_ATTEMPTS && !result; numAttempts++) {
            mReceiver.reset();

            CompletableFuture<Integer> callbackReturn = new CompletableFuture<>();
            RemoteCallback cb = new RemoteCallback((Bundle res) -> {
                callbackReturn.complete(
                        res.getInt(mContext.getResources().getString(R.string.status_key)));
            });

            // Launch process registering a dynamic auido policy and dying after RECORD_TIME_MS ms
            // RECORD_TIME_MS must be shorter than PLAYBACK_TIME_MS
            Intent intent = new Intent(mContext, AudioPolicyDeathTestActivity.class);
        intent.putExtra("captureDurationMs", PLAYBACK_TIME_MS / 2);
            intent.putExtra(mContext.getResources().getString(R.string.capture_duration_key),
                    RECORD_TIME_MS);
            intent.putExtra(mContext.getResources().getString(R.string.callback_key), cb);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

            mContext.startActivity(intent);

        AudioTrack track = createAudioTrack();
            Integer status = AudioManager.ERROR;
            try {
                status = callbackReturn.get(ACTIVITY_TIMEOUT_SEC, TimeUnit.SECONDS);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                assumeNoException(e);
            }
            assumeTrue(status != null && status == AudioManager.SUCCESS);

            Log.i(TAG, "Activity started");
            AudioTrack track = null;
            try {
                track = createAudioTrack();
                track.play();
        synchronized (mReceiver) {
            long startTimeMs = System.currentTimeMillis();
            long elapsedTimeMs = 0;
            while (elapsedTimeMs < PLAYBACK_TIME_MS && !mReceiver.received()) {
                result = mReceiver.waitForBroadcast();
            } finally {
                if (track != null) {
                    track.stop();
                    track.release();
                }
            }
            if (!result) {
                try {
                    mReceiver.wait(PLAYBACK_TIME_MS - elapsedTimeMs);
                    Log.i(TAG, "Retrying after attempt: " + numAttempts);
                    Thread.sleep(DELAY_BETWEEN_ATTEMPTS_MS);
                } catch (InterruptedException e) {
                    Log.w(TAG, "wait interrupted");
                }
                elapsedTimeMs = System.currentTimeMillis() - startTimeMs;
            }
        }

        track.stop();
        track.release();

        assertTrue(mReceiver.received());
        assertTrue(result);
    }

    private AudioTrack createAudioTrack() {
+21 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.media.audiopolicy.AudioMixingRule;
import android.media.audiopolicy.AudioPolicy;
import android.os.Bundle;
import android.os.Looper;
import android.os.RemoteCallback;
import android.util.Log;

// This activity will register a dynamic audio policy to intercept media playback and launch
@@ -71,19 +72,29 @@ public class AudioPolicyDeathTestActivity extends Activity {
        mAudioPolicy = audioPolicyBuilder.build();

        int result = mAudioManager.registerAudioPolicy(mAudioPolicy);
        if (result != AudioManager.SUCCESS) {
            Log.w(TAG, "registerAudioPolicy failed, status: " + result);
            return;
        }
        if (result == AudioManager.SUCCESS) {
            AudioRecord audioRecord = mAudioPolicy.createAudioRecordSink(audioMix);
        if (audioRecord == null) {
            if (audioRecord != null && audioRecord.getState() != AudioRecord.STATE_UNINITIALIZED) {
                int captureDurationMs = getIntent().getIntExtra(
                                getString(R.string.capture_duration_key), RECORD_TIME_MS);
                AudioCapturingThread thread =
                        new AudioCapturingThread(audioRecord, captureDurationMs);
                thread.start();
            } else {
                Log.w(TAG, "AudioRecord creation failed");
            return;
                result = AudioManager.ERROR_NO_INIT;
            }
        } else {
            Log.w(TAG, "registerAudioPolicy failed, status: " + result);
        }

        int captureDurationMs = getIntent().getIntExtra("captureDurationMs", RECORD_TIME_MS);
        AudioCapturingThread thread = new AudioCapturingThread(audioRecord, captureDurationMs);
        thread.start();
        RemoteCallback cb =
                (RemoteCallback) getIntent().getExtras().get(getString(R.string.callback_key));
        Bundle res = new Bundle();
        res.putInt(getString(R.string.status_key), result);
        Log.i(TAG, "policy " + (result ==  AudioManager.SUCCESS ? "" : "un")
                + "successfully registered");
        cb.sendResult(res);
    }

    @Override