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

Commit 044ca5ad authored by lpeter's avatar lpeter
Browse files

Close audio source after getting callback from HotwordDetectionService

Due to the file descriptor of audio source was closed before
the attempt to parcel the file descriptor when passing the
file descriptor to the HotwordDetectionService.

It will cause the exception below:
java.lang.RuntimeException: Bad file descriptor

It would be better to close audio source when getting
callback from HotwordDetectionService

Bug: 185569471
Test: atest CtsVoiceInteractionTestCases
Test: atest CtsVoiceInteractionTestCases --instant
Change-Id: I10774fd29d559b5d2bedc21b0ebb2ef849a1b7c6
parent 8fbe605e
Loading
Loading
Loading
Loading
+22 −67
Original line number Diff line number Diff line
@@ -270,14 +270,9 @@ final class HotwordDetectionConnection {
        if (DEBUG) {
            Slog.d(TAG, "startListeningFromExternalSource");
        }

        ParcelFileDescriptor.AutoCloseInputStream audioReader =
                new ParcelFileDescriptor.AutoCloseInputStream(audioStream);

        handleSoftwareHotwordDetection(
        handleExternalSourceHotwordDetection(
                audioStream,
                audioFormat,
                AudioReader.createFromInputStream(audioReader),
                AUDIO_SOURCE_EXTERNAL,
                options,
                callback);
    }
@@ -556,14 +551,18 @@ final class HotwordDetectionConnection {
        }
    }

    private void handleSoftwareHotwordDetection(
    private void handleExternalSourceHotwordDetection(
            ParcelFileDescriptor audioStream,
            AudioFormat audioFormat,
            AudioReader audioSource,
            int audioSourceValue,
            @Nullable PersistableBundle options,
            IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
        Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
        if (DEBUG) {
            Slog.d(TAG, "#handleExternalSourceHotwordDetection");
        }
        AudioReader audioSource = AudioReader.createFromInputStream(
                new ParcelFileDescriptor.AutoCloseInputStream(audioStream));

        Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
        if (clientPipe == null) {
            // TODO: Need to propagate as unknown error or something?
            return;
@@ -579,8 +578,8 @@ final class HotwordDetectionConnection {
            try (AudioReader source = audioSource;
                 OutputStream fos =
                         new ParcelFileDescriptor.AutoCloseOutputStream(serviceAudioSink)) {
                byte[] buffer = new byte[1024];

                byte[] buffer = new byte[1024];
                while (true) {
                    int bytesRead = source.read(buffer, 0, 1024);

@@ -608,76 +607,32 @@ final class HotwordDetectionConnection {
                service -> service.detectFromMicrophoneSource(
                        serviceAudioSource,
                        // TODO: consider making a proxy callback + copy of audio format
                        audioSourceValue, audioFormat, options,
                        AUDIO_SOURCE_EXTERNAL,
                        audioFormat,
                        options,
                        new IDspHotwordDetectionCallback.Stub() {
                            @Override
                            public void onRejected(@Nullable HotwordRejectedResult result)
                                    throws RemoteException {
                                bestEffortClose(serviceAudioSink);
                                bestEffortClose(serviceAudioSource);
                                bestEffortClose(audioSource);

                                // TODO: Propagate the HotwordRejectedResult.
                            }

                            @Override
                            public void onDetected(@Nullable HotwordDetectedResult triggerResult)
                                    throws RemoteException {
                                // Stop
                                bestEffortClose(serviceAudioSink);

                                if (audioSourceValue == AUDIO_SOURCE_EXTERNAL) {
                                    callback.onDetected(triggerResult, null, null);
                                    // TODO: Add a delay before closing.
                                    bestEffortClose(audioSource);
                                }

                                Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe =
                                        createPipe();

                                if (clientPipe == null) {
                                    // Error.
                                    // Need to propagate as unknown error or something?
                                    return;
                                }
                                ParcelFileDescriptor callbackAudioSink = clientPipe.second;
                                ParcelFileDescriptor callbackClientRead = clientPipe.first;

                                mAudioCopyExecutor.execute(() -> {
                                    try (AudioReader source = audioSource;
                                         OutputStream fos =
                                                 new ParcelFileDescriptor.AutoCloseOutputStream(
                                                         callbackAudioSink)) {

                                        // TODO: get ring buffer suffix here
                                        // fos.write(lastSeveralSeconds);

                                        byte[] buffer = new byte[1024];
                                        while (true) {
                                            int bytesRead = source.read(buffer, 0, 1024);

                                            if (bytesRead < 0) {
                                                break;
                                            }

                                            fos.write(buffer, 0, bytesRead);
                                        }
                                    } catch (IOException e) {
                                        Slog.w(TAG, "Failed supplying audio data to validator", e);
                                    } finally {
                                        synchronized (mLock) {
                                            mCurrentAudioSink = null;
                                        }
                                    }
                                });

                                bestEffortClose(serviceAudioSource);
                                // TODO: noteOp here.
                                // Remove hotword offset from trigger result
                                // TODO: consider propagating only capture session.
                                callback.onDetected(triggerResult, null, callbackClientRead);
                                callback.onDetected(triggerResult, null /* audioFormat */,
                                        null /* audioStream */);
                                // TODO: Add a delay before closing.
                                bestEffortClose(callbackClientRead);
                                bestEffortClose(audioSource);
                            }
                        }));

        // TODO: verify this is the right thing to do here.
        bestEffortClose(serviceAudioSource);
    }

    private static void bestEffortClose(Closeable closeable) {
+5 −0
Original line number Diff line number Diff line
@@ -477,6 +477,11 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
            return;
        }

        if (audioStream == null) {
            Slog.w(TAG, "External source is null for hotword detector");
            throw new IllegalStateException("External source is null for hotword detector");
        }

        mHotwordDetectionConnection
                .startListeningFromExternalSource(audioStream, audioFormat, options, callback);
    }