Addresses further review comments from ag/8000041

Including:
 - An API to opt out of Int/StringDefs generation on per-field basis
 - A way to customize Builder
 - Non-optional fields are passed in Builder constructor
 - Various adjustments to SampleDataclass examples, as requested

Test: . $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/runTest.sh
Change-Id: I32d2eec52f05d505ff07779d923e4793d3036579
diff --git a/core/java/com/android/internal/util/DataClass.java b/core/java/com/android/internal/util/DataClass.java
index 146f546..da33f99 100644
--- a/core/java/com/android/internal/util/DataClass.java
+++ b/core/java/com/android/internal/util/DataClass.java
@@ -177,11 +177,10 @@
 
     /**
      * @deprecated to be used by code generator exclusively
-     * @hide
      */
     @Deprecated
     @Retention(RetentionPolicy.SOURCE)
-    @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, ANNOTATION_TYPE, CONSTRUCTOR, TYPE})
+    @Target({METHOD})
     @interface Generated {
         long time();
         String codegenVersion();
@@ -190,7 +189,6 @@
 
         /**
          * @deprecated to be used by code generator exclusively
-         * @hide
          */
         @Deprecated
         @Retention(RetentionPolicy.SOURCE)
@@ -199,6 +197,22 @@
     }
 
     /**
+     * Opt out of generating {@link #genConstDefs IntDef/StringDef}s for annotated constant
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({FIELD})
+    @interface SuppressConstDefsGeneration {}
+
+    /**
+     * A class-level annotation to suppress methods' generation by name
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({TYPE})
+    @interface Suppress {
+        String[] value();
+    }
+
+    /**
      * Callback used by {@link #genForEachField}.
      *
      * @param <THIS> The enclosing data class instance.
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index 63530dc..390c596 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -24,6 +24,8 @@
 /**
  * Describes a 2-way parcelling contract of type {@code T} into/out of a {@link Parcel}
  *
+ * Implementations should be stateless.
+ *
  * @param <T> the type being [un]parcelled
  */
 public interface Parcelling<T> {
@@ -69,6 +71,7 @@
          * instance or reflectively creating one.
          */
         public static <P extends Parcelling<?>> P getOrCreate(Class<P> clazz) {
+            // No synchronization - creating an extra instance in a race case is ok
             P cached = get(clazz);
             if (cached != null) {
                 return cached;
diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh
index bc1aae0..614cbb7 100755
--- a/tests/Codegen/runTest.sh
+++ b/tests/Codegen/runTest.sh
@@ -12,6 +12,7 @@
 
     header_and_eval m -j16 codegen_cli && \
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \
+        header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java && \
         cd $ANDROID_BUILD_TOP &&
         header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
         header_and_eval adb install -r -t $ANDROID_PRODUCT_OUT/testcases/CodegenTests/arm64/CodegenTests.apk && \
diff --git a/tests/Codegen/src/com/android/codegentest/DateParcelling.java b/tests/Codegen/src/com/android/codegentest/MyDateParcelling.java
similarity index 94%
rename from tests/Codegen/src/com/android/codegentest/DateParcelling.java
rename to tests/Codegen/src/com/android/codegentest/MyDateParcelling.java
index b0b00d0..4faeb8e 100644
--- a/tests/Codegen/src/com/android/codegentest/DateParcelling.java
+++ b/tests/Codegen/src/com/android/codegentest/MyDateParcelling.java
@@ -31,11 +31,11 @@
  *
  * Ignore {@link #sInstanceCount} - used for testing.
  */
-public class DateParcelling implements Parcelling<Date> {
+public class MyDateParcelling implements Parcelling<Date> {
 
     static AtomicInteger sInstanceCount = new AtomicInteger(0);
 
-    public DateParcelling() {
+    public MyDateParcelling() {
         sInstanceCount.getAndIncrement();
     }
 
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index f69a092..b30fde4 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -53,11 +53,13 @@
 //        genAidl = true,       // implied by `implements Parcelable`
 //        genGetters = true,    // on by default
 //        genConstDefs = true,  // implied by presence of constants with common prefix
+        genBuilder = true,      // on by default if optional fields present, but suppressed by
+                                // genConstructor
+        genConstructor = true,  // on by default but normally suppressed by genBuilder
         genEqualsHashCode = true,
-        genBuilder = true,
         genToString = true,
         genForEachField = true,
-        genConstructor = true   // on by default but normally suppressed by genBuilder
+        genSetters = true
 )
 public final class SampleDataClass implements Parcelable {
 
@@ -136,48 +138,53 @@
     private int mNum4;
 
     /**
-     * {@link Nullable} fields are considered optional and will not throw an exception if omitted
-     * (or set to null) when creating an instance either via a {@link Builder} or constructor.
+     * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields.
      */
     private @Nullable String mName;
     /**
-     * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically
+     * Fields with default value expressions ("mFoo = ...") are optional, and are automatically
      * initialized to the provided default expression, unless explicitly set.
-     */
-    private String mName2 = "Bob";
-    /**
-     * Fields without {@link Nullable} annotation or default value are considered required.
      *
-     * {@link NonNull} annotation is recommended on such non-primitive fields for documentation.
+     * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter}
+     * while mandatory fields are passed via {@link Builder#Builder constructor}.
+     */
+    private @NonNull String mName2 = "Bob";
+    /**
+     * Alternatively, when default value computation is expensive,
+     * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value.
      */
     private @NonNull String mName4;
+    private static String defaultName4() {
+        // Expensive computation
+        return "Bob4";
+    }
 
     /**
      * For parcelling, any field type supported by {@link Parcel} is supported out of the box.
      * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc.
      */
-    private AccessibilityNodeInfo mOtherParcelable = null;
+    private @Nullable AccessibilityNodeInfo mOtherParcelable = null;
     /**
      * Additionally, support for parcelling other types can be added by implementing a
      * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation.
      *
-     * @see DateParcelling an example {@link Parcelling} implementation
+     * @see MyDateParcelling an example {@link Parcelling} implementation
      */
-    @DataClass.ParcelWith(DateParcelling.class)
-    private Date mDate = new Date(42 * 42);
+    @DataClass.ParcelWith(MyDateParcelling.class)
+    private @NonNull Date mDate = new Date(42 * 42);
     /**
      * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn}
      * to encourage its reuse.
      */
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class)
-    private Pattern mPattern = Pattern.compile("");
+    private @NonNull Pattern mPattern = Pattern.compile("");
 
     /**
      * For lists, when using a {@link Builder}, other than a regular
      * {@link Builder#setLinkAddresses2(List) setter}, and additional
      * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience.
      */
-    private List<LinkAddress> mLinkAddresses2 = new ArrayList<>();
+    private @NonNull List<LinkAddress> mLinkAddresses2 = new ArrayList<>();
     /**
      * For aesthetics, you may want to consider providing a singular version of the plural field
      * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method.
@@ -185,7 +192,7 @@
      * @see Builder#addLinkAddress(LinkAddress)
      */
     @DataClass.PluralOf("linkAddress")
-    private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
+    private @NonNull ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
     /**
      * For array fields, when using a {@link Builder}, vararg argument format is used for
      * convenience.
@@ -193,11 +200,6 @@
      * @see Builder#setLinkAddresses4(LinkAddress...)
      */
     private @Nullable LinkAddress[] mLinkAddresses4 = null;
-    /**
-     * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods
-     * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated.
-     */
-    private boolean mActive = true;
 
     /**
      * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to
@@ -206,7 +208,7 @@
      * @see #getStateName
      * @see Builder#setStateName
      */
-    private @StateName String mStateName = STATE_NAME_UNDEFINED;
+    private @StateName @NonNull String mStateName = STATE_NAME_UNDEFINED;
     /**
      * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value.
      */
@@ -220,11 +222,11 @@
     /**
      * Making a field public will suppress getter generation in favor of accessing it directly.
      */
-    public CharSequence charSeq = "";
+    public @NonNull CharSequence charSeq = "";
     /**
      * Final fields suppress generating a setter (when setters are requested).
      */
-    private final LinkAddress[] mLinkAddresses5;
+    private final @Nullable LinkAddress[] mLinkAddresses5;
     /**
      * Transient fields are completely ignored and can be used for caching.
      */
@@ -261,7 +263,7 @@
      *
      * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int)
      */
-    private @android.annotation.IntRange(from = 0, to = 4) int mLimited = 3;
+    private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek = 3;
     /**
      * Unnamed validation annotation parameter gets supplied to the validating method named as
      * "value".
@@ -272,6 +274,7 @@
      * @see AnnotationValidations#validate(Class, Size, int, String, int)
      */
     @Size(2)
+    @NonNull
     @Each @FloatRange(from = 0f)
     private float[] mCoords = new float[] {0f, 0f};
 
@@ -340,7 +343,6 @@
 
 
     // Code below generated by codegen v1.0.0.
-    //   on Jul 29, 2019, 2:50:21 PM PDT
     //
     // DO NOT MODIFY!
     //
@@ -408,13 +410,9 @@
     @DataClass.Generated.Member
     public @interface StateName {}
 
-    @DataClass.Generated(
-            time = 1564437021513L,
-            codegenVersion = "1.0.0",
-            sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
-            inputSignatures = "public static final  java.lang.String STATE_NAME_UNDEFINED\npublic static final  java.lang.String STATE_NAME_ON\npublic static final  java.lang.String STATE_NAME_OFF\npublic static final  int STATE_UNDEFINED\npublic static final  int STATE_ON\npublic static final  int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate  int mNum\nprivate  int mNum2\nprivate  int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate  java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate  android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.DateParcelling.class) java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) java.util.regex.Pattern mPattern\nprivate  java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate  boolean mActive\nprivate @com.android.codegentest.SampleDataClass.StateName java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic  java.lang.CharSequence charSeq\nprivate final  android.net.LinkAddress[] mLinkAddresses5\nprivate transient  android.net.LinkAddress[] mLinkAddresses6\ntransient  int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=4L) int mLimited\nprivate @android.annotation.Size(2L) @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate  int[] lazyInitTmpStorage()\npublic  android.net.LinkAddress[] getLinkAddresses4()\nprivate  boolean patternEquals(java.util.regex.Pattern)\nprivate  int patternHashCode()\nprivate  void onConstructed()\npublic  void dump(java.io.PrintWriter)")
-
-/**
+    /**
+     * Creates a new SampleDataClass.
+     *
      * @param num
      *   Any property javadoc should go onto the field, and will be copied where appropriate,
      *   including getters, constructor parameters, builder setters, etc.
@@ -429,15 +427,16 @@
      *   {@code @hide} javadoc annotation is also propagated, which can be used to adjust the
      *   desired public API surface.
      * @param name
-     *   {@link Nullable} fields are considered optional and will not throw an exception if omitted
-     *   (or set to null) when creating an instance either via a {@link Builder} or constructor.
+     *   {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields.
      * @param name2
-     *   Fields with default value expressions ("mFoo = ...") are also optional, and are automatically
+     *   Fields with default value expressions ("mFoo = ...") are optional, and are automatically
      *   initialized to the provided default expression, unless explicitly set.
-     * @param name4
-     *   Fields without {@link Nullable} annotation or default value are considered required.
      *
-     *   {@link NonNull} annotation is recommended on such non-primitive fields for documentation.
+     *   When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter}
+     *   while mandatory fields are passed via {@link Builder#Builder constructor}.
+     * @param name4
+     *   Alternatively, when default value computation is expensive,
+     *   {@link #defaultName4 defaultFieldName()} can be defined to compute the default value.
      * @param otherParcelable
      *   For parcelling, any field type supported by {@link Parcel} is supported out of the box.
      *   E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc.
@@ -457,9 +456,6 @@
      * @param linkAddresses4
      *   For array fields, when using a {@link Builder}, vararg argument format is used for
      *   convenience.
-     * @param active
-     *   For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods
-     *   like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated.
      * @param stateName
      *   {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to
      *   getter/constructor/setter/builder parameters, making for a nicer api.
@@ -480,7 +476,7 @@
      *   You can also extend support to your custom annotations by creating another corresponding
      *   overloads like
      *   {@link AnnotationValidations#validate(Class, UserIdInt, int)}.
-     * @param limited
+     * @param dayOfWeek
      *   Validation annotations may also have parameters.
      *
      *   Parameter values will be supplied to validation method as name-value pairs.
@@ -497,50 +493,49 @@
             int num2,
             int num4,
             @Nullable String name,
-            String name2,
+            @NonNull String name2,
             @NonNull String name4,
-            AccessibilityNodeInfo otherParcelable,
-            Date date,
-            Pattern pattern,
-            List<LinkAddress> linkAddresses2,
-            ArrayList<LinkAddress> linkAddresses,
+            @Nullable AccessibilityNodeInfo otherParcelable,
+            @NonNull Date date,
+            @NonNull Pattern pattern,
+            @NonNull List<LinkAddress> linkAddresses2,
+            @NonNull ArrayList<LinkAddress> linkAddresses,
             @Nullable LinkAddress[] linkAddresses4,
-            boolean active,
-            @StateName String stateName,
+            @StateName @NonNull String stateName,
             @RequestFlags int flags,
             @State int state,
-            CharSequence charSeq,
-            LinkAddress[] linkAddresses5,
+            @NonNull CharSequence charSeq,
+            @Nullable LinkAddress[] linkAddresses5,
             @StringRes int stringRes,
-            @android.annotation.IntRange(from = 0, to = 4) int limited,
-            @Size(2) @FloatRange(from = 0f) float[] coords) {
+            @android.annotation.IntRange(from = 0, to = 6) int dayOfWeek,
+            @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] coords) {
         this.mNum = num;
         this.mNum2 = num2;
         this.mNum4 = num4;
         this.mName = name;
         this.mName2 = name2;
-        this.mName4 = Preconditions.checkNotNull(name4);
-        this.mOtherParcelable = otherParcelable;
-        this.mDate = date;
-        this.mPattern = pattern;
-        this.mLinkAddresses2 = linkAddresses2;
-        this.mLinkAddresses = linkAddresses;
-        this.mLinkAddresses4 = linkAddresses4;
-        this.mActive = active;
-        this.mStateName = stateName;
-        this.mFlags = flags;
-        this.mState = state;
-        this.charSeq = charSeq;
-        this.mLinkAddresses5 = linkAddresses5;
-        this.mStringRes = stringRes;
-        this.mLimited = limited;
-        this.mCoords = coords;
+        AnnotationValidations.validate(
+                NonNull.class, null, mName2);
+        this.mName4 = name4;
         AnnotationValidations.validate(
                 NonNull.class, null, mName4);
+        this.mOtherParcelable = otherParcelable;
+        this.mDate = date;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDate);
+        this.mPattern = pattern;
+        AnnotationValidations.validate(
+                NonNull.class, null, mPattern);
+        this.mLinkAddresses2 = linkAddresses2;
+        AnnotationValidations.validate(
+                NonNull.class, null, mLinkAddresses2);
+        this.mLinkAddresses = linkAddresses;
+        AnnotationValidations.validate(
+                NonNull.class, null, mLinkAddresses);
+        this.mLinkAddresses4 = linkAddresses4;
+        this.mStateName = stateName;
 
-        //noinspection PointlessBooleanExpression
-        if (true
-                && !(Objects.equals(mStateName, STATE_NAME_UNDEFINED))
+        if (!(Objects.equals(mStateName, STATE_NAME_UNDEFINED))
                 && !(Objects.equals(mStateName, STATE_NAME_ON))
                 && !(Objects.equals(mStateName, STATE_NAME_OFF))) {
             throw new java.lang.IllegalArgumentException(
@@ -550,17 +545,18 @@
                             + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")");
         }
 
+        AnnotationValidations.validate(
+                NonNull.class, null, mStateName);
+        this.mFlags = flags;
 
-        //noinspection PointlessBitwiseExpression
         Preconditions.checkFlagsArgument(
-                mFlags, 0
-                        | FLAG_MANUAL_REQUEST
+                mFlags,
+                FLAG_MANUAL_REQUEST
                         | FLAG_COMPATIBILITY_MODE_REQUEST
                         | FLAG_AUGMENTED_REQUEST);
+        this.mState = state;
 
-        //noinspection PointlessBooleanExpression
-        if (true
-                && !(mState == STATE_UNDEFINED)
+        if (!(mState == STATE_UNDEFINED)
                 && !(mState == STATE_ON)
                 && !(mState == STATE_OFF)) {
             throw new java.lang.IllegalArgumentException(
@@ -570,15 +566,24 @@
                             + "STATE_OFF(" + STATE_OFF + ")");
         }
 
+        this.charSeq = charSeq;
+        AnnotationValidations.validate(
+                NonNull.class, null, charSeq);
+        this.mLinkAddresses5 = linkAddresses5;
+        this.mStringRes = stringRes;
         AnnotationValidations.validate(
                 StringRes.class, null, mStringRes);
+        this.mDayOfWeek = dayOfWeek;
         AnnotationValidations.validate(
-                android.annotation.IntRange.class, null, mLimited,
+                android.annotation.IntRange.class, null, mDayOfWeek,
                 "from", 0,
-                "to", 4);
+                "to", 6);
+        this.mCoords = coords;
         AnnotationValidations.validate(
                 Size.class, null, mCoords.length,
                 "value", 2);
+        AnnotationValidations.validate(
+                NonNull.class, null, mCoords);
         int coordsSize = mCoords.length;
         for (int i = 0; i < coordsSize; i++) {
             AnnotationValidations.validate(
@@ -628,8 +633,7 @@
     }
 
     /**
-     * {@link Nullable} fields are considered optional and will not throw an exception if omitted
-     * (or set to null) when creating an instance either via a {@link Builder} or constructor.
+     * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields.
      */
     @DataClass.Generated.Member
     public @Nullable String getName() {
@@ -637,18 +641,20 @@
     }
 
     /**
-     * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically
+     * Fields with default value expressions ("mFoo = ...") are optional, and are automatically
      * initialized to the provided default expression, unless explicitly set.
+     *
+     * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter}
+     * while mandatory fields are passed via {@link Builder#Builder constructor}.
      */
     @DataClass.Generated.Member
-    public String getName2() {
+    public @NonNull String getName2() {
         return mName2;
     }
 
     /**
-     * Fields without {@link Nullable} annotation or default value are considered required.
-     *
-     * {@link NonNull} annotation is recommended on such non-primitive fields for documentation.
+     * Alternatively, when default value computation is expensive,
+     * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value.
      */
     @DataClass.Generated.Member
     public @NonNull String getName4() {
@@ -660,7 +666,7 @@
      * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc.
      */
     @DataClass.Generated.Member
-    public AccessibilityNodeInfo getOtherParcelable() {
+    public @Nullable AccessibilityNodeInfo getOtherParcelable() {
         return mOtherParcelable;
     }
 
@@ -668,10 +674,10 @@
      * Additionally, support for parcelling other types can be added by implementing a
      * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation.
      *
-     * @see DateParcelling an example {@link Parcelling} implementation
+     * @see MyDateParcelling an example {@link Parcelling} implementation
      */
     @DataClass.Generated.Member
-    public Date getDate() {
+    public @NonNull Date getDate() {
         return mDate;
     }
 
@@ -680,7 +686,7 @@
      * to encourage its reuse.
      */
     @DataClass.Generated.Member
-    public Pattern getPattern() {
+    public @NonNull Pattern getPattern() {
         return mPattern;
     }
 
@@ -690,7 +696,7 @@
      * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience.
      */
     @DataClass.Generated.Member
-    public List<LinkAddress> getLinkAddresses2() {
+    public @NonNull List<LinkAddress> getLinkAddresses2() {
         return mLinkAddresses2;
     }
 
@@ -701,20 +707,11 @@
      * @see Builder#addLinkAddress(LinkAddress)
      */
     @DataClass.Generated.Member
-    public ArrayList<LinkAddress> getLinkAddresses() {
+    public @NonNull ArrayList<LinkAddress> getLinkAddresses() {
         return mLinkAddresses;
     }
 
     /**
-     * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods
-     * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated.
-     */
-    @DataClass.Generated.Member
-    public boolean isActive() {
-        return mActive;
-    }
-
-    /**
      * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to
      * getter/constructor/setter/builder parameters, making for a nicer api.
      *
@@ -722,7 +719,7 @@
      * @see Builder#setStateName
      */
     @DataClass.Generated.Member
-    public @StateName String getStateName() {
+    public @StateName @NonNull String getStateName() {
         return mStateName;
     }
 
@@ -746,7 +743,7 @@
      * Final fields suppress generating a setter (when setters are requested).
      */
     @DataClass.Generated.Member
-    public LinkAddress[] getLinkAddresses5() {
+    public @Nullable LinkAddress[] getLinkAddresses5() {
         return mLinkAddresses5;
     }
 
@@ -775,8 +772,8 @@
      * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int)
      */
     @DataClass.Generated.Member
-    public @android.annotation.IntRange(from = 0, to = 4) int getLimited() {
-        return mLimited;
+    public @android.annotation.IntRange(from = 0, to = 6) int getDayOfWeek() {
+        return mDayOfWeek;
     }
 
     /**
@@ -789,7 +786,7 @@
      * @see AnnotationValidations#validate(Class, Size, int, String, int)
      */
     @DataClass.Generated.Member
-    public @Size(2) @FloatRange(from = 0f) float[] getCoords() {
+    public @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] getCoords() {
         return mCoords;
     }
 
@@ -810,6 +807,282 @@
         return tmpStorage;
     }
 
+    /**
+     * Any property javadoc should go onto the field, and will be copied where appropriate,
+     * including getters, constructor parameters, builder setters, etc.
+     *
+     * <p>
+     * This allows to avoid the burden of maintaining copies of the same documentation
+     * pieces in multiple places for each field.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setNum(int value) {
+        mNum = value;
+        return this;
+    }
+
+    /**
+     * Various javadoc features should work as expected when copied, e.g {@code code},
+     * {@link #mName links}, <a href="https://google.com">html links</a>, etc.
+     *
+     * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setNum2(int value) {
+        mNum2 = value;
+        return this;
+    }
+
+    /**
+     * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the
+     * desired public API surface.
+     *
+     * @see #getNum4() is hidden
+     * @see Builder#setNum4(int) also hidden
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setNum4(int value) {
+        mNum4 = value;
+        return this;
+    }
+
+    /**
+     * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setName(@Nullable String value) {
+        mName = value;
+        return this;
+    }
+
+    /**
+     * Fields with default value expressions ("mFoo = ...") are optional, and are automatically
+     * initialized to the provided default expression, unless explicitly set.
+     *
+     * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter}
+     * while mandatory fields are passed via {@link Builder#Builder constructor}.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setName2(@NonNull String value) {
+        mName2 = value;
+        AnnotationValidations.validate(
+                NonNull.class, null, mName2);
+        return this;
+    }
+
+    /**
+     * Alternatively, when default value computation is expensive,
+     * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setName4(@NonNull String value) {
+        mName4 = value;
+        AnnotationValidations.validate(
+                NonNull.class, null, mName4);
+        return this;
+    }
+
+    /**
+     * For parcelling, any field type supported by {@link Parcel} is supported out of the box.
+     * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setOtherParcelable(@Nullable AccessibilityNodeInfo value) {
+        mOtherParcelable = value;
+        return this;
+    }
+
+    /**
+     * Additionally, support for parcelling other types can be added by implementing a
+     * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation.
+     *
+     * @see MyDateParcelling an example {@link Parcelling} implementation
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setDate(@NonNull Date value) {
+        mDate = value;
+        AnnotationValidations.validate(
+                NonNull.class, null, mDate);
+        return this;
+    }
+
+    /**
+     * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn}
+     * to encourage its reuse.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setPattern(@NonNull Pattern value) {
+        mPattern = value;
+        AnnotationValidations.validate(
+                NonNull.class, null, mPattern);
+        return this;
+    }
+
+    /**
+     * For lists, when using a {@link Builder}, other than a regular
+     * {@link Builder#setLinkAddresses2(List) setter}, and additional
+     * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setLinkAddresses2(@NonNull List<LinkAddress> value) {
+        mLinkAddresses2 = value;
+        AnnotationValidations.validate(
+                NonNull.class, null, mLinkAddresses2);
+        return this;
+    }
+
+    /**
+     * For aesthetics, you may want to consider providing a singular version of the plural field
+     * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method.
+     *
+     * @see Builder#addLinkAddress(LinkAddress)
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setLinkAddresses(@NonNull ArrayList<LinkAddress> value) {
+        mLinkAddresses = value;
+        AnnotationValidations.validate(
+                NonNull.class, null, mLinkAddresses);
+        return this;
+    }
+
+    /**
+     * For array fields, when using a {@link Builder}, vararg argument format is used for
+     * convenience.
+     *
+     * @see Builder#setLinkAddresses4(LinkAddress...)
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setLinkAddresses4(@Nullable LinkAddress... value) {
+        mLinkAddresses4 = value;
+        return this;
+    }
+
+    /**
+     * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to
+     * getter/constructor/setter/builder parameters, making for a nicer api.
+     *
+     * @see #getStateName
+     * @see Builder#setStateName
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setStateName(@StateName @NonNull String value) {
+        mStateName = value;
+
+        if (!(Objects.equals(mStateName, STATE_NAME_UNDEFINED))
+                && !(Objects.equals(mStateName, STATE_NAME_ON))
+                && !(Objects.equals(mStateName, STATE_NAME_OFF))) {
+            throw new java.lang.IllegalArgumentException(
+                    "stateName was " + mStateName + " but must be one of: "
+                            + "STATE_NAME_UNDEFINED(" + STATE_NAME_UNDEFINED + "), "
+                            + "STATE_NAME_ON(" + STATE_NAME_ON + "), "
+                            + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")");
+        }
+
+        AnnotationValidations.validate(
+                NonNull.class, null, mStateName);
+        return this;
+    }
+
+    /**
+     * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value.
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setFlags(@RequestFlags int value) {
+        mFlags = value;
+
+        Preconditions.checkFlagsArgument(
+                mFlags,
+                FLAG_MANUAL_REQUEST
+                        | FLAG_COMPATIBILITY_MODE_REQUEST
+                        | FLAG_AUGMENTED_REQUEST);
+        return this;
+    }
+
+    /**
+     * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setState(@State int value) {
+        mState = value;
+
+        if (!(mState == STATE_UNDEFINED)
+                && !(mState == STATE_ON)
+                && !(mState == STATE_OFF)) {
+            throw new java.lang.IllegalArgumentException(
+                    "state was " + mState + " but must be one of: "
+                            + "STATE_UNDEFINED(" + STATE_UNDEFINED + "), "
+                            + "STATE_ON(" + STATE_ON + "), "
+                            + "STATE_OFF(" + STATE_OFF + ")");
+        }
+
+        return this;
+    }
+
+    /**
+     * Fields with certain annotations are automatically validated in constructor
+     *
+     * You can see overloads in {@link AnnotationValidations} for a list of currently
+     * supported ones.
+     *
+     * You can also extend support to your custom annotations by creating another corresponding
+     * overloads like
+     * {@link AnnotationValidations#validate(Class, UserIdInt, int)}.
+     *
+     * @see #SampleDataClass
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setStringRes(@StringRes int value) {
+        mStringRes = value;
+        AnnotationValidations.validate(
+                StringRes.class, null, mStringRes);
+        return this;
+    }
+
+    /**
+     * Validation annotations may also have parameters.
+     *
+     * Parameter values will be supplied to validation method as name-value pairs.
+     *
+     * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int)
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setDayOfWeek(@android.annotation.IntRange(from = 0, to = 6) int value) {
+        mDayOfWeek = value;
+        AnnotationValidations.validate(
+                android.annotation.IntRange.class, null, mDayOfWeek,
+                "from", 0,
+                "to", 6);
+        return this;
+    }
+
+    /**
+     * Unnamed validation annotation parameter gets supplied to the validating method named as
+     * "value".
+     *
+     * Validation annotations following {@link Each} annotation, will be applied for each
+     * array/collection element instead.
+     *
+     * @see AnnotationValidations#validate(Class, Size, int, String, int)
+     */
+    @DataClass.Generated.Member
+    public SampleDataClass setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) {
+        mCoords = value;
+        AnnotationValidations.validate(
+                Size.class, null, mCoords.length,
+                "value", 2);
+        AnnotationValidations.validate(
+                NonNull.class, null, mCoords);
+        int coordsSize = mCoords.length;
+        for (int i = 0; i < coordsSize; i++) {
+            AnnotationValidations.validate(
+                    FloatRange.class, null, mCoords[i],
+                    "from", 0f);
+        }
+
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -829,14 +1102,13 @@
                 "linkAddresses2 = " + mLinkAddresses2 + ", " +
                 "linkAddresses = " + mLinkAddresses + ", " +
                 "linkAddresses4 = " + java.util.Arrays.toString(mLinkAddresses4) + ", " +
-                "active = " + mActive + ", " +
                 "stateName = " + mStateName + ", " +
                 "flags = " + requestFlagsToString(mFlags) + ", " +
                 "state = " + stateToString(mState) + ", " +
                 "charSeq = " + charSeq + ", " +
                 "linkAddresses5 = " + java.util.Arrays.toString(mLinkAddresses5) + ", " +
                 "stringRes = " + mStringRes + ", " +
-                "limited = " + mLimited + ", " +
+                "dayOfWeek = " + mDayOfWeek + ", " +
                 "coords = " + java.util.Arrays.toString(mCoords) +
         " }";
     }
@@ -866,14 +1138,13 @@
                 && Objects.equals(mLinkAddresses2, that.mLinkAddresses2)
                 && Objects.equals(mLinkAddresses, that.mLinkAddresses)
                 && java.util.Arrays.equals(mLinkAddresses4, that.mLinkAddresses4)
-                && mActive == that.mActive
                 && Objects.equals(mStateName, that.mStateName)
                 && mFlags == that.mFlags
                 && mState == that.mState
                 && Objects.equals(charSeq, that.charSeq)
                 && java.util.Arrays.equals(mLinkAddresses5, that.mLinkAddresses5)
                 && mStringRes == that.mStringRes
-                && mLimited == that.mLimited
+                && mDayOfWeek == that.mDayOfWeek
                 && java.util.Arrays.equals(mCoords, that.mCoords);
     }
 
@@ -896,14 +1167,13 @@
         _hash = 31 * _hash + Objects.hashCode(mLinkAddresses2);
         _hash = 31 * _hash + Objects.hashCode(mLinkAddresses);
         _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses4);
-        _hash = 31 * _hash + Boolean.hashCode(mActive);
         _hash = 31 * _hash + Objects.hashCode(mStateName);
         _hash = 31 * _hash + mFlags;
         _hash = 31 * _hash + mState;
         _hash = 31 * _hash + Objects.hashCode(charSeq);
         _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses5);
         _hash = 31 * _hash + mStringRes;
-        _hash = 31 * _hash + mLimited;
+        _hash = 31 * _hash + mDayOfWeek;
         _hash = 31 * _hash + java.util.Arrays.hashCode(mCoords);
         return _hash;
     }
@@ -924,14 +1194,13 @@
         actionObject.acceptObject(this, "linkAddresses2", mLinkAddresses2);
         actionObject.acceptObject(this, "linkAddresses", mLinkAddresses);
         actionObject.acceptObject(this, "linkAddresses4", mLinkAddresses4);
-        actionObject.acceptObject(this, "active", mActive);
         actionObject.acceptObject(this, "stateName", mStateName);
         actionInt.acceptInt(this, "flags", mFlags);
         actionInt.acceptInt(this, "state", mState);
         actionObject.acceptObject(this, "charSeq", charSeq);
         actionObject.acceptObject(this, "linkAddresses5", mLinkAddresses5);
         actionInt.acceptInt(this, "stringRes", mStringRes);
-        actionInt.acceptInt(this, "limited", mLimited);
+        actionInt.acceptInt(this, "dayOfWeek", mDayOfWeek);
         actionObject.acceptObject(this, "coords", mCoords);
     }
 
@@ -951,25 +1220,24 @@
         action.acceptObject(this, "linkAddresses2", mLinkAddresses2);
         action.acceptObject(this, "linkAddresses", mLinkAddresses);
         action.acceptObject(this, "linkAddresses4", mLinkAddresses4);
-        action.acceptObject(this, "active", mActive);
         action.acceptObject(this, "stateName", mStateName);
         action.acceptObject(this, "flags", mFlags);
         action.acceptObject(this, "state", mState);
         action.acceptObject(this, "charSeq", charSeq);
         action.acceptObject(this, "linkAddresses5", mLinkAddresses5);
         action.acceptObject(this, "stringRes", mStringRes);
-        action.acceptObject(this, "limited", mLimited);
+        action.acceptObject(this, "dayOfWeek", mDayOfWeek);
         action.acceptObject(this, "coords", mCoords);
     }
 
     @DataClass.Generated.Member
     static Parcelling<Date> sParcellingForDate =
             Parcelling.Cache.get(
-                    DateParcelling.class);
+                    MyDateParcelling.class);
     static {
         if (sParcellingForDate == null) {
             sParcellingForDate = Parcelling.Cache.put(
-                    new DateParcelling());
+                    new MyDateParcelling());
         }
     }
 
@@ -991,40 +1259,31 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         long flg = 0;
-        if (mActive) flg |= 0x1000;
         if (mName != null) flg |= 0x8;
-        if (mName2 != null) flg |= 0x10;
         if (mOtherParcelable != null) flg |= 0x40;
-        if (mDate != null) flg |= 0x80;
-        if (mPattern != null) flg |= 0x100;
-        if (mLinkAddresses2 != null) flg |= 0x200;
-        if (mLinkAddresses != null) flg |= 0x400;
         if (mLinkAddresses4 != null) flg |= 0x800;
-        if (mStateName != null) flg |= 0x2000;
-        if (charSeq != null) flg |= 0x10000;
-        if (mLinkAddresses5 != null) flg |= 0x20000;
-        if (mCoords != null) flg |= 0x100000;
+        if (mLinkAddresses5 != null) flg |= 0x10000;
         dest.writeLong(flg);
         dest.writeInt(mNum);
         dest.writeInt(mNum2);
         dest.writeInt(mNum4);
         if (mName != null) dest.writeString(mName);
-        if (mName2 != null) dest.writeString(mName2);
+        dest.writeString(mName2);
         dest.writeString(mName4);
         if (mOtherParcelable != null) dest.writeTypedObject(mOtherParcelable, flags);
         sParcellingForDate.parcel(mDate, dest, flags);
         sParcellingForPattern.parcel(mPattern, dest, flags);
-        if (mLinkAddresses2 != null) dest.writeParcelableList(mLinkAddresses2, flags);
-        if (mLinkAddresses != null) dest.writeParcelableList(mLinkAddresses, flags);
+        dest.writeParcelableList(mLinkAddresses2, flags);
+        dest.writeParcelableList(mLinkAddresses, flags);
         if (mLinkAddresses4 != null) dest.writeTypedArray(mLinkAddresses4, flags);
-        if (mStateName != null) dest.writeString(mStateName);
+        dest.writeString(mStateName);
         dest.writeInt(mFlags);
         dest.writeInt(mState);
-        if (charSeq != null) dest.writeCharSequence(charSeq);
+        dest.writeCharSequence(charSeq);
         if (mLinkAddresses5 != null) dest.writeTypedArray(mLinkAddresses5, flags);
         dest.writeInt(mStringRes);
-        dest.writeInt(mLimited);
-        if (mCoords != null) dest.writeFloatArray(mCoords);
+        dest.writeInt(mDayOfWeek);
+        dest.writeFloatArray(mCoords);
     }
 
     @Override
@@ -1046,35 +1305,28 @@
             // static FieldType unparcelFieldName(Parcel in) { ... }
 
             long flg = in.readLong();
-            boolean active = (flg & 0x1000) != 0;
             int num = in.readInt();
             int num2 = in.readInt();
             int num4 = in.readInt();
             String name = (flg & 0x8) == 0 ? null : in.readString();
-            String name2 = (flg & 0x10) == 0 ? null : in.readString();
+            String name2 = in.readString();
             String name4 = in.readString();
             AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR);
             Date date = sParcellingForDate.unparcel(in);
             Pattern pattern = sParcellingForPattern.unparcel(in);
-            List<LinkAddress> linkAddresses2 = null;
-            if ((flg & 0x200) != 0) {
-                linkAddresses2 = new ArrayList<>();
-                in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader());
-            }
-            ArrayList<LinkAddress> linkAddresses = null;
-            if ((flg & 0x400) != 0) {
-                linkAddresses = new ArrayList<>();
-                in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader());
-            }
+            List<LinkAddress> linkAddresses2 = new ArrayList<>();
+            in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader());
+            ArrayList<LinkAddress> linkAddresses = new ArrayList<>();
+            in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader());
             LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
-            String stateName = (flg & 0x2000) == 0 ? null : in.readString();
+            String stateName = in.readString();
             int flags = in.readInt();
             int state = in.readInt();
-            CharSequence _charSeq = (flg & 0x10000) == 0 ? null : (CharSequence) in.readCharSequence();
-            LinkAddress[] linkAddresses5 = (flg & 0x20000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
+            CharSequence _charSeq = (CharSequence) in.readCharSequence();
+            LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
             int stringRes = in.readInt();
-            int limited = in.readInt();
-            float[] coords = (flg & 0x100000) == 0 ? null : in.createFloatArray();
+            int dayOfWeek = in.readInt();
+            float[] coords = in.createFloatArray();
             return new SampleDataClass(
                     num,
                     num2,
@@ -1088,14 +1340,13 @@
                     linkAddresses2,
                     linkAddresses,
                     linkAddresses4,
-                    active,
                     stateName,
                     flags,
                     state,
                     _charSeq,
                     linkAddresses5,
                     stringRes,
-                    limited,
+                    dayOfWeek,
                     coords);
         }
     };
@@ -1105,34 +1356,74 @@
      */
     @SuppressWarnings("WeakerAccess")
     @DataClass.Generated.Member
-    public static class Builder
-            extends android.provider.OneTimeUseBuilder<SampleDataClass> {
+    public static class Builder {
 
-        protected int mNum;
-        protected int mNum2;
-        protected int mNum4;
-        protected @Nullable String mName;
-        protected String mName2;
-        protected @NonNull String mName4;
-        protected AccessibilityNodeInfo mOtherParcelable;
-        protected Date mDate;
-        protected Pattern mPattern;
-        protected List<LinkAddress> mLinkAddresses2;
-        protected ArrayList<LinkAddress> mLinkAddresses;
-        protected @Nullable LinkAddress[] mLinkAddresses4;
-        protected boolean mActive;
-        protected @StateName String mStateName;
-        protected @RequestFlags int mFlags;
-        protected @State int mState;
-        protected CharSequence charSeq;
-        protected LinkAddress[] mLinkAddresses5;
-        protected @StringRes int mStringRes;
-        protected @android.annotation.IntRange(from = 0, to = 4) int mLimited;
-        protected @Size(2) @FloatRange(from = 0f) float[] mCoords;
+        private int mNum;
+        private int mNum2;
+        private int mNum4;
+        private @Nullable String mName;
+        private @NonNull String mName2;
+        private @NonNull String mName4;
+        private @Nullable AccessibilityNodeInfo mOtherParcelable;
+        private @NonNull Date mDate;
+        private @NonNull Pattern mPattern;
+        private @NonNull List<LinkAddress> mLinkAddresses2;
+        private @NonNull ArrayList<LinkAddress> mLinkAddresses;
+        private @Nullable LinkAddress[] mLinkAddresses4;
+        private @StateName @NonNull String mStateName;
+        private @RequestFlags int mFlags;
+        private @State int mState;
+        private @NonNull CharSequence charSeq;
+        private @Nullable LinkAddress[] mLinkAddresses5;
+        private @StringRes int mStringRes;
+        private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek;
+        private @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] mCoords;
 
-        protected long mBuilderFieldsSet = 0L;
+        private long mBuilderFieldsSet = 0L;
 
-        public Builder() {};
+        /**
+         * Creates a new Builder.
+         *
+         * @param num
+         *   Any property javadoc should go onto the field, and will be copied where appropriate,
+         *   including getters, constructor parameters, builder setters, etc.
+         *
+         *   <p>
+         *   This allows to avoid the burden of maintaining copies of the same documentation
+         *   pieces in multiple places for each field.
+         * @param num2
+         *   Various javadoc features should work as expected when copied, e.g {@code code},
+         *   {@link #mName links}, <a href="https://google.com">html links</a>, etc.
+         * @param num4
+         *   {@code @hide} javadoc annotation is also propagated, which can be used to adjust the
+         *   desired public API surface.
+         * @param name
+         *   {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields.
+         * @param flags
+         *   Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value.
+         * @param linkAddresses5
+         *   Final fields suppress generating a setter (when setters are requested).
+         */
+        public Builder(
+                int num,
+                int num2,
+                int num4,
+                @Nullable String name,
+                @RequestFlags int flags,
+                @Nullable LinkAddress[] linkAddresses5) {
+            mNum = num;
+            mNum2 = num2;
+            mNum4 = num4;
+            mName = name;
+            mFlags = flags;
+
+            Preconditions.checkFlagsArgument(
+                    mFlags,
+                    FLAG_MANUAL_REQUEST
+                            | FLAG_COMPATIBILITY_MODE_REQUEST
+                            | FLAG_AUGMENTED_REQUEST);
+            mLinkAddresses5 = linkAddresses5;
+        }
 
         /**
          * Any property javadoc should go onto the field, and will be copied where appropriate,
@@ -1143,7 +1434,7 @@
          * pieces in multiple places for each field.
          */
         @DataClass.Generated.Member
-        public Builder setNum(int value) {
+        public @NonNull Builder setNum(int value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x1;
             mNum = value;
@@ -1157,7 +1448,7 @@
          * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks.
          */
         @DataClass.Generated.Member
-        public Builder setNum2(int value) {
+        public @NonNull Builder setNum2(int value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x2;
             mNum2 = value;
@@ -1173,7 +1464,7 @@
          * @hide
          */
         @DataClass.Generated.Member
-        public Builder setNum4(int value) {
+        public @NonNull Builder setNum4(int value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x4;
             mNum4 = value;
@@ -1181,11 +1472,10 @@
         }
 
         /**
-         * {@link Nullable} fields are considered optional and will not throw an exception if omitted
-         * (or set to null) when creating an instance either via a {@link Builder} or constructor.
+         * {@link Nullable} or {@link NonNull} annotation is required on all non-primitive fields.
          */
         @DataClass.Generated.Member
-        public Builder setName(@Nullable String value) {
+        public @NonNull Builder setName(@Nullable String value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x8;
             mName = value;
@@ -1193,11 +1483,14 @@
         }
 
         /**
-         * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically
+         * Fields with default value expressions ("mFoo = ...") are optional, and are automatically
          * initialized to the provided default expression, unless explicitly set.
+         *
+         * When using a {@link Builder} optional fields are passed via a {@link Builder#setName2 setter}
+         * while mandatory fields are passed via {@link Builder#Builder constructor}.
          */
         @DataClass.Generated.Member
-        public Builder setName2(String value) {
+        public @NonNull Builder setName2(@NonNull String value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x10;
             mName2 = value;
@@ -1205,12 +1498,11 @@
         }
 
         /**
-         * Fields without {@link Nullable} annotation or default value are considered required.
-         *
-         * {@link NonNull} annotation is recommended on such non-primitive fields for documentation.
+         * Alternatively, when default value computation is expensive,
+         * {@link #defaultName4 defaultFieldName()} can be defined to compute the default value.
          */
         @DataClass.Generated.Member
-        public Builder setName4(@NonNull String value) {
+        public @NonNull Builder setName4(@NonNull String value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x20;
             mName4 = value;
@@ -1222,7 +1514,7 @@
          * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc.
          */
         @DataClass.Generated.Member
-        public Builder setOtherParcelable(AccessibilityNodeInfo value) {
+        public @NonNull Builder setOtherParcelable(@Nullable AccessibilityNodeInfo value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x40;
             mOtherParcelable = value;
@@ -1233,10 +1525,10 @@
          * Additionally, support for parcelling other types can be added by implementing a
          * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation.
          *
-         * @see DateParcelling an example {@link Parcelling} implementation
+         * @see MyDateParcelling an example {@link Parcelling} implementation
          */
         @DataClass.Generated.Member
-        public Builder setDate(Date value) {
+        public @NonNull Builder setDate(@NonNull Date value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x80;
             mDate = value;
@@ -1248,7 +1540,7 @@
          * to encourage its reuse.
          */
         @DataClass.Generated.Member
-        public Builder setPattern(Pattern value) {
+        public @NonNull Builder setPattern(@NonNull Pattern value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x100;
             mPattern = value;
@@ -1261,7 +1553,7 @@
          * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience.
          */
         @DataClass.Generated.Member
-        public Builder setLinkAddresses2(List<LinkAddress> value) {
+        public @NonNull Builder setLinkAddresses2(@NonNull List<LinkAddress> value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x200;
             mLinkAddresses2 = value;
@@ -1270,7 +1562,7 @@
 
         /** @see #setLinkAddresses2 */
         @DataClass.Generated.Member
-        public Builder addLinkAddresses2(@NonNull LinkAddress value) {
+        public @NonNull Builder addLinkAddresses2(LinkAddress value) {
             // You can refine this method's name by providing item's singular name, e.g.:
             // @DataClass.PluralOf("item")) mItems = ...
 
@@ -1286,7 +1578,7 @@
          * @see Builder#addLinkAddress(LinkAddress)
          */
         @DataClass.Generated.Member
-        public Builder setLinkAddresses(ArrayList<LinkAddress> value) {
+        public @NonNull Builder setLinkAddresses(@NonNull ArrayList<LinkAddress> value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x400;
             mLinkAddresses = value;
@@ -1295,7 +1587,7 @@
 
         /** @see #setLinkAddresses */
         @DataClass.Generated.Member
-        public Builder addLinkAddress(@NonNull LinkAddress value) {
+        public @NonNull Builder addLinkAddress(LinkAddress value) {
             if (mLinkAddresses == null) setLinkAddresses(new ArrayList<>());
             mLinkAddresses.add(value);
             return this;
@@ -1308,7 +1600,7 @@
          * @see Builder#setLinkAddresses4(LinkAddress...)
          */
         @DataClass.Generated.Member
-        public Builder setLinkAddresses4(@Nullable LinkAddress... value) {
+        public @NonNull Builder setLinkAddresses4(@Nullable LinkAddress... value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x800;
             mLinkAddresses4 = value;
@@ -1316,30 +1608,6 @@
         }
 
         /**
-         * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods
-         * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated.
-         */
-        @DataClass.Generated.Member
-        public Builder setActive(boolean value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x1000;
-            mActive = value;
-            return this;
-        }
-
-        /** @see #setActive */
-        @DataClass.Generated.Member
-        public Builder markActive() {
-            return setActive(true);
-        }
-
-        /** @see #setActive */
-        @DataClass.Generated.Member
-        public Builder markNotActive() {
-            return setActive(false);
-        }
-
-        /**
          * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to
          * getter/constructor/setter/builder parameters, making for a nicer api.
          *
@@ -1347,9 +1615,9 @@
          * @see Builder#setStateName
          */
         @DataClass.Generated.Member
-        public Builder setStateName(@StateName String value) {
+        public @NonNull Builder setStateName(@StateName @NonNull String value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x2000;
+            mBuilderFieldsSet |= 0x1000;
             mStateName = value;
             return this;
         }
@@ -1358,9 +1626,9 @@
          * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value.
          */
         @DataClass.Generated.Member
-        public Builder setFlags(@RequestFlags int value) {
+        public @NonNull Builder setFlags(@RequestFlags int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4000;
+            mBuilderFieldsSet |= 0x2000;
             mFlags = value;
             return this;
         }
@@ -1369,9 +1637,9 @@
          * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s
          */
         @DataClass.Generated.Member
-        public Builder setState(@State int value) {
+        public @NonNull Builder setState(@State int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x8000;
+            mBuilderFieldsSet |= 0x4000;
             mState = value;
             return this;
         }
@@ -1380,9 +1648,9 @@
          * Making a field public will suppress getter generation in favor of accessing it directly.
          */
         @DataClass.Generated.Member
-        public Builder setCharSeq(CharSequence value) {
+        public @NonNull Builder setCharSeq(@NonNull CharSequence value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x10000;
+            mBuilderFieldsSet |= 0x8000;
             charSeq = value;
             return this;
         }
@@ -1391,9 +1659,9 @@
          * Final fields suppress generating a setter (when setters are requested).
          */
         @DataClass.Generated.Member
-        public Builder setLinkAddresses5(LinkAddress... value) {
+        public @NonNull Builder setLinkAddresses5(@Nullable LinkAddress... value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20000;
+            mBuilderFieldsSet |= 0x10000;
             mLinkAddresses5 = value;
             return this;
         }
@@ -1411,9 +1679,9 @@
          * @see #SampleDataClass
          */
         @DataClass.Generated.Member
-        public Builder setStringRes(@StringRes int value) {
+        public @NonNull Builder setStringRes(@StringRes int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40000;
+            mBuilderFieldsSet |= 0x20000;
             mStringRes = value;
             return this;
         }
@@ -1426,10 +1694,10 @@
          * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int)
          */
         @DataClass.Generated.Member
-        public Builder setLimited(@android.annotation.IntRange(from = 0, to = 4) int value) {
+        public @NonNull Builder setDayOfWeek(@android.annotation.IntRange(from = 0, to = 6) int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x80000;
-            mLimited = value;
+            mBuilderFieldsSet |= 0x40000;
+            mDayOfWeek = value;
             return this;
         }
 
@@ -1443,30 +1711,23 @@
          * @see AnnotationValidations#validate(Class, Size, int, String, int)
          */
         @DataClass.Generated.Member
-        public Builder setCoords(@Size(2) @FloatRange(from = 0f) float... value) {
+        public @NonNull Builder setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x100000;
+            mBuilderFieldsSet |= 0x80000;
             mCoords = value;
             return this;
         }
 
         /** Builds the instance. This builder should not be touched after calling this! */
         public SampleDataClass build() {
-            markUsed();
-            if ((mBuilderFieldsSet & 0x1) == 0) {
-                throw new IllegalStateException("Required field not set: num");
-            }
-            if ((mBuilderFieldsSet & 0x2) == 0) {
-                throw new IllegalStateException("Required field not set: num2");
-            }
-            if ((mBuilderFieldsSet & 0x4) == 0) {
-                throw new IllegalStateException("Required field not set: num4");
-            }
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100000; // Mark builder used
+
             if ((mBuilderFieldsSet & 0x10) == 0) {
                 mName2 = "Bob";
             }
             if ((mBuilderFieldsSet & 0x20) == 0) {
-                throw new IllegalStateException("Required field not set: name4");
+                mName4 = defaultName4();
             }
             if ((mBuilderFieldsSet & 0x40) == 0) {
                 mOtherParcelable = null;
@@ -1487,30 +1748,21 @@
                 mLinkAddresses4 = null;
             }
             if ((mBuilderFieldsSet & 0x1000) == 0) {
-                mActive = true;
-            }
-            if ((mBuilderFieldsSet & 0x2000) == 0) {
                 mStateName = STATE_NAME_UNDEFINED;
             }
             if ((mBuilderFieldsSet & 0x4000) == 0) {
-                throw new IllegalStateException("Required field not set: flags");
-            }
-            if ((mBuilderFieldsSet & 0x8000) == 0) {
                 mState = STATE_UNDEFINED;
             }
-            if ((mBuilderFieldsSet & 0x10000) == 0) {
+            if ((mBuilderFieldsSet & 0x8000) == 0) {
                 charSeq = "";
             }
             if ((mBuilderFieldsSet & 0x20000) == 0) {
-                throw new IllegalStateException("Required field not set: linkAddresses5");
-            }
-            if ((mBuilderFieldsSet & 0x40000) == 0) {
                 mStringRes = 0;
             }
-            if ((mBuilderFieldsSet & 0x80000) == 0) {
-                mLimited = 3;
+            if ((mBuilderFieldsSet & 0x40000) == 0) {
+                mDayOfWeek = 3;
             }
-            if ((mBuilderFieldsSet & 0x100000) == 0) {
+            if ((mBuilderFieldsSet & 0x80000) == 0) {
                 mCoords = new float[] { 0f, 0f };
             }
             SampleDataClass o = new SampleDataClass(
@@ -1526,17 +1778,31 @@
                     mLinkAddresses2,
                     mLinkAddresses,
                     mLinkAddresses4,
-                    mActive,
                     mStateName,
                     mFlags,
                     mState,
                     charSeq,
                     mLinkAddresses5,
                     mStringRes,
-                    mLimited,
+                    mDayOfWeek,
                     mCoords);
             return o;
         }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x100000) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
     }
 
+    @DataClass.Generated(
+            time = 1565048798524L,
+            codegenVersion = "1.0.0",
+            sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
+            inputSignatures = "public static final  java.lang.String STATE_NAME_UNDEFINED\npublic static final  java.lang.String STATE_NAME_ON\npublic static final  java.lang.String STATE_NAME_OFF\npublic static final  int STATE_UNDEFINED\npublic static final  int STATE_ON\npublic static final  int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate  int mNum\nprivate  int mNum2\nprivate  int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient  android.net.LinkAddress[] mLinkAddresses6\ntransient  int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static  java.lang.String defaultName4()\nprivate  int[] lazyInitTmpStorage()\npublic  android.net.LinkAddress[] getLinkAddresses4()\nprivate  boolean patternEquals(java.util.regex.Pattern)\nprivate  int patternHashCode()\nprivate  void onConstructed()\npublic  void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
 }
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java
index 71e85ab..6636207 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java
@@ -49,7 +49,7 @@
     private SampleDataClass mSpecimen = newBuilder().build();
 
     private static SampleDataClass.Builder newBuilder() {
-        return newIncompleteBuilder()
+        return newInvalidBuilder()
                 .setNum(42)
                 .setNum2(42)
                 .setNum4(42)
@@ -57,9 +57,8 @@
                 .setLinkAddresses5();
     }
 
-    private static SampleDataClass.Builder newIncompleteBuilder() {
-        return new SampleDataClass.Builder()
-                .markActive()
+    private static SampleDataClass.Builder newInvalidBuilder() {
+        return new SampleDataClass.Builder(1, 2, 3, "a", 0, null)
                 .setName("some parcelable")
                 .setFlags(SampleDataClass.FLAG_MANUAL_REQUEST);
     }
@@ -91,7 +90,7 @@
     public void testCustomParcelling_instanceIsCached() {
         parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR);
         parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR);
-        assertEquals(1, DateParcelling.sInstanceCount.get());
+        assertEquals(1, MyDateParcelling.sInstanceCount.get());
     }
 
     @Test
@@ -149,8 +148,8 @@
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testBuilder_throwsWhenRequiredFieldMissing() {
-        newIncompleteBuilder().build();
+    public void testBuilder_performsValidation() {
+        newInvalidBuilder().build();
     }
 
     @Test
@@ -205,6 +204,11 @@
         assertSame(tmpStorage, tmpStorageAgain);
     }
 
+    @Test(expected = IllegalStateException.class)
+    public void testCustomAnnotationValidation_isRun() {
+        newBuilder().setDayOfWeek(42).build();
+    }
+
     private static <T extends Parcelable> T parcelAndUnparcel(
             T original, Parcelable.Creator<T> creator) {
         Parcel p = Parcel.obtain();
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
new file mode 100644
index 0000000..d88035c
--- /dev/null
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.codegentest;
+
+import android.annotation.NonNull;
+import android.os.SystemClock;
+
+import com.android.internal.util.DataClass;
+
+import java.util.concurrent.TimeUnit;
+
+@DataClass(genBuilder = true)
+public class SampleWithCustomBuilder {
+
+    long delayAmount = 0;
+    @NonNull
+    TimeUnit delayUnit = TimeUnit.MILLISECONDS;
+
+    long creationTimestamp = SystemClock.uptimeMillis();
+
+    /**
+     * You can declare a class named {@code BaseBuilder} to have the generated builder extend from
+     * it instead.
+     *
+     * Same rules apply where defining a non-abstract method will suppress the generation of a
+     * method with the same signature.
+     *
+     * For abstract generatable methods, implementations are generated as normal, but original
+     * visibility is used, allowing you to hide methods.
+     *
+     * Here for example, we hide {@link #setDelayUnit} and {@link #setDelayAmount} from public API,
+     * replacing it with {@link #setDelay} instead.
+     */
+    // Suppress setter generation for a field that is not supposed to come from user input.
+    @DataClass.Suppress("setCreationTimestamp")
+    static abstract class BaseBuilder {
+
+        /**
+         * Hide methods by declaring them with reduced (package-private) visibility.
+         */
+        abstract Builder setDelayAmount(long value);
+
+        /**
+         * Alternatively, hide methods by using @hide, to hide them from public API only.
+         *
+         * @hide
+         */
+        public abstract Builder setDelayUnit(TimeUnit value);
+
+        /**
+         * Can provide additional method on the builder, e.g. as a replacement for the ones we've
+         * just hidden.
+         */
+        public Builder setDelay(long amount, TimeUnit unit) {
+            setDelayAmount(amount);
+            setDelayUnit(unit);
+            return (Builder) this;
+        }
+    }
+
+
+
+    // Code below generated by codegen v1.0.0.
+    //
+    // DO NOT MODIFY!
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+    //
+    // CHECKSTYLE:OFF Generated code
+
+    @DataClass.Generated.Member
+    /* package-private */ SampleWithCustomBuilder(
+            long delayAmount,
+            @NonNull TimeUnit delayUnit,
+            long creationTimestamp) {
+        this.delayAmount = delayAmount;
+        this.delayUnit = delayUnit;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, delayUnit);
+        this.creationTimestamp = creationTimestamp;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public long getDelayAmount() {
+        return delayAmount;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull TimeUnit getDelayUnit() {
+        return delayUnit;
+    }
+
+    @DataClass.Generated.Member
+    public long getCreationTimestamp() {
+        return creationTimestamp;
+    }
+
+    /**
+     * A builder for {@link SampleWithCustomBuilder}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder extends BaseBuilder {
+
+        private long delayAmount;
+        private @NonNull TimeUnit delayUnit;
+        private long creationTimestamp;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        @DataClass.Generated.Member
+        @Override
+        @NonNull Builder setDelayAmount(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            delayAmount = value;
+            return this;
+        }
+
+        @DataClass.Generated.Member
+        @Override
+        public @NonNull Builder setDelayUnit(@NonNull TimeUnit value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            delayUnit = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public SampleWithCustomBuilder build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                delayAmount = 0;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                delayUnit = TimeUnit.MILLISECONDS;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                creationTimestamp = SystemClock.uptimeMillis();
+            }
+            SampleWithCustomBuilder o = new SampleWithCustomBuilder(
+                    delayAmount,
+                    delayUnit,
+                    creationTimestamp);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1565048799396L,
+            codegenVersion = "1.0.0",
+            sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
+            inputSignatures = "  long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n  long creationTimestamp\nclass SampleWithCustomBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true)\nabstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic  com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
+    @Deprecated
+    private void __metadata() {}
+
+}
diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt
index 7ee79f6..578fb28 100644
--- a/tools/codegen/src/com/android/codegen/ClassInfo.kt
+++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt
@@ -18,13 +18,7 @@
                 e)
     }
     val classAst = fileAst.types[0] as ClassOrInterfaceDeclaration
-
-    fun hasMethod(name: String, vararg argTypes: String): Boolean {
-        return classAst.methods.any {
-            it.name.asString() == name &&
-                    it.parameters.map { it.type.asString() } == argTypes.toList()
-        }
-    }
+    val nestedClasses = classAst.members.filterIsInstance<ClassOrInterfaceDeclaration>()
 
     val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration)
             .implementedTypes.map { it.asString() }
@@ -42,8 +36,4 @@
             .filterNot { it.isTransient || it.isStatic }
             .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) }
             .apply { lastOrNull()?.isLast = true }
-    val lazyTransientFields = classAst.fields
-            .filter { it.isTransient && !it.isStatic }
-            .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) }
-            .filter { hasMethod("lazyInit${it.NameUpperCamel}") }
 }
\ No newline at end of file
diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
index 33256b7..f1645ea 100644
--- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt
+++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
@@ -1,9 +1,9 @@
 package com.android.codegen
 
+import com.github.javaparser.ast.Modifier
 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
 import com.github.javaparser.ast.body.TypeDeclaration
-import com.github.javaparser.ast.expr.BooleanLiteralExpr
-import com.github.javaparser.ast.expr.NormalAnnotationExpr
+import com.github.javaparser.ast.expr.*
 import com.github.javaparser.ast.type.ClassOrInterfaceType
 
 /**
@@ -32,10 +32,31 @@
     val PluralOf by lazy { classRef("com.android.internal.util.DataClass.PluralOf") }
     val Each by lazy { classRef("com.android.internal.util.DataClass.Each") }
     val DataClassGenerated by lazy { classRef("com.android.internal.util.DataClass.Generated") }
+    val DataClassSuppressConstDefs by lazy { classRef("com.android.internal.util.DataClass.SuppressConstDefsGeneration") }
+    val DataClassSuppress by lazy { classRef("com.android.internal.util.DataClass.Suppress") }
     val GeneratedMember by lazy { classRef("com.android.internal.util.DataClass.Generated.Member") }
     val Parcelling by lazy { classRef("com.android.internal.util.Parcelling") }
+    val Parcelable by lazy { classRef("android.os.Parcelable") }
     val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") }
 
+    init {
+        val fieldsWithMissingNullablity = fields.filter { field ->
+            !field.isPrimitive
+                    && Modifier.TRANSIENT !in field.fieldAst.modifiers
+                    && "@$Nullable" !in field.annotations
+                    && "@$NonNull" !in field.annotations
+        }
+        if (fieldsWithMissingNullablity.isNotEmpty()) {
+            abort("Non-primitive fields must have @$Nullable or @$NonNull annotation.\n" +
+                    "Missing nullability annotations on: "
+                    + fieldsWithMissingNullablity.joinToString(", ") { it.name })
+        }
+
+        if (!classAst.isFinal &&
+                classAst.extendedTypes.any { it.nameAsString == Parcelable }) {
+            abort("Parcelable classes must be final")
+        }
+    }
 
     /**
      * Optionally shortens a class reference if there's a corresponding import present
@@ -54,7 +75,7 @@
             return simpleName
         } else {
             val outerClass = pkg.substringAfterLast(".", "")
-            if (outerClass.firstOrNull()?.isUpperCase() ?: false) {
+            if (outerClass.firstOrNull()?.isUpperCase() == true) {
                 return classRef(pkg) + "." + simpleName
             }
         }
@@ -89,7 +110,9 @@
             ?.toMap()
             ?: emptyMap()
 
-    val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, Each, UnsupportedAppUsage)
+    val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, UnsupportedAppUsage,
+            DataClassSuppressConstDefs)
+    val knownNonValidationAnnotations = internalAnnotations + Each + Nullable
 
     /**
      * @return whether the given feature is enabled
@@ -109,7 +132,9 @@
         return when (this) {
             FeatureFlag.SETTERS ->
                 !FeatureFlag.CONSTRUCTOR() && !FeatureFlag.BUILDER() && fields.any { !it.isFinal }
-            FeatureFlag.BUILDER -> cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS) || onByDefault
+            FeatureFlag.BUILDER -> cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS)
+                    || fields.any { it.hasDefault }
+                    || onByDefault
             FeatureFlag.CONSTRUCTOR -> !FeatureFlag.BUILDER()
             FeatureFlag.PARCELABLE -> "Parcelable" in superInterfaces
             FeatureFlag.AIDL -> FeatureFlag.PARCELABLE()
@@ -287,6 +312,48 @@
 
     var BuilderClass = CANONICAL_BUILDER_CLASS
     var BuilderType = BuilderClass + genericArgs
+    val customBaseBuilderAst: ClassOrInterfaceDeclaration? by lazy {
+        nestedClasses.find { it.nameAsString == BASE_BUILDER_CLASS }
+    }
+
+    val suppressedMembers by lazy {
+        getSuppressedMembers(classAst)
+    }
+    val builderSuppressedMembers by lazy {
+        getSuppressedMembers(customBaseBuilderAst)
+    }
+
+    private fun getSuppressedMembers(clazz: ClassOrInterfaceDeclaration?): List<String> {
+        return clazz
+                ?.annotations
+                ?.find { it.nameAsString == DataClassSuppress }
+                ?.as_<SingleMemberAnnotationExpr>()
+                ?.memberValue
+                ?.run {
+                    when (this) {
+                        is ArrayInitializerExpr -> values.map { it.asLiteralStringValueExpr().value }
+                        is StringLiteralExpr -> listOf(value)
+                        else -> abort("Can't parse annotation arg: $this")
+                    }
+                }
+                ?: emptyList()
+    }
+
+    fun isMethodGenerationSuppressed(name: String, vararg argTypes: String): Boolean {
+        return name in suppressedMembers || hasMethod(name, *argTypes)
+    }
+
+    fun hasMethod(name: String, vararg argTypes: String): Boolean {
+        return classAst.methods.any {
+            it.name.asString() == name &&
+                    it.parameters.map { it.type.asString() } == argTypes.toList()
+        }
+    }
+
+    val lazyTransientFields = classAst.fields
+            .filter { it.isTransient && !it.isStatic }
+            .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) }
+            .filter { hasMethod("lazyInit${it.NameUpperCamel}") }
 
     init {
         val builderFactoryOverride = classAst.methods.find {
@@ -301,7 +368,7 @@
                 it.nameAsString == CANONICAL_BUILDER_CLASS
             }
             if (builderExtension != null) {
-                BuilderClass = GENERATED_BUILDER_CLASS
+                BuilderClass = BASE_BUILDER_CLASS
                 val tp = (builderExtension as ClassOrInterfaceDeclaration).typeParameters
                 BuilderType = if (tp.isEmpty()) BuilderClass
                 else "$BuilderClass<${tp.map { it.nameAsString }.joinToString(", ")}>"
diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt
index f326fd5..74e7948 100644
--- a/tools/codegen/src/com/android/codegen/FieldInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt
@@ -9,7 +9,6 @@
 import com.github.javaparser.ast.type.ArrayType
 import com.github.javaparser.ast.type.ClassOrInterfaceType
 import com.github.javaparser.javadoc.Javadoc
-import java.lang.Long
 
 data class FieldInfo(
     val index: Int,
@@ -85,7 +84,7 @@
             variableAst.initializer.orElse(null)?.let { return it }
             classInfo.classAst.methods.find {
                 it.nameAsString == "default$NameUpperCamel" && it.parameters.isEmpty()
-            }?.run { "$nameAsString()" }?.let { return it }
+            }?.run { return "$nameAsString()" }
             if (FieldClass == "List") return "${classPrinter.memberRef("java.util.Collections.emptyList")}()"
             return null
         }
@@ -95,7 +94,7 @@
     // Generic args
     val isArray = Type.endsWith("[]")
     val isList = FieldClass == "List" || FieldClass == "ArrayList"
-    val fieldBit = "0x${Long.toHexString(1L shl index)}"
+    val fieldBit = bitAtExpr(index)
     var isLast = false
     val isFinal = fieldAst.isFinal
     val fieldTypeGenegicArgs = when (typeAst) {
@@ -143,8 +142,10 @@
     }
     val annotationsAndType by lazy { (annotationsNoInternal + Type).joinToString(" ") }
     val sParcelling by lazy { customParcellingClass?.let { "sParcellingFor$NameUpperCamel" } }
+
+    val SetterParamType = if (isArray) "$FieldInnerType..." else Type
     val annotatedTypeForSetterParam by lazy {
-        (annotationsNoInternal + if (isArray) "$FieldInnerType..." else Type).joinToString(" ")
+        (annotationsNoInternal + SetterParamType).joinToString(" ")
     }
 
     // Utilities
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index ab64f4e..c6e0a06 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -1,6 +1,7 @@
 package com.android.codegen
 
 import com.github.javaparser.ast.body.FieldDeclaration
+import com.github.javaparser.ast.body.MethodDeclaration
 import com.github.javaparser.ast.body.VariableDeclarator
 import com.github.javaparser.ast.expr.*
 import java.io.File
@@ -16,7 +17,7 @@
             val isLiteral = initializer is LiteralExpr
                     || (initializer is UnaryExpr && initializer.expression is LiteralExpr)
             isLiteral && variable.type.asString() in listOf("int", "String")
-        }
+        } && it.annotations.none { it.nameAsString == DataClassSuppressConstDefs }
     }.flatMap { field -> field.variables.map { it to field } }
     val intConsts = consts.filter { it.first.type.asString() == "int" }
     val strConsts = consts.filter { it.first.type.asString() == "String" }
@@ -131,7 +132,7 @@
 fun ClassPrinter.generateWithers() {
     fields.forEachApply {
         val metodName = "with$NameUpperCamel"
-        if (!hasMethod(metodName, Type)) {
+        if (!isMethodGenerationSuppressed(metodName, Type)) {
             generateFieldJavadoc(forceHide = FeatureFlag.WITHERS.hidden)
             """@$NonNull
                         $GENERATED_MEMBER_HEADER
@@ -171,7 +172,7 @@
  * ```
  */
 fun ClassPrinter.generateBuildUpon() {
-    if (hasMethod("buildUpon")) return
+    if (isMethodGenerationSuppressed("buildUpon")) return
 
     +"/**"
     +" * Provides an instance of {@link $BuilderClass} with state corresponding to this instance."
@@ -195,7 +196,15 @@
     val constructorVisibility = if (BuilderClass == CANONICAL_BUILDER_CLASS)
         "public" else "/* package-*/"
 
-    val OneTimeUseBuilder = classRef("android.provider.OneTimeUseBuilder")
+    val providedSubclassAst = nestedClasses.find {
+        it.extendedTypes.any { it.nameAsString == BASE_BUILDER_CLASS }
+    }
+
+    val BuilderSupertype = if (customBaseBuilderAst != null) {
+        customBaseBuilderAst!!.nameAsString
+    } else {
+        "Object"
+    }
 
     +"/**"
     +" * A builder for {@link $ClassName}"
@@ -203,104 +212,155 @@
     +" */"
     +"@SuppressWarnings(\"WeakerAccess\")"
     +GENERATED_MEMBER_HEADER
-    "public static class $BuilderClass$genericArgs" {
-        +"extends $OneTimeUseBuilder<$ClassType>"
+    !"public static class $BuilderClass$genericArgs"
+    if (BuilderSupertype != "Object") {
+        appendSameLine(" extends $BuilderSupertype")
     }
     " {" {
 
         +""
         fields.forEachApply {
-            +"protected $annotationsAndType $name;"
+            +"private $annotationsAndType $name;"
         }
         +""
-        +"protected long mBuilderFieldsSet = 0L;"
+        +"private long mBuilderFieldsSet = 0L;"
         +""
-        +"$constructorVisibility $BuilderClass() {};"
-        +""
+
+        val requiredFields = fields.filter { !it.hasDefault }
+
+        generateConstructorJavadoc(
+                fields = requiredFields,
+                ClassName = BuilderClass,
+                hidden = false)
+        "$constructorVisibility $BuilderClass(" {
+            requiredFields.forEachLastAware { field, isLast ->
+                +"${field.annotationsAndType} ${field._name}${if_(!isLast, ",")}"
+            }
+        }; " {" {
+            requiredFields.forEachApply {
+                generateSetFrom(_name)
+            }
+        }
 
         generateBuilderSetters(setterVisibility)
 
         generateBuilderBuild()
 
+        "private void checkNotUsed() {" {
+            "if ((mBuilderFieldsSet & ${bitAtExpr(fields.size)}) != 0)" {
+                "throw new IllegalStateException(" {
+                    +"\"This Builder should not be reused. Use a new Builder instance instead\""
+                }
+                +";"
+            }
+        }
+
         rmEmptyLine()
     }
 }
 
+private fun ClassPrinter.generateBuilderMethod(
+        defVisibility: String,
+        name: String,
+        ParamAnnotations: String? = null,
+        paramTypes: List<String>,
+        paramNames: List<String> = listOf("value"),
+        genJavadoc: ClassPrinter.() -> Unit,
+        genBody: ClassPrinter.() -> Unit) {
+
+    val providedMethod = customBaseBuilderAst?.members?.find {
+        it is MethodDeclaration
+                && it.nameAsString == name
+                && it.parameters.map { it.typeAsString } == paramTypes.toTypedArray().toList()
+    } as? MethodDeclaration
+
+    if ((providedMethod == null || providedMethod.isAbstract)
+            && name !in builderSuppressedMembers) {
+        val visibility = providedMethod?.visibility?.asString() ?: defVisibility
+        val ReturnType = providedMethod?.typeAsString ?: CANONICAL_BUILDER_CLASS
+        val Annotations = providedMethod?.annotations?.joinToString("\n")
+
+        genJavadoc()
+        +GENERATED_MEMBER_HEADER
+        if (providedMethod?.isAbstract == true) +"@Override"
+        if (!Annotations.isNullOrEmpty()) +Annotations
+        "$visibility @$NonNull $ReturnType $name(${if_(!ParamAnnotations.isNullOrEmpty(), "$ParamAnnotations ")}${
+                paramTypes.zip(paramNames).joinToString(", ") { (Type, paramName) -> "$Type $paramName" }
+        })" {
+            genBody()
+        }
+    }
+}
+
 private fun ClassPrinter.generateBuilderSetters(visibility: String) {
 
     fields.forEachApply {
         val maybeCast =
                 if_(BuilderClass != CANONICAL_BUILDER_CLASS, " ($CANONICAL_BUILDER_CLASS)")
 
-        generateFieldJavadoc()
-        +GENERATED_MEMBER_HEADER
-        "$visibility $CANONICAL_BUILDER_CLASS set$NameUpperCamel($annotatedTypeForSetterParam value)" {
+        val setterName = "set$NameUpperCamel"
+
+        generateBuilderMethod(
+                name = setterName,
+                defVisibility = visibility,
+                ParamAnnotations = annotationsNoInternal.joinToString(" "),
+                paramTypes = listOf(SetterParamType),
+                genJavadoc = { generateFieldJavadoc() }) {
             +"checkNotUsed();"
             +"mBuilderFieldsSet |= $fieldBit;"
             +"$name = value;"
             +"return$maybeCast this;"
         }
 
+        val javadocSeeSetter = "/** @see #$setterName */"
+        val adderName = "add$SingularName"
 
-        val javadocSeeSetter = "/** @see #set$NameUpperCamel */"
         val singularNameCustomizationHint = if (SingularNameOrNull == null) {
             "// You can refine this method's name by providing item's singular name, e.g.:\n" +
                     "// @DataClass.PluralOf(\"item\")) mItems = ...\n\n"
         } else ""
 
-        if (isList && FieldInnerType != null) {
 
-            +javadocSeeSetter
-            +GENERATED_MEMBER_HEADER
-            "$visibility $CANONICAL_BUILDER_CLASS add$SingularName(@$NonNull $FieldInnerType value)" {
+        if (isList && FieldInnerType != null) {
+            generateBuilderMethod(
+                    name = adderName,
+                    defVisibility = visibility,
+                    paramTypes = listOf(FieldInnerType),
+                    genJavadoc = { +javadocSeeSetter }) {
+
                 !singularNameCustomizationHint
-                +"if ($name == null) set$NameUpperCamel(new $ArrayList<>());"
+                +"if ($name == null) $setterName(new $ArrayList<>());"
                 +"$name.add(value);"
                 +"return$maybeCast this;"
             }
         }
 
         if (Type.contains("Map<")) {
-            val (Key, Value) = fieldTypeGenegicArgs
-
-            +javadocSeeSetter
-            +GENERATED_MEMBER_HEADER
-            "$visibility $CANONICAL_BUILDER_CLASS add$SingularName($Key key, $Value value)" {
+            generateBuilderMethod(
+                    name = adderName,
+                    defVisibility = visibility,
+                    paramTypes = fieldTypeGenegicArgs,
+                    paramNames = listOf("key", "value"),
+                    genJavadoc = { +javadocSeeSetter }) {
                 !singularNameCustomizationHint
-                +"if ($name == null) set$NameUpperCamel(new $LinkedHashMap());"
+                +"if ($name == null) $setterName(new $LinkedHashMap());"
                 +"$name.put(key, value);"
                 +"return$maybeCast this;"
             }
         }
-
-        if (Type == "boolean") {
-            +javadocSeeSetter
-            +GENERATED_MEMBER_HEADER
-            "$visibility $CANONICAL_BUILDER_CLASS mark$NameUpperCamel()" {
-                +"return set$NameUpperCamel(true);"
-            }
-
-            +javadocSeeSetter
-            +GENERATED_MEMBER_HEADER
-            "$visibility $CANONICAL_BUILDER_CLASS markNot$NameUpperCamel()" {
-                +"return set$NameUpperCamel(false);"
-            }
-        }
     }
 }
 
 private fun ClassPrinter.generateBuilderBuild() {
     +"/** Builds the instance. This builder should not be touched after calling this! */"
     "public $ClassType build()" {
-        +"markUsed();"
+        +"checkNotUsed();"
+        +"mBuilderFieldsSet |= ${bitAtExpr(fields.size)}; // Mark builder used"
+        +""
         fields.forEachApply {
-            if (!isNullable || hasDefault) {
+            if (hasDefault) {
                 "if ((mBuilderFieldsSet & $fieldBit) == 0)" {
-                    if (!isNullable && !hasDefault) {
-                        +"throw new IllegalStateException(\"Required field not set: $nameLowerCamel\");"
-                    } else {
-                        +"$name = $defaultExpr;"
-                    }
+                    +"$name = $defaultExpr;"
                 }
             }
         }
@@ -348,7 +408,7 @@
     }
 
     val Parcel = classRef("android.os.Parcel")
-    if (!hasMethod("writeToParcel", Parcel, "int")) {
+    if (!isMethodGenerationSuppressed("writeToParcel", Parcel, "int")) {
         +"@Override"
         +GENERATED_MEMBER_HEADER
         "public void writeToParcel($Parcel dest, int flags)" {
@@ -390,7 +450,7 @@
         }
     }
 
-    if (!hasMethod("describeContents")) {
+    if (!isMethodGenerationSuppressed("describeContents")) {
         +"@Override"
         +GENERATED_MEMBER_HEADER
         +"public int describeContents() { return 0; }"
@@ -442,8 +502,6 @@
                             FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()"
                             FieldClass == "List" || FieldClass == "ArrayList" ->
                                 "new ${classRef("java.util.ArrayList")}<>()"
-//                            isArray && FieldInnerType in (PRIMITIVE_TYPES + "String") ->
-//                                "new $FieldInnerType[in.readInt()]"
                             else -> ""
                         }
                         val passContainer = containerInitExpr.isNotEmpty()
@@ -519,7 +577,7 @@
 }
 
 fun ClassPrinter.generateEqualsHashcode() {
-    if (!hasMethod("equals", "Object")) {
+    if (!isMethodGenerationSuppressed("equals", "Object")) {
         +"@Override"
         +GENERATED_MEMBER_HEADER
         "public boolean equals(Object o)" {
@@ -546,7 +604,7 @@
         }
     }
 
-    if (!hasMethod("hashCode")) {
+    if (!isMethodGenerationSuppressed("hashCode")) {
         +"@Override"
         +GENERATED_MEMBER_HEADER
         "public int hashCode()" {
@@ -572,7 +630,7 @@
 
 //TODO support IntDef flags?
 fun ClassPrinter.generateToString() {
-    if (!hasMethod("toString")) {
+    if (!isMethodGenerationSuppressed("toString")) {
         +"@Override"
         +GENERATED_MEMBER_HEADER
         "public String toString()" {
@@ -599,7 +657,7 @@
 
 fun ClassPrinter.generateSetters() {
     fields.forEachApply {
-        if (!hasMethod("set$NameUpperCamel", Type)
+        if (!isMethodGenerationSuppressed("set$NameUpperCamel", Type)
                 && !fieldAst.isPublic
                 && !isFinal) {
 
@@ -618,7 +676,7 @@
         val methodPrefix = if (Type == "boolean") "is" else "get"
         val methodName = methodPrefix + NameUpperCamel
 
-        if (!hasMethod(methodName) && !fieldAst.isPublic) {
+        if (!isMethodGenerationSuppressed(methodName) && !fieldAst.isPublic) {
 
             generateFieldJavadoc(forceHide = FeatureFlag.GETTERS.hidden)
             +GENERATED_MEMBER_HEADER
@@ -662,23 +720,8 @@
 }
 
 fun FieldInfo.generateSetFrom(source: String) = classPrinter {
-    !"$name = "
-    if (Type in PRIMITIVE_TYPES || mayBeNull) {
-        +"$source;"
-    } else if (defaultExpr != null) {
-        "$source != null" {
-            +"? $source"
-            +": $defaultExpr;"
-        }
-    } else {
-        val checkNotNull = memberRef("com.android.internal.util.Preconditions.checkNotNull")
-        +"$checkNotNull($source);"
-    }
-    if (isNonEmpty) {
-        "if ($isEmptyExpr)" {
-            +"throw new IllegalArgumentException(\"$nameLowerCamel cannot be empty\");"
-        }
-    }
+    +"$name = $source;"
+    generateFieldValidation(field = this@generateSetFrom)
 }
 
 fun ClassPrinter.generateConstructor(visibility: String = "public") {
@@ -697,15 +740,18 @@
             generateSetFrom(nameLowerCamel)
         }
 
-        generateStateValidation()
-
         generateOnConstructedCallback()
     }
 }
 
-private fun ClassPrinter.generateConstructorJavadoc() {
+private fun ClassPrinter.generateConstructorJavadoc(
+        fields: List<FieldInfo> = this.fields,
+        ClassName: String = this.ClassName,
+        hidden: Boolean = FeatureFlag.CONSTRUCTOR.hidden) {
     if (fields.all { it.javadoc == null } && !FeatureFlag.CONSTRUCTOR.hidden) return
     +"/**"
+    +" * Creates a new $ClassName."
+    +" *"
     fields.filter { it.javadoc != null }.forEachApply {
         javadocTextNoAnnotationLines?.apply {
             +" * @param $nameLowerCamel"
@@ -718,87 +764,97 @@
     +" */"
 }
 
-private fun ClassPrinter.generateStateValidation() {
-    val Size = classRef("android.annotation.Size")
-    val knownNonValidationAnnotations = internalAnnotations + Nullable
-
-    val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
-    fun appendValidateCall(annotation: AnnotationExpr, valueToValidate: String) {
-        "$validate(" {
-            !"${annotation.nameAsString}.class, null, $valueToValidate"
-            val params = when (annotation) {
-                is MarkerAnnotationExpr -> emptyMap()
-                is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
-                is NormalAnnotationExpr ->
-                    annotation.pairs.map { it.name.asString() to it.value }.toMap()
-                else -> throw IllegalStateException()
-            }
-            params.forEach { name, value ->
-                !",\n\"$name\", $value"
+private fun ClassPrinter.appendLinesWithContinuationIndent(text: String) {
+    val lines = text.lines()
+    if (lines.isNotEmpty()) {
+        !lines[0]
+    }
+    if (lines.size >= 2) {
+        "" {
+            lines.drop(1).forEach {
+                +it
             }
         }
-        +";"
     }
+}
 
-    fields.forEachApply {
-        if (intOrStringDef != null) {
-            if (intOrStringDef!!.type == ConstDef.Type.INT_FLAGS) {
-                +""
-                +"//noinspection PointlessBitwiseExpression"
-                "$Preconditions.checkFlagsArgument(" {
-                    "$name, 0" {
-                        intOrStringDef!!.CONST_NAMES.forEach {
-                            +"| $it"
+private fun ClassPrinter.generateFieldValidation(field: FieldInfo) = field.run {
+    if (isNonEmpty) {
+        "if ($isEmptyExpr)" {
+            +"throw new IllegalArgumentException(\"$nameLowerCamel cannot be empty\");"
+        }
+    }
+    if (intOrStringDef != null) {
+        if (intOrStringDef!!.type == ConstDef.Type.INT_FLAGS) {
+            +""
+            "$Preconditions.checkFlagsArgument(" {
+                +"$name, "
+                appendLinesWithContinuationIndent(intOrStringDef!!.CONST_NAMES.joinToString("\n| "))
+            }
+            +";"
+        } else {
+            +""
+            !"if ("
+            appendLinesWithContinuationIndent(intOrStringDef!!.CONST_NAMES.joinToString("\n&& ") {
+                "!(${isEqualToExpr(it)})"
+            })
+            rmEmptyLine(); ") {" {
+                "throw new ${classRef<IllegalArgumentException>()}(" {
+                    "\"$nameLowerCamel was \" + $internalGetter + \" but must be one of: \"" {
+
+                        intOrStringDef!!.CONST_NAMES.forEachLastAware { CONST_NAME, isLast ->
+                            +"""+ "$CONST_NAME(" + $CONST_NAME + ")${if_(!isLast, ", ")}""""
                         }
                     }
                 }
                 +";"
-            } else {
-                +""
-                +"//noinspection PointlessBooleanExpression"
-                "if (true" {
-                    intOrStringDef!!.CONST_NAMES.forEach { CONST_NAME ->
-                        +"&& !(${isEqualToExpr(CONST_NAME)})"
-                    }
-                }; rmEmptyLine(); ") {" {
-                    "throw new ${classRef<IllegalArgumentException>()}(" {
-                        "\"$nameLowerCamel was \" + $internalGetter + \" but must be one of: \"" {
-
-                            intOrStringDef!!.CONST_NAMES.forEachLastAware { CONST_NAME, isLast ->
-                                +"""+ "$CONST_NAME(" + $CONST_NAME + ")${if_(!isLast, ", ")}""""
-                            }
-                        }
-                    }
-                    +";"
-                }
-            }
-        }
-
-        val eachLine = fieldAst.annotations.find { it.nameAsString == Each }?.range?.orElse(null)?.end?.line
-        val perElementValidations = if (eachLine == null) emptyList() else fieldAst.annotations.filter {
-            it.nameAsString != Each &&
-                it.range.orElse(null)?.begin?.line?.let { it >= eachLine } ?: false
-        }
-
-        fieldAst.annotations.filterNot {
-            it.nameAsString == intOrStringDef?.AnnotationName
-                    || it.nameAsString in knownNonValidationAnnotations
-                    || it in perElementValidations
-        }.forEach { annotation ->
-            appendValidateCall(annotation,
-                    valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
-        }
-
-        if (perElementValidations.isNotEmpty()) {
-            +"int ${nameLowerCamel}Size = $sizeExpr;"
-            "for (int i = 0; i < ${nameLowerCamel}Size; i++) {" {
-                perElementValidations.forEach { annotation ->
-                    appendValidateCall(annotation,
-                            valueToValidate = elemAtIndexExpr("i"))
-                }
             }
         }
     }
+
+    val eachLine = fieldAst.annotations.find { it.nameAsString == Each }?.range?.orElse(null)?.end?.line
+    val perElementValidations = if (eachLine == null) emptyList() else fieldAst.annotations.filter {
+        it.nameAsString != Each &&
+                it.range.orElse(null)?.begin?.line?.let { it >= eachLine } ?: false
+    }
+
+    val Size = classRef("android.annotation.Size")
+    fieldAst.annotations.filterNot {
+        it.nameAsString == intOrStringDef?.AnnotationName
+                || it.nameAsString in knownNonValidationAnnotations
+                || it in perElementValidations
+    }.forEach { annotation ->
+        appendValidateCall(annotation,
+                valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
+    }
+
+    if (perElementValidations.isNotEmpty()) {
+        +"int ${nameLowerCamel}Size = $sizeExpr;"
+        "for (int i = 0; i < ${nameLowerCamel}Size; i++) {" {
+            perElementValidations.forEach { annotation ->
+                appendValidateCall(annotation,
+                        valueToValidate = elemAtIndexExpr("i"))
+            }
+        }
+    }
+}
+
+fun ClassPrinter.appendValidateCall(annotation: AnnotationExpr, valueToValidate: String) {
+    val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
+    "$validate(" {
+        !"${annotation.nameAsString}.class, null, $valueToValidate"
+        val params = when (annotation) {
+            is MarkerAnnotationExpr -> emptyMap()
+            is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
+            is NormalAnnotationExpr ->
+                annotation.pairs.map { it.name.asString() to it.value }.toMap()
+            else -> throw IllegalStateException()
+        }
+        params.forEach { name, value ->
+            !",\n\"$name\", $value"
+        }
+    }
+    +";"
 }
 
 private fun ClassPrinter.generateOnConstructedCallback(prefix: String = "") {
@@ -845,3 +901,15 @@
         }
     }
 }
+
+fun ClassPrinter.generateMetadata(file: File) {
+    "@$DataClassGenerated(" {
+        +"time = ${System.currentTimeMillis()}L,"
+        +"codegenVersion = \"$CODEGEN_VERSION\","
+        +"sourceFile = \"${file.relativeTo(File(System.getenv("ANDROID_BUILD_TOP")))}\","
+        +"inputSignatures = \"${getInputSignatures().joinToString("\\n")}\""
+    }
+    +""
+    +"@Deprecated"
+    +"private void __metadata() {}\n"
+}
\ No newline at end of file
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index d1dc88f..1e7a267 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -1,6 +1,6 @@
 package com.android.codegen
 
-import com.github.javaparser.ast.body.TypeDeclaration
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
 import com.github.javaparser.ast.expr.*
 import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations
 import com.github.javaparser.ast.type.ClassOrInterfaceType
@@ -8,9 +8,17 @@
 
 
 fun ClassPrinter.getInputSignatures(): List<String> {
+    return generateInputSignaturesForClass(classAst) +
+            annotationToString(classAst.annotations.find { it.nameAsString == DataClass }) +
+            generateInputSignaturesForClass(customBaseBuilderAst)
+}
+
+private fun ClassPrinter.generateInputSignaturesForClass(classAst: ClassOrInterfaceDeclaration?): List<String> {
+    if (classAst == null) return emptyList()
+
     return classAst.fields.map { fieldAst ->
         buildString {
-            append(fieldAst.modifiers.joinToString(" ") {it.asString()})
+            append(fieldAst.modifiers.joinToString(" ") { it.asString() })
             append(" ")
             append(annotationsToString(fieldAst))
             append(" ")
@@ -20,7 +28,7 @@
         }
     } + classAst.methods.map { methodAst ->
         buildString {
-            append(methodAst.modifiers.joinToString(" ") {it.asString()})
+            append(methodAst.modifiers.joinToString(" ") { it.asString() })
             append(" ")
             append(annotationsToString(methodAst))
             append(" ")
@@ -28,19 +36,26 @@
             append(" ")
             append(methodAst.nameAsString)
             append("(")
-            append(methodAst.parameters.joinToString(",") {getFullClassName(it.type)})
+            append(methodAst.parameters.joinToString(",") { getFullClassName(it.type) })
             append(")")
         }
-    }
+    } + ("class ${classAst.nameAsString}" +
+            " extends ${classAst.extendedTypes.map { getFullClassName(it) }.ifEmpty { listOf("java.lang.Object") }.joinToString(", ")}" +
+            " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]")
 }
 
 private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String {
-    return annotatedAst.annotations.joinToString(" ") {
-        annotationToString(it)
-    }
+    return annotatedAst
+            .annotations
+            .groupBy { it.nameAsString } // dedupe annotations by name (javaparser bug?)
+            .values
+            .joinToString(" ") {
+                annotationToString(it[0])
+            }
 }
 
-private fun ClassPrinter.annotationToString(ann: AnnotationExpr): String {
+private fun ClassPrinter.annotationToString(ann: AnnotationExpr?): String {
+    if (ann == null) return ""
     return buildString {
         append("@")
         append(getFullClassName(ann.nameAsString))
@@ -78,9 +93,9 @@
 
 private fun ClassPrinter.getFullClassName(type: Type): String {
     return if (type is ClassOrInterfaceType) {
+
         getFullClassName(buildString {
             type.scope.ifPresent { append(it).append(".") }
-            type.isArrayType
             append(type.nameAsString)
         }) + (type.typeArguments.orElse(null)?.let { args -> args.joinToString(", ") {getFullClassName(it)}}?.let { "<$it>" } ?: "")
     } else getFullClassName(type.asString())
@@ -100,10 +115,16 @@
     val thisPackagePrefix = fileAst.packageDeclaration.map { it.nameAsString + "." }.orElse("")
     val thisClassPrefix = thisPackagePrefix + classAst.nameAsString + "."
 
-    classAst.childNodes.filterIsInstance<TypeDeclaration<*>>().find {
+    if (classAst.nameAsString == className) return thisPackagePrefix + classAst.nameAsString
+
+    nestedClasses.find {
         it.nameAsString == className
     }?.let { return thisClassPrefix + it.nameAsString }
 
+    if (className == CANONICAL_BUILDER_CLASS || className == BASE_BUILDER_CLASS) {
+        return thisClassPrefix + className
+    }
+
     constDefs.find { it.AnnotationName == className }?.let { return thisClassPrefix + className }
 
     if (tryOrNull { Class.forName("java.lang.$className") } != null) {
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index 8fafa7c..f71bfd3 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -9,9 +9,6 @@
 
 val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean")
 
-const val CANONICAL_BUILDER_CLASS = "Builder"
-const val GENERATED_BUILDER_CLASS = "GeneratedBuilder"
-
 val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern")
 
 const val FLAG_BUILDER_PROTECTED_SETTERS = "--builder-protected-setters"
@@ -66,10 +63,10 @@
       Will be called in constructor, after all the fields have been initialized.
       This is a good place to put any custom validation logic that you may have
 
-  static class $CANONICAL_BUILDER_CLASS extends $GENERATED_BUILDER_CLASS
-      If a class extending $GENERATED_BUILDER_CLASS is specified, generated builder's setters will
+  static class $CANONICAL_BUILDER_CLASS extends $BASE_BUILDER_CLASS
+      If a class extending $BASE_BUILDER_CLASS is specified, generated builder's setters will
       return the provided $CANONICAL_BUILDER_CLASS type.
-      $GENERATED_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead
+      $BASE_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead
       This allows you to extend the generated builder, adding or overriding any methods you may want
 
 
@@ -131,7 +128,6 @@
 
 
         // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION.
-        //   on ${currentTimestamp()}
         //
         // DO NOT MODIFY!
         //
@@ -143,14 +139,6 @@
 
         if (FeatureFlag.CONST_DEFS()) generateConstDefs()
 
-        "@$DataClassGenerated(" {
-            +"time = ${System.currentTimeMillis()}L,"
-            +"codegenVersion = \"$CODEGEN_VERSION\","
-            +"sourceFile = \"${file.relativeTo(File(System.getenv("ANDROID_BUILD_TOP")))}\","
-            +"inputSignatures = \"${getInputSignatures().joinToString("\\n")}\""
-        }
-        +"\n"
-
 
         if (FeatureFlag.CONSTRUCTOR()) {
             generateConstructor("public")
@@ -160,6 +148,7 @@
                 || FeatureFlag.PARCELABLE()) {
             generateConstructor("/* package-private */")
         }
+        if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor()
 
         if (FeatureFlag.GETTERS()) generateGetters()
         if (FeatureFlag.SETTERS()) generateSetters()
@@ -168,7 +157,6 @@
 
         if (FeatureFlag.FOR_EACH_FIELD()) generateForEachField()
 
-        if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor()
         if (FeatureFlag.WITHERS()) generateWithers()
 
         if (FeatureFlag.PARCELABLE()) generateParcelable()
@@ -178,6 +166,8 @@
 
         if (FeatureFlag.AIDL()) generateAidl(file)
 
+        generateMetadata(file)
+
         rmEmptyLine()
     }
     stringBuilder.append("\n}\n")
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 175eea6..7d50ad1 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,4 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.0"
\ No newline at end of file
+const val CODEGEN_VERSION = "1.0.0"
+
+const val CANONICAL_BUILDER_CLASS = "Builder"
+const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 95c9909..73ceac4 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -1,8 +1,10 @@
 package com.android.codegen
 
+import com.github.javaparser.ast.Modifier
 import com.github.javaparser.ast.expr.AnnotationExpr
 import com.github.javaparser.ast.expr.Expression
 import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.nodeTypes.NodeWithModifiers
 import java.time.Instant
 import java.time.ZoneId
 import java.time.format.DateTimeFormatter
@@ -22,6 +24,8 @@
 
 fun if_(cond: Boolean, then: String) = if (cond) then else ""
 
+fun <T> Any?.as_(): T = this as T
+
 inline infix fun Int.times(action: () -> Unit) {
     for (i in 1..this) action()
 }
@@ -73,4 +77,15 @@
 fun currentTimestamp() = DateTimeFormatter
         .ofLocalizedDateTime(/* date */ FormatStyle.MEDIUM, /* time */ FormatStyle.LONG)
         .withZone(ZoneId.systemDefault())
-        .format(Instant.now())
\ No newline at end of file
+        .format(Instant.now())
+
+val NodeWithModifiers<*>.visibility get() = Modifier.getAccessSpecifier(modifiers)
+
+fun abort(msg: String): Nothing {
+    System.err.println("ERROR: $msg")
+    System.exit(1)
+    throw InternalError() // can't get here
+}
+
+fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}"
+
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index e5ec17a..26b15ae 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -17,6 +17,8 @@
 
 package android.processor.staledataclass
 
+import com.android.codegen.BASE_BUILDER_CLASS
+import com.android.codegen.CANONICAL_BUILDER_CLASS
 import com.android.codegen.CODEGEN_NAME
 import com.android.codegen.CODEGEN_VERSION
 import com.sun.tools.javac.code.Symbol
@@ -29,6 +31,7 @@
 import javax.lang.model.SourceVersion
 import javax.lang.model.element.AnnotationMirror
 import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
 import javax.lang.model.element.TypeElement
 import javax.tools.Diagnostic
 
@@ -107,20 +110,30 @@
 
     private fun processSingleFile(elementAnnotatedWithGenerated: Element) {
 
-        val inputSignatures = elementAnnotatedWithGenerated
-                .enclosingElement
-                .enclosedElements
-                .filterNot {
-                    it.annotationMirrors.any { "Generated" in it.annotationType.toString() }
-                }.map {
-                    elemToString(it)
-                }.toSet()
+        val classElement = elementAnnotatedWithGenerated.enclosingElement
+
+        val inputSignatures = computeSignaturesForClass(classElement)
+                .plus(computeSignaturesForClass(classElement.enclosedElements.find {
+                    it.kind == ElementKind.CLASS
+                            && !isGenerated(it)
+                            && it.simpleName.toString() == BASE_BUILDER_CLASS
+                }))
+                .plus(computeSignaturesForClass(classElement.enclosedElements.find {
+                    it.kind == ElementKind.CLASS
+                            && !isGenerated(it)
+                            && it.simpleName.toString() == CANONICAL_BUILDER_CLASS
+                }))
+                .plus(classElement
+                        .annotationMirrors
+                        .find { it.annotationType.toString() == DATACLASS_ANNOTATION_NAME }
+                        .toString())
+                .toSet()
 
         val annotationParams = elementAnnotatedWithGenerated
                 .annotationMirrors
                 .find { ann -> isGeneratedAnnotation(ann) }!!
                 .elementValues
-                .map { (k, v) -> k.getSimpleName().toString() to v.getValue() }
+                .map { (k, v) -> k.simpleName.toString() to v.value }
                 .toMap()
 
         val lastGenerated = annotationParams["time"] as Long
@@ -140,7 +153,7 @@
         }
 
         val source = repoRoot!!.resolve(sourceRelative)
-        val clazz = elementAnnotatedWithGenerated.enclosingElement.toString()
+        val clazz = classElement.toString()
 
         if (inputSignatures != lastGenInputSignatures) {
             error(buildString {
@@ -157,6 +170,23 @@
         }
     }
 
+    private fun computeSignaturesForClass(classElement: Element?): List<String> {
+        if (classElement == null) return emptyList()
+        val type = classElement as TypeElement
+        return classElement
+                .enclosedElements
+                .filterNot {
+                    it.kind == ElementKind.CLASS
+                            || it.kind == ElementKind.CONSTRUCTOR
+                            || isGenerated(it)
+                }.map {
+                    elemToString(it)
+                } + "class ${classElement.simpleName} extends ${type.superclass} implements [${type.interfaces.joinToString(", ")}]"
+    }
+
+    private fun isGenerated(it: Element) =
+            it.annotationMirrors.any { "Generated" in it.annotationType.toString() }
+
     private fun error(msg: String) {
         processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg)
     }