Add initialAudio field to HotwordAudioStream

Bug: 262433819
Test: atest HotwordAudioStreamTest
Change-Id: Ic95b701409656034a31724d50fb8bebf555cdab4
This commit is contained in:
Mark Punzalan
2022-12-15 11:48:16 +00:00
parent 8e2fe2566d
commit 72e19eb853
2 changed files with 140 additions and 18 deletions

View File

@@ -12326,6 +12326,7 @@ package android.service.voice {
method public int describeContents(); method public int describeContents();
method @NonNull public android.media.AudioFormat getAudioFormat(); method @NonNull public android.media.AudioFormat getAudioFormat();
method @NonNull public android.os.ParcelFileDescriptor getAudioStreamParcelFileDescriptor(); method @NonNull public android.os.ParcelFileDescriptor getAudioStreamParcelFileDescriptor();
method @NonNull public byte[] getInitialAudio();
method @NonNull public android.os.PersistableBundle getMetadata(); method @NonNull public android.os.PersistableBundle getMetadata();
method @Nullable public android.media.AudioTimestamp getTimestamp(); method @Nullable public android.media.AudioTimestamp getTimestamp();
method public void writeToParcel(@NonNull android.os.Parcel, int); method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -12338,6 +12339,7 @@ package android.service.voice {
method @NonNull public android.service.voice.HotwordAudioStream build(); method @NonNull public android.service.voice.HotwordAudioStream build();
method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioFormat(@NonNull android.media.AudioFormat); method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioFormat(@NonNull android.media.AudioFormat);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioStreamParcelFileDescriptor(@NonNull android.os.ParcelFileDescriptor); method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioStreamParcelFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setInitialAudio(@NonNull byte[]);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setMetadata(@NonNull android.os.PersistableBundle); method @NonNull public android.service.voice.HotwordAudioStream.Builder setMetadata(@NonNull android.os.PersistableBundle);
method @NonNull public android.service.voice.HotwordAudioStream.Builder setTimestamp(@NonNull android.media.AudioTimestamp); method @NonNull public android.service.voice.HotwordAudioStream.Builder setTimestamp(@NonNull android.media.AudioTimestamp);
} }

View File

@@ -29,6 +29,7 @@ import android.os.PersistableBundle;
import com.android.internal.util.DataClass; import com.android.internal.util.DataClass;
import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -68,8 +69,17 @@ public final class HotwordAudioStream implements Parcelable {
private final AudioFormat mAudioFormat; private final AudioFormat mAudioFormat;
/** /**
* This stream starts with the audio bytes used for hotword detection, but continues streaming * This stream typically starts with the audio bytes used for hotword detection, but continues
* the audio until the stream is shutdown by the {@link HotwordDetectionService}. * streaming the audio (e.g., with the query) until the stream is shutdown by the
* {@link HotwordDetectionService}. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
* to pass the start of the audio instead of streaming it here. This may prevent added latency
* caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
* being large enough to handle this initial chunk of audio.
* </p>
*/ */
@NonNull @NonNull
private final ParcelFileDescriptor mAudioStreamParcelFileDescriptor; private final ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
@@ -107,6 +117,29 @@ public final class HotwordAudioStream implements Parcelable {
return new PersistableBundle(); return new PersistableBundle();
} }
/**
* The start of the audio used for hotword detection. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* The {@link HotwordDetectionService} may use this instead of using
* {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. This
* may prevent added latency caused by the streaming buffer (see
* {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this
* initial chunk of audio.
* </p>
*/
@NonNull
private final byte[] mInitialAudio;
private static final byte[] DEFAULT_INITIAL_EMPTY_AUDIO = {};
private static byte[] defaultInitialAudio() {
return DEFAULT_INITIAL_EMPTY_AUDIO;
}
private String initialAudioToString() {
return "length=" + mInitialAudio.length;
}
/** /**
* Provides an instance of {@link Builder} with state corresponding to this instance. * Provides an instance of {@link Builder} with state corresponding to this instance.
* @hide * @hide
@@ -114,7 +147,33 @@ public final class HotwordAudioStream implements Parcelable {
public Builder buildUpon() { public Builder buildUpon() {
return new Builder(mAudioFormat, mAudioStreamParcelFileDescriptor) return new Builder(mAudioFormat, mAudioStreamParcelFileDescriptor)
.setTimestamp(mTimestamp) .setTimestamp(mTimestamp)
.setMetadata(mMetadata); .setMetadata(mMetadata)
.setInitialAudio(mInitialAudio);
}
@DataClass.Suppress("setInitialAudio")
abstract static class BaseBuilder {
/**
* The start of the audio used for hotword detection. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* The {@link HotwordDetectionService} may use this instead of using
* {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio.
* This may prevent added latency caused by the streaming buffer (see
* {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this
* initial chunk of audio.
* </p>
*/
public @NonNull Builder setInitialAudio(@NonNull byte[] value) {
Objects.requireNonNull(value, "value should not be null");
final Builder builder = (Builder) this;
// If the code gen flag in build() is changed, we must update the flag e.g. 0x10 here.
builder.mBuilderFieldsSet |= 0x10;
builder.mInitialAudio = value;
return builder;
}
} }
@@ -137,7 +196,8 @@ public final class HotwordAudioStream implements Parcelable {
@NonNull AudioFormat audioFormat, @NonNull AudioFormat audioFormat,
@NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor, @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor,
@Nullable AudioTimestamp timestamp, @Nullable AudioTimestamp timestamp,
@NonNull PersistableBundle metadata) { @NonNull PersistableBundle metadata,
@NonNull byte[] initialAudio) {
this.mAudioFormat = audioFormat; this.mAudioFormat = audioFormat;
com.android.internal.util.AnnotationValidations.validate( com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mAudioFormat); NonNull.class, null, mAudioFormat);
@@ -148,6 +208,9 @@ public final class HotwordAudioStream implements Parcelable {
this.mMetadata = metadata; this.mMetadata = metadata;
com.android.internal.util.AnnotationValidations.validate( com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMetadata); NonNull.class, null, mMetadata);
this.mInitialAudio = initialAudio;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInitialAudio);
// onConstructed(); // You can define this method to get a callback // onConstructed(); // You can define this method to get a callback
} }
@@ -161,8 +224,17 @@ public final class HotwordAudioStream implements Parcelable {
} }
/** /**
* This stream starts with the audio bytes used for hotword detection, but continues streaming * This stream typically starts with the audio bytes used for hotword detection, but continues
* the audio until the stream is shutdown by the {@link HotwordDetectionService}. * streaming the audio (e.g., with the query) until the stream is shutdown by the
* {@link HotwordDetectionService}. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
* to pass the start of the audio instead of streaming it here. This may prevent added latency
* caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
* being large enough to handle this initial chunk of audio.
* </p>
*/ */
@DataClass.Generated.Member @DataClass.Generated.Member
public @NonNull ParcelFileDescriptor getAudioStreamParcelFileDescriptor() { public @NonNull ParcelFileDescriptor getAudioStreamParcelFileDescriptor() {
@@ -200,6 +272,23 @@ public final class HotwordAudioStream implements Parcelable {
return mMetadata; return mMetadata;
} }
/**
* The start of the audio used for hotword detection. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* The {@link HotwordDetectionService} may use this instead of using
* {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. This
* may prevent added latency caused by the streaming buffer (see
* {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this
* initial chunk of audio.
* </p>
*/
@DataClass.Generated.Member
public @NonNull byte[] getInitialAudio() {
return mInitialAudio;
}
@Override @Override
@DataClass.Generated.Member @DataClass.Generated.Member
public String toString() { public String toString() {
@@ -210,7 +299,8 @@ public final class HotwordAudioStream implements Parcelable {
"audioFormat = " + mAudioFormat + ", " + "audioFormat = " + mAudioFormat + ", " +
"audioStreamParcelFileDescriptor = " + mAudioStreamParcelFileDescriptor + ", " + "audioStreamParcelFileDescriptor = " + mAudioStreamParcelFileDescriptor + ", " +
"timestamp = " + mTimestamp + ", " + "timestamp = " + mTimestamp + ", " +
"metadata = " + mMetadata + "metadata = " + mMetadata + ", " +
"initialAudio = " + initialAudioToString() +
" }"; " }";
} }
@@ -230,7 +320,8 @@ public final class HotwordAudioStream implements Parcelable {
&& Objects.equals(mAudioFormat, that.mAudioFormat) && Objects.equals(mAudioFormat, that.mAudioFormat)
&& Objects.equals(mAudioStreamParcelFileDescriptor, that.mAudioStreamParcelFileDescriptor) && Objects.equals(mAudioStreamParcelFileDescriptor, that.mAudioStreamParcelFileDescriptor)
&& Objects.equals(mTimestamp, that.mTimestamp) && Objects.equals(mTimestamp, that.mTimestamp)
&& Objects.equals(mMetadata, that.mMetadata); && Objects.equals(mMetadata, that.mMetadata)
&& Arrays.equals(mInitialAudio, that.mInitialAudio);
} }
@Override @Override
@@ -244,6 +335,7 @@ public final class HotwordAudioStream implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mAudioStreamParcelFileDescriptor); _hash = 31 * _hash + Objects.hashCode(mAudioStreamParcelFileDescriptor);
_hash = 31 * _hash + Objects.hashCode(mTimestamp); _hash = 31 * _hash + Objects.hashCode(mTimestamp);
_hash = 31 * _hash + Objects.hashCode(mMetadata); _hash = 31 * _hash + Objects.hashCode(mMetadata);
_hash = 31 * _hash + Arrays.hashCode(mInitialAudio);
return _hash; return _hash;
} }
@@ -260,6 +352,7 @@ public final class HotwordAudioStream implements Parcelable {
dest.writeTypedObject(mAudioStreamParcelFileDescriptor, flags); dest.writeTypedObject(mAudioStreamParcelFileDescriptor, flags);
if (mTimestamp != null) dest.writeTypedObject(mTimestamp, flags); if (mTimestamp != null) dest.writeTypedObject(mTimestamp, flags);
dest.writeTypedObject(mMetadata, flags); dest.writeTypedObject(mMetadata, flags);
dest.writeByteArray(mInitialAudio);
} }
@Override @Override
@@ -278,6 +371,7 @@ public final class HotwordAudioStream implements Parcelable {
ParcelFileDescriptor audioStreamParcelFileDescriptor = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR); ParcelFileDescriptor audioStreamParcelFileDescriptor = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR);
AudioTimestamp timestamp = (flg & 0x4) == 0 ? null : (AudioTimestamp) in.readTypedObject(AudioTimestamp.CREATOR); AudioTimestamp timestamp = (flg & 0x4) == 0 ? null : (AudioTimestamp) in.readTypedObject(AudioTimestamp.CREATOR);
PersistableBundle metadata = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR); PersistableBundle metadata = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
byte[] initialAudio = in.createByteArray();
this.mAudioFormat = audioFormat; this.mAudioFormat = audioFormat;
com.android.internal.util.AnnotationValidations.validate( com.android.internal.util.AnnotationValidations.validate(
@@ -289,6 +383,9 @@ public final class HotwordAudioStream implements Parcelable {
this.mMetadata = metadata; this.mMetadata = metadata;
com.android.internal.util.AnnotationValidations.validate( com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMetadata); NonNull.class, null, mMetadata);
this.mInitialAudio = initialAudio;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInitialAudio);
// onConstructed(); // You can define this method to get a callback // onConstructed(); // You can define this method to get a callback
} }
@@ -312,12 +409,13 @@ public final class HotwordAudioStream implements Parcelable {
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member @DataClass.Generated.Member
public static final class Builder { public static final class Builder extends BaseBuilder {
private @NonNull AudioFormat mAudioFormat; private @NonNull AudioFormat mAudioFormat;
private @NonNull ParcelFileDescriptor mAudioStreamParcelFileDescriptor; private @NonNull ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
private @Nullable AudioTimestamp mTimestamp; private @Nullable AudioTimestamp mTimestamp;
private @NonNull PersistableBundle mMetadata; private @NonNull PersistableBundle mMetadata;
private @NonNull byte[] mInitialAudio;
private long mBuilderFieldsSet = 0L; private long mBuilderFieldsSet = 0L;
@@ -327,8 +425,17 @@ public final class HotwordAudioStream implements Parcelable {
* @param audioFormat * @param audioFormat
* The {@link AudioFormat} of the audio stream. * The {@link AudioFormat} of the audio stream.
* @param audioStreamParcelFileDescriptor * @param audioStreamParcelFileDescriptor
* This stream starts with the audio bytes used for hotword detection, but continues streaming * This stream typically starts with the audio bytes used for hotword detection, but continues
* the audio until the stream is shutdown by the {@link HotwordDetectionService}. * streaming the audio (e.g., with the query) until the stream is shutdown by the
* {@link HotwordDetectionService}. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
* to pass the start of the audio instead of streaming it here. This may prevent added latency
* caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
* being large enough to handle this initial chunk of audio.
* </p>
*/ */
public Builder( public Builder(
@NonNull AudioFormat audioFormat, @NonNull AudioFormat audioFormat,
@@ -353,8 +460,17 @@ public final class HotwordAudioStream implements Parcelable {
} }
/** /**
* This stream starts with the audio bytes used for hotword detection, but continues streaming * This stream typically starts with the audio bytes used for hotword detection, but continues
* the audio until the stream is shutdown by the {@link HotwordDetectionService}. * streaming the audio (e.g., with the query) until the stream is shutdown by the
* {@link HotwordDetectionService}. The data format is expected to match
* {@link #getAudioFormat()}.
*
* <p>
* Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()}
* to pass the start of the audio instead of streaming it here. This may prevent added latency
* caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not
* being large enough to handle this initial chunk of audio.
* </p>
*/ */
@DataClass.Generated.Member @DataClass.Generated.Member
public @NonNull Builder setAudioStreamParcelFileDescriptor(@NonNull ParcelFileDescriptor value) { public @NonNull Builder setAudioStreamParcelFileDescriptor(@NonNull ParcelFileDescriptor value) {
@@ -404,7 +520,7 @@ public final class HotwordAudioStream implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */ /** Builds the instance. This builder should not be touched after calling this! */
public @NonNull HotwordAudioStream build() { public @NonNull HotwordAudioStream build() {
checkNotUsed(); checkNotUsed();
mBuilderFieldsSet |= 0x10; // Mark builder used mBuilderFieldsSet |= 0x20; // Mark builder used
if ((mBuilderFieldsSet & 0x4) == 0) { if ((mBuilderFieldsSet & 0x4) == 0) {
mTimestamp = defaultTimestamp(); mTimestamp = defaultTimestamp();
@@ -412,16 +528,20 @@ public final class HotwordAudioStream implements Parcelable {
if ((mBuilderFieldsSet & 0x8) == 0) { if ((mBuilderFieldsSet & 0x8) == 0) {
mMetadata = defaultMetadata(); mMetadata = defaultMetadata();
} }
if ((mBuilderFieldsSet & 0x10) == 0) {
mInitialAudio = defaultInitialAudio();
}
HotwordAudioStream o = new HotwordAudioStream( HotwordAudioStream o = new HotwordAudioStream(
mAudioFormat, mAudioFormat,
mAudioStreamParcelFileDescriptor, mAudioStreamParcelFileDescriptor,
mTimestamp, mTimestamp,
mMetadata); mMetadata,
mInitialAudio);
return o; return o;
} }
private void checkNotUsed() { private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x10) != 0) { if ((mBuilderFieldsSet & 0x20) != 0) {
throw new IllegalStateException( throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead"); "This Builder should not be reused. Use a new Builder instance instead");
} }
@@ -429,10 +549,10 @@ public final class HotwordAudioStream implements Parcelable {
} }
@DataClass.Generated( @DataClass.Generated(
time = 1669916341034L, time = 1671232056270L,
codegenVersion = "1.0.23", codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java", sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
inputSignatures = "public static final java.lang.String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)") inputSignatures = "public static final java.lang.String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate final @android.annotation.NonNull byte[] mInitialAudio\nprivate static final byte[] DEFAULT_INITIAL_EMPTY_AUDIO\nprivate static android.media.AudioTimestamp defaultTimestamp()\nprivate static android.os.PersistableBundle defaultMetadata()\nprivate static byte[] defaultInitialAudio()\nprivate java.lang.String initialAudioToString()\npublic android.service.voice.HotwordAudioStream.Builder buildUpon()\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull android.service.voice.HotwordAudioStream.Builder setInitialAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)\npublic @android.annotation.NonNull android.service.voice.HotwordAudioStream.Builder setInitialAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated @Deprecated
private void __metadata() {} private void __metadata() {}