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

Commit 72d9e091 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Add support for onReject and onError for external hotword." into tm-dev...

Merge "Add support for onReject and onError for external hotword." into tm-dev am: 90579f32 am: 5ec4c563

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18308197



Change-Id: Ic3ec74aaaa851b3860e7bddcab1decc9a9db34e1
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents f6970011 5ec4c563
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -198,5 +198,16 @@ abstract class AbstractHotwordDetector implements HotwordDetector {
                    HotwordDetector.Callback::onError,
                    HotwordDetector.Callback::onError,
                    mCallback));
                    mCallback));
        }
        }

        @Override
        public void onRejected(@Nullable HotwordRejectedResult result) {
            if (result == null) {
                result = new HotwordRejectedResult.Builder().build();
            }
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onRejected,
                    mCallback,
                    result));
        }
    }
    }
}
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.service.voice;


import android.media.AudioFormat;
import android.media.AudioFormat;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordRejectedResult;


/**
/**
 * Callback for returning the detected result from the HotwordDetectionService.
 * Callback for returning the detected result from the HotwordDetectionService.
@@ -38,4 +39,10 @@ oneway interface IMicrophoneHotwordDetectionVoiceInteractionCallback {
     * Called when the detection fails due to an error.
     * Called when the detection fails due to an error.
     */
     */
    void onError();
    void onError();

    /**
     * Called when the detected result was not detected.
     */
    void onRejected(
        in HotwordRejectedResult hotwordRejectedResult);
}
}
+11 −0
Original line number Original line Diff line number Diff line
@@ -164,6 +164,17 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector {
                    HotwordDetector.Callback::onError,
                    HotwordDetector.Callback::onError,
                    mCallback));
                    mCallback));
        }
        }

        @Override
        public void onRejected(@Nullable HotwordRejectedResult result) {
            if (result == null) {
                result = new HotwordRejectedResult.Builder().build();
            }
            mHandler.sendMessage(obtainMessage(
                    HotwordDetector.Callback::onRejected,
                    mCallback,
                    result));
        }
    }
    }


    private static class InitializationStateListener
    private static class InitializationStateListener
+73 −42
Original line number Original line Diff line number Diff line
@@ -117,6 +117,7 @@ final class HotwordDetectionConnection {
    // TODO: These constants need to be refined.
    // TODO: These constants need to be refined.
    private static final long VALIDATION_TIMEOUT_MILLIS = 4000;
    private static final long VALIDATION_TIMEOUT_MILLIS = 4000;
    private static final long MAX_UPDATE_TIMEOUT_MILLIS = 30000;
    private static final long MAX_UPDATE_TIMEOUT_MILLIS = 30000;
    private static final long EXTERNAL_HOTWORD_CLEANUP_MILLIS = 2000;
    private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
    private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
            Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
            Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
    private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
    private static final long RESET_DEBUG_HOTWORD_LOGGING_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
@@ -852,6 +853,7 @@ final class HotwordDetectionConnection {
                    int bytesRead = source.read(buffer, 0, 1024);
                    int bytesRead = source.read(buffer, 0, 1024);


                    if (bytesRead < 0) {
                    if (bytesRead < 0) {
                        Slog.i(TAG, "Reached end of stream for external hotword");
                        break;
                        break;
                    }
                    }


@@ -862,6 +864,12 @@ final class HotwordDetectionConnection {
                }
                }
            } catch (IOException e) {
            } catch (IOException e) {
                Slog.w(TAG, "Failed supplying audio data to validator", e);
                Slog.w(TAG, "Failed supplying audio data to validator", e);

                try {
                    callback.onError();
                } catch (RemoteException ex) {
                    Slog.w(TAG, "Failed to report onError status: " + ex);
                }
            } finally {
            } finally {
                synchronized (mLock) {
                synchronized (mLock) {
                    mCurrentAudioSink = null;
                    mCurrentAudioSink = null;
@@ -872,7 +880,8 @@ final class HotwordDetectionConnection {
        // TODO: handle cancellations well
        // TODO: handle cancellations well
        // TODO: what if we cancelled and started a new one?
        // TODO: what if we cancelled and started a new one?
        mRemoteHotwordDetectionService.run(
        mRemoteHotwordDetectionService.run(
                service -> service.detectFromMicrophoneSource(
                service -> {
                    service.detectFromMicrophoneSource(
                            serviceAudioSource,
                            serviceAudioSource,
                            // TODO: consider making a proxy callback + copy of audio format
                            // TODO: consider making a proxy callback + copy of audio format
                            AUDIO_SOURCE_EXTERNAL,
                            AUDIO_SOURCE_EXTERNAL,
@@ -882,41 +891,57 @@ final class HotwordDetectionConnection {
                                @Override
                                @Override
                                public void onRejected(HotwordRejectedResult result)
                                public void onRejected(HotwordRejectedResult result)
                                        throws RemoteException {
                                        throws RemoteException {
                                bestEffortClose(serviceAudioSink);
                                    mScheduledExecutorService.schedule(
                                bestEffortClose(serviceAudioSource);
                                            () -> {
                                bestEffortClose(audioSource);
                                                bestEffortClose(serviceAudioSink, audioSource);
                                            },
                                            EXTERNAL_HOTWORD_CLEANUP_MILLIS,
                                            TimeUnit.MILLISECONDS);


                                if (mDebugHotwordLogging && result != null) {
                                    callback.onRejected(result);
                                    Slog.i(TAG, "Egressed rejected result: " + result);

                                    if (result != null) {
                                        Slog.i(TAG, "Egressed 'hotword rejected result' "
                                                + "from hotword trusted process");
                                        if (mDebugHotwordLogging) {
                                            Slog.i(TAG, "Egressed detected result: " + result);
                                        }
                                    }
                                    }
                                // TODO: Propagate the HotwordRejectedResult.
                                }
                                }


                                @Override
                                @Override
                                public void onDetected(HotwordDetectedResult triggerResult)
                                public void onDetected(HotwordDetectedResult triggerResult)
                                        throws RemoteException {
                                        throws RemoteException {
                                bestEffortClose(serviceAudioSink);
                                    mScheduledExecutorService.schedule(
                                bestEffortClose(serviceAudioSource);
                                            () -> {
                                                bestEffortClose(serviceAudioSink, audioSource);
                                            },
                                            EXTERNAL_HOTWORD_CLEANUP_MILLIS,
                                            TimeUnit.MILLISECONDS);

                                    try {
                                    try {
                                        enforcePermissionsForDataDelivery();
                                        enforcePermissionsForDataDelivery();
                                    } catch (SecurityException e) {
                                    } catch (SecurityException e) {
                                    bestEffortClose(audioSource);
                                        callback.onError();
                                        callback.onError();
                                        return;
                                        return;
                                    }
                                    }
                                    callback.onDetected(triggerResult, null /* audioFormat */,
                                    callback.onDetected(triggerResult, null /* audioFormat */,
                                            null /* audioStream */);
                                            null /* audioStream */);
                                    if (triggerResult != null) {
                                    if (triggerResult != null) {
                                    Slog.i(TAG, "Egressed " + HotwordDetectedResult.getUsageSize(
                                        Slog.i(TAG, "Egressed "
                                            triggerResult) + " bits from hotword trusted process");
                                                + HotwordDetectedResult.getUsageSize(triggerResult)
                                                + " bits from hotword trusted process");
                                        if (mDebugHotwordLogging) {
                                        if (mDebugHotwordLogging) {
                                        Slog.i(TAG, "Egressed detected result: " + triggerResult);
                                            Slog.i(TAG,
                                                    "Egressed detected result: " + triggerResult);
                                        }
                                        }
                                    }
                                    }
                                // TODO: Add a delay before closing.
                                bestEffortClose(audioSource);
                                }
                                }
                        }));
                            });

                    // A copy of this has been created and passed to the hotword validator
                    bestEffortClose(serviceAudioSource);
                });
    }
    }


    private class ServiceConnectionFactory {
    private class ServiceConnectionFactory {
@@ -1116,6 +1141,12 @@ final class HotwordDetectionConnection {
        });
        });
    }
    }


    private static void bestEffortClose(Closeable... closeables) {
        for (Closeable closeable : closeables) {
            bestEffortClose(closeable);
        }
    }

    private static void bestEffortClose(Closeable closeable) {
    private static void bestEffortClose(Closeable closeable) {
        try {
        try {
            closeable.close();
            closeable.close();