1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.voice; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.media.AudioFormat; 23 import android.media.AudioRecord; 24 import android.media.AudioTimestamp; 25 import android.os.Parcel; 26 import android.os.ParcelFileDescriptor; 27 import android.os.Parcelable; 28 import android.os.PersistableBundle; 29 30 import com.android.internal.util.DataClass; 31 32 import java.util.Arrays; 33 import java.util.Objects; 34 35 /** 36 * Represents an audio stream supporting the hotword detection. 37 * 38 * @hide 39 */ 40 @DataClass( 41 genConstructor = false, 42 genBuilder = true, 43 genEqualsHashCode = true, 44 genParcelable = true, 45 genToString = true 46 ) 47 @SystemApi 48 public final class HotwordAudioStream implements Parcelable { 49 50 /** 51 * Key for int value to be read from {@link #getMetadata()}. The value is read by the system and 52 * is the length (in bytes) of the byte buffers created to copy bytes in the 53 * {@link #getAudioStreamParcelFileDescriptor()} written by the {@link HotwordDetectionService}. 54 * The buffer length should be chosen such that no additional latency is introduced. Typically, 55 * this should be <em>at least</em> the size of byte chunks written by the 56 * {@link HotwordDetectionService}. 57 * 58 * <p>If no value specified in the metadata for the buffer length, or if the value is less than 59 * 1, or if it is greater than 65,536, or if it is not an int, the default value of 2,560 will 60 * be used.</p> 61 */ 62 public static final String KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES = 63 "android.service.voice.key.AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES"; 64 65 /** 66 * The {@link AudioFormat} of the audio stream. 67 */ 68 @NonNull 69 private final AudioFormat mAudioFormat; 70 71 /** 72 * This stream typically starts with the audio bytes used for hotword detection, but continues 73 * streaming the audio (e.g., with the query) until the stream is shutdown by the 74 * {@link HotwordDetectionService}. The data format is expected to match 75 * {@link #getAudioFormat()}. 76 * 77 * <p> 78 * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()} 79 * to pass the start of the audio instead of streaming it here. This may prevent added latency 80 * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not 81 * being large enough to handle this initial chunk of audio. 82 * </p> 83 */ 84 @NonNull 85 private final ParcelFileDescriptor mAudioStreamParcelFileDescriptor; 86 87 /** 88 * The timestamp when the audio stream was captured by the Audio platform. 89 * 90 * <p> 91 * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying 92 * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this 93 * field by {@link AudioRecord#getTimestamp}. 94 * </p> 95 * 96 * <p> 97 * This timestamp can be used in conjunction with the 98 * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and 99 * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to 100 * timestamps. 101 * </p> 102 * 103 * @see #getAudioStreamParcelFileDescriptor() 104 */ 105 @Nullable 106 private final AudioTimestamp mTimestamp; defaultTimestamp()107 private static AudioTimestamp defaultTimestamp() { 108 return null; 109 } 110 111 /** 112 * The metadata associated with the audio stream. 113 */ 114 @NonNull 115 private final PersistableBundle mMetadata; defaultMetadata()116 private static PersistableBundle defaultMetadata() { 117 return new PersistableBundle(); 118 } 119 120 /** 121 * The start of the audio used for hotword detection. The data format is expected to match 122 * {@link #getAudioFormat()}. 123 * 124 * <p> 125 * The {@link HotwordDetectionService} may use this instead of using 126 * {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. This 127 * may prevent added latency caused by the streaming buffer (see 128 * {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this 129 * initial chunk of audio. 130 * </p> 131 */ 132 @NonNull 133 private final byte[] mInitialAudio; 134 private static final byte[] DEFAULT_INITIAL_EMPTY_AUDIO = {}; defaultInitialAudio()135 private static byte[] defaultInitialAudio() { 136 return DEFAULT_INITIAL_EMPTY_AUDIO; 137 } 138 initialAudioToString()139 private String initialAudioToString() { 140 return "length=" + mInitialAudio.length; 141 } 142 143 /** 144 * Provides an instance of {@link Builder} with state corresponding to this instance. 145 * @hide 146 */ buildUpon()147 public Builder buildUpon() { 148 return new Builder(mAudioFormat, mAudioStreamParcelFileDescriptor) 149 .setTimestamp(mTimestamp) 150 .setMetadata(mMetadata) 151 .setInitialAudio(mInitialAudio); 152 } 153 154 @DataClass.Suppress("setInitialAudio") 155 abstract static class BaseBuilder { 156 157 /** 158 * The start of the audio used for hotword detection. The data format is expected to match 159 * {@link #getAudioFormat()}. 160 * 161 * <p> 162 * The {@link HotwordDetectionService} may use this instead of using 163 * {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. 164 * This may prevent added latency caused by the streaming buffer (see 165 * {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this 166 * initial chunk of audio. 167 * </p> 168 */ setInitialAudio(@onNull byte[] value)169 public @NonNull Builder setInitialAudio(@NonNull byte[] value) { 170 Objects.requireNonNull(value, "value should not be null"); 171 final Builder builder = (Builder) this; 172 // If the code gen flag in build() is changed, we must update the flag e.g. 0x10 here. 173 builder.mBuilderFieldsSet |= 0x10; 174 builder.mInitialAudio = value; 175 return builder; 176 } 177 } 178 179 180 181 // Code below generated by codegen v1.0.23. 182 // 183 // DO NOT MODIFY! 184 // CHECKSTYLE:OFF Generated code 185 // 186 // To regenerate run: 187 // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordAudioStream.java 188 // 189 // To exclude the generated code from IntelliJ auto-formatting enable (one-time): 190 // Settings > Editor > Code Style > Formatter Control 191 //@formatter:off 192 193 194 @DataClass.Generated.Member HotwordAudioStream( @onNull AudioFormat audioFormat, @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor, @Nullable AudioTimestamp timestamp, @NonNull PersistableBundle metadata, @NonNull byte[] initialAudio)195 /* package-private */ HotwordAudioStream( 196 @NonNull AudioFormat audioFormat, 197 @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor, 198 @Nullable AudioTimestamp timestamp, 199 @NonNull PersistableBundle metadata, 200 @NonNull byte[] initialAudio) { 201 this.mAudioFormat = audioFormat; 202 com.android.internal.util.AnnotationValidations.validate( 203 NonNull.class, null, mAudioFormat); 204 this.mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor; 205 com.android.internal.util.AnnotationValidations.validate( 206 NonNull.class, null, mAudioStreamParcelFileDescriptor); 207 this.mTimestamp = timestamp; 208 this.mMetadata = metadata; 209 com.android.internal.util.AnnotationValidations.validate( 210 NonNull.class, null, mMetadata); 211 this.mInitialAudio = initialAudio; 212 com.android.internal.util.AnnotationValidations.validate( 213 NonNull.class, null, mInitialAudio); 214 215 // onConstructed(); // You can define this method to get a callback 216 } 217 218 /** 219 * The {@link AudioFormat} of the audio stream. 220 */ 221 @DataClass.Generated.Member getAudioFormat()222 public @NonNull AudioFormat getAudioFormat() { 223 return mAudioFormat; 224 } 225 226 /** 227 * This stream typically starts with the audio bytes used for hotword detection, but continues 228 * streaming the audio (e.g., with the query) until the stream is shutdown by the 229 * {@link HotwordDetectionService}. The data format is expected to match 230 * {@link #getAudioFormat()}. 231 * 232 * <p> 233 * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()} 234 * to pass the start of the audio instead of streaming it here. This may prevent added latency 235 * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not 236 * being large enough to handle this initial chunk of audio. 237 * </p> 238 */ 239 @DataClass.Generated.Member getAudioStreamParcelFileDescriptor()240 public @NonNull ParcelFileDescriptor getAudioStreamParcelFileDescriptor() { 241 return mAudioStreamParcelFileDescriptor; 242 } 243 244 /** 245 * The timestamp when the audio stream was captured by the Audio platform. 246 * 247 * <p> 248 * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying 249 * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this 250 * field by {@link AudioRecord#getTimestamp}. 251 * </p> 252 * 253 * <p> 254 * This timestamp can be used in conjunction with the 255 * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and 256 * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to 257 * timestamps. 258 * </p> 259 * 260 * @see #getAudioStreamParcelFileDescriptor() 261 */ 262 @DataClass.Generated.Member getTimestamp()263 public @Nullable AudioTimestamp getTimestamp() { 264 return mTimestamp; 265 } 266 267 /** 268 * The metadata associated with the audio stream. 269 */ 270 @DataClass.Generated.Member getMetadata()271 public @NonNull PersistableBundle getMetadata() { 272 return mMetadata; 273 } 274 275 /** 276 * The start of the audio used for hotword detection. The data format is expected to match 277 * {@link #getAudioFormat()}. 278 * 279 * <p> 280 * The {@link HotwordDetectionService} may use this instead of using 281 * {@link #getAudioStreamParcelFileDescriptor()} to stream these initial bytes of audio. This 282 * may prevent added latency caused by the streaming buffer (see 283 * {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not being large enough to handle this 284 * initial chunk of audio. 285 * </p> 286 */ 287 @DataClass.Generated.Member getInitialAudio()288 public @NonNull byte[] getInitialAudio() { 289 return mInitialAudio; 290 } 291 292 @Override 293 @DataClass.Generated.Member toString()294 public String toString() { 295 // You can override field toString logic by defining methods like: 296 // String fieldNameToString() { ... } 297 298 return "HotwordAudioStream { " + 299 "audioFormat = " + mAudioFormat + ", " + 300 "audioStreamParcelFileDescriptor = " + mAudioStreamParcelFileDescriptor + ", " + 301 "timestamp = " + mTimestamp + ", " + 302 "metadata = " + mMetadata + ", " + 303 "initialAudio = " + initialAudioToString() + 304 " }"; 305 } 306 307 @Override 308 @DataClass.Generated.Member equals(@ullable Object o)309 public boolean equals(@Nullable Object o) { 310 // You can override field equality logic by defining either of the methods like: 311 // boolean fieldNameEquals(HotwordAudioStream other) { ... } 312 // boolean fieldNameEquals(FieldType otherValue) { ... } 313 314 if (this == o) return true; 315 if (o == null || getClass() != o.getClass()) return false; 316 @SuppressWarnings("unchecked") 317 HotwordAudioStream that = (HotwordAudioStream) o; 318 //noinspection PointlessBooleanExpression 319 return true 320 && Objects.equals(mAudioFormat, that.mAudioFormat) 321 && Objects.equals(mAudioStreamParcelFileDescriptor, that.mAudioStreamParcelFileDescriptor) 322 && Objects.equals(mTimestamp, that.mTimestamp) 323 && Objects.equals(mMetadata, that.mMetadata) 324 && Arrays.equals(mInitialAudio, that.mInitialAudio); 325 } 326 327 @Override 328 @DataClass.Generated.Member hashCode()329 public int hashCode() { 330 // You can override field hashCode logic by defining methods like: 331 // int fieldNameHashCode() { ... } 332 333 int _hash = 1; 334 _hash = 31 * _hash + Objects.hashCode(mAudioFormat); 335 _hash = 31 * _hash + Objects.hashCode(mAudioStreamParcelFileDescriptor); 336 _hash = 31 * _hash + Objects.hashCode(mTimestamp); 337 _hash = 31 * _hash + Objects.hashCode(mMetadata); 338 _hash = 31 * _hash + Arrays.hashCode(mInitialAudio); 339 return _hash; 340 } 341 342 @Override 343 @DataClass.Generated.Member writeToParcel(@onNull Parcel dest, int flags)344 public void writeToParcel(@NonNull Parcel dest, int flags) { 345 // You can override field parcelling by defining methods like: 346 // void parcelFieldName(Parcel dest, int flags) { ... } 347 348 byte flg = 0; 349 if (mTimestamp != null) flg |= 0x4; 350 dest.writeByte(flg); 351 dest.writeTypedObject(mAudioFormat, flags); 352 dest.writeTypedObject(mAudioStreamParcelFileDescriptor, flags); 353 if (mTimestamp != null) dest.writeTypedObject(mTimestamp, flags); 354 dest.writeTypedObject(mMetadata, flags); 355 dest.writeByteArray(mInitialAudio); 356 } 357 358 @Override 359 @DataClass.Generated.Member describeContents()360 public int describeContents() { return 0; } 361 362 /** @hide */ 363 @SuppressWarnings({"unchecked", "RedundantCast"}) 364 @DataClass.Generated.Member HotwordAudioStream(@onNull Parcel in)365 /* package-private */ HotwordAudioStream(@NonNull Parcel in) { 366 // You can override field unparcelling by defining methods like: 367 // static FieldType unparcelFieldName(Parcel in) { ... } 368 369 byte flg = in.readByte(); 370 AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR); 371 ParcelFileDescriptor audioStreamParcelFileDescriptor = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR); 372 AudioTimestamp timestamp = (flg & 0x4) == 0 ? null : (AudioTimestamp) in.readTypedObject(AudioTimestamp.CREATOR); 373 PersistableBundle metadata = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR); 374 byte[] initialAudio = in.createByteArray(); 375 376 this.mAudioFormat = audioFormat; 377 com.android.internal.util.AnnotationValidations.validate( 378 NonNull.class, null, mAudioFormat); 379 this.mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor; 380 com.android.internal.util.AnnotationValidations.validate( 381 NonNull.class, null, mAudioStreamParcelFileDescriptor); 382 this.mTimestamp = timestamp; 383 this.mMetadata = metadata; 384 com.android.internal.util.AnnotationValidations.validate( 385 NonNull.class, null, mMetadata); 386 this.mInitialAudio = initialAudio; 387 com.android.internal.util.AnnotationValidations.validate( 388 NonNull.class, null, mInitialAudio); 389 390 // onConstructed(); // You can define this method to get a callback 391 } 392 393 @DataClass.Generated.Member 394 public static final @NonNull Parcelable.Creator<HotwordAudioStream> CREATOR 395 = new Parcelable.Creator<HotwordAudioStream>() { 396 @Override 397 public HotwordAudioStream[] newArray(int size) { 398 return new HotwordAudioStream[size]; 399 } 400 401 @Override 402 public HotwordAudioStream createFromParcel(@NonNull Parcel in) { 403 return new HotwordAudioStream(in); 404 } 405 }; 406 407 /** 408 * A builder for {@link HotwordAudioStream} 409 */ 410 @SuppressWarnings("WeakerAccess") 411 @DataClass.Generated.Member 412 public static final class Builder extends BaseBuilder { 413 414 private @NonNull AudioFormat mAudioFormat; 415 private @NonNull ParcelFileDescriptor mAudioStreamParcelFileDescriptor; 416 private @Nullable AudioTimestamp mTimestamp; 417 private @NonNull PersistableBundle mMetadata; 418 private @NonNull byte[] mInitialAudio; 419 420 private long mBuilderFieldsSet = 0L; 421 422 /** 423 * Creates a new Builder. 424 * 425 * @param audioFormat 426 * The {@link AudioFormat} of the audio stream. 427 * @param audioStreamParcelFileDescriptor 428 * This stream typically starts with the audio bytes used for hotword detection, but continues 429 * streaming the audio (e.g., with the query) until the stream is shutdown by the 430 * {@link HotwordDetectionService}. The data format is expected to match 431 * {@link #getAudioFormat()}. 432 * 433 * <p> 434 * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()} 435 * to pass the start of the audio instead of streaming it here. This may prevent added latency 436 * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not 437 * being large enough to handle this initial chunk of audio. 438 * </p> 439 */ Builder( @onNull AudioFormat audioFormat, @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor)440 public Builder( 441 @NonNull AudioFormat audioFormat, 442 @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor) { 443 mAudioFormat = audioFormat; 444 com.android.internal.util.AnnotationValidations.validate( 445 NonNull.class, null, mAudioFormat); 446 mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor; 447 com.android.internal.util.AnnotationValidations.validate( 448 NonNull.class, null, mAudioStreamParcelFileDescriptor); 449 } 450 451 /** 452 * The {@link AudioFormat} of the audio stream. 453 */ 454 @DataClass.Generated.Member setAudioFormat(@onNull AudioFormat value)455 public @NonNull Builder setAudioFormat(@NonNull AudioFormat value) { 456 checkNotUsed(); 457 mBuilderFieldsSet |= 0x1; 458 mAudioFormat = value; 459 return this; 460 } 461 462 /** 463 * This stream typically starts with the audio bytes used for hotword detection, but continues 464 * streaming the audio (e.g., with the query) until the stream is shutdown by the 465 * {@link HotwordDetectionService}. The data format is expected to match 466 * {@link #getAudioFormat()}. 467 * 468 * <p> 469 * Alternatively, the {@link HotwordDetectionService} may use {@link #getInitialAudio()} 470 * to pass the start of the audio instead of streaming it here. This may prevent added latency 471 * caused by the streaming buffer (see {@link #KEY_AUDIO_STREAM_COPY_BUFFER_LENGTH_BYTES}) not 472 * being large enough to handle this initial chunk of audio. 473 * </p> 474 */ 475 @DataClass.Generated.Member setAudioStreamParcelFileDescriptor(@onNull ParcelFileDescriptor value)476 public @NonNull Builder setAudioStreamParcelFileDescriptor(@NonNull ParcelFileDescriptor value) { 477 checkNotUsed(); 478 mBuilderFieldsSet |= 0x2; 479 mAudioStreamParcelFileDescriptor = value; 480 return this; 481 } 482 483 /** 484 * The timestamp when the audio stream was captured by the Audio platform. 485 * 486 * <p> 487 * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying 488 * AudioRecord. The {@link HotwordDetectionService} is expected to optionally populate this 489 * field by {@link AudioRecord#getTimestamp}. 490 * </p> 491 * 492 * <p> 493 * This timestamp can be used in conjunction with the 494 * {@link HotwordDetectedResult#getHotwordOffsetMillis()} and 495 * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to 496 * timestamps. 497 * </p> 498 * 499 * @see #getAudioStreamParcelFileDescriptor() 500 */ 501 @DataClass.Generated.Member setTimestamp(@onNull AudioTimestamp value)502 public @NonNull Builder setTimestamp(@NonNull AudioTimestamp value) { 503 checkNotUsed(); 504 mBuilderFieldsSet |= 0x4; 505 mTimestamp = value; 506 return this; 507 } 508 509 /** 510 * The metadata associated with the audio stream. 511 */ 512 @DataClass.Generated.Member setMetadata(@onNull PersistableBundle value)513 public @NonNull Builder setMetadata(@NonNull PersistableBundle value) { 514 checkNotUsed(); 515 mBuilderFieldsSet |= 0x8; 516 mMetadata = value; 517 return this; 518 } 519 520 /** Builds the instance. This builder should not be touched after calling this! */ build()521 public @NonNull HotwordAudioStream build() { 522 checkNotUsed(); 523 mBuilderFieldsSet |= 0x20; // Mark builder used 524 525 if ((mBuilderFieldsSet & 0x4) == 0) { 526 mTimestamp = defaultTimestamp(); 527 } 528 if ((mBuilderFieldsSet & 0x8) == 0) { 529 mMetadata = defaultMetadata(); 530 } 531 if ((mBuilderFieldsSet & 0x10) == 0) { 532 mInitialAudio = defaultInitialAudio(); 533 } 534 HotwordAudioStream o = new HotwordAudioStream( 535 mAudioFormat, 536 mAudioStreamParcelFileDescriptor, 537 mTimestamp, 538 mMetadata, 539 mInitialAudio); 540 return o; 541 } 542 checkNotUsed()543 private void checkNotUsed() { 544 if ((mBuilderFieldsSet & 0x20) != 0) { 545 throw new IllegalStateException( 546 "This Builder should not be reused. Use a new Builder instance instead"); 547 } 548 } 549 } 550 551 @DataClass.Generated( 552 time = 1671232056270L, 553 codegenVersion = "1.0.23", 554 sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java", 555 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 []") 556 @Deprecated __metadata()557 private void __metadata() {} 558 559 560 //@formatter:on 561 // End of generated code 562 563 } 564