Add CDM API to filter by BSSID bitmask

Per recommendation from wifi team, this is an important filter to provide

Test: manual
Fixes: 143313206
Change-Id: Ifcad0f6ce5134854114aa9df97b2c44ece858a30
diff --git a/api/current.txt b/api/current.txt
index 71dc58b..5d532d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9230,14 +9230,16 @@
 
   public final class WifiDeviceFilter implements android.companion.DeviceFilter<android.net.wifi.ScanResult> {
     method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.WifiDeviceFilter> CREATOR;
   }
 
   public static final class WifiDeviceFilter.Builder {
     ctor public WifiDeviceFilter.Builder();
     method @NonNull public android.companion.WifiDeviceFilter build();
-    method public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern);
+    method @NonNull public android.companion.WifiDeviceFilter.Builder setBssid(@Nullable android.net.MacAddress);
+    method @NonNull public android.companion.WifiDeviceFilter.Builder setBssidMask(@NonNull android.net.MacAddress);
+    method @NonNull public android.companion.WifiDeviceFilter.Builder setNamePattern(@Nullable java.util.regex.Pattern);
   }
 
 }
diff --git a/core/java/android/companion/WifiDeviceFilter.java b/core/java/android/companion/WifiDeviceFilter.java
index 62098d5..58bf874 100644
--- a/core/java/android/companion/WifiDeviceFilter.java
+++ b/core/java/android/companion/WifiDeviceFilter.java
@@ -17,17 +17,18 @@
 package android.companion;
 
 import static android.companion.BluetoothDeviceFilterUtils.getDeviceDisplayNameInternal;
-import static android.companion.BluetoothDeviceFilterUtils.patternFromString;
-import static android.companion.BluetoothDeviceFilterUtils.patternToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.le.ScanFilter;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.os.Parcel;
-import android.provider.OneTimeUseBuilder;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
 
 import java.util.Objects;
 import java.util.regex.Pattern;
@@ -37,30 +38,38 @@
  *
  * @see ScanFilter
  */
+@DataClass(
+        genParcelable = true,
+        genAidl = false,
+        genBuilder = true,
+        genEqualsHashCode = true,
+        genHiddenGetters = true)
 public final class WifiDeviceFilter implements DeviceFilter<ScanResult> {
 
-    private final Pattern mNamePattern;
+    /**
+     * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular
+     * expression will be shown
+     */
+    @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class)
+    private @Nullable Pattern mNamePattern = null;
 
-    private WifiDeviceFilter(Pattern namePattern) {
-        mNamePattern = namePattern;
-    }
+    /**
+     * If set, only devices with BSSID matching the given one will be shown
+     */
+    private @Nullable MacAddress mBssid = null;
 
-    @SuppressLint("ParcelClassLoader")
-    private WifiDeviceFilter(Parcel in) {
-        this(patternFromString(in.readString()));
-    }
-
-    /** @hide */
-    @Nullable
-    public Pattern getNamePattern() {
-        return mNamePattern;
-    }
-
+    /**
+     * If set, only bits at positions set in this mask, will be compared to the given
+     * {@link Builder#setBssid BSSID} filter.
+     */
+    private @NonNull MacAddress mBssidMask = MacAddress.BROADCAST_ADDRESS;
 
     /** @hide */
     @Override
     public boolean matches(ScanResult device) {
-        return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device);
+        return BluetoothDeviceFilterUtils.matchesName(getNamePattern(), device)
+                && (mBssid == null
+                        || MacAddress.fromString(device.BSSID).matches(mBssid, mBssidMask));
     }
 
     /** @hide */
@@ -75,65 +84,249 @@
         return MEDIUM_TYPE_WIFI;
     }
 
+
+
+    // Code below generated by codegen v1.0.11.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/companion/WifiDeviceFilter.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ WifiDeviceFilter(
+            @Nullable Pattern namePattern,
+            @Nullable MacAddress bssid,
+            @NonNull MacAddress bssidMask) {
+        this.mNamePattern = namePattern;
+        this.mBssid = bssid;
+        this.mBssidMask = bssidMask;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mBssidMask);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular
+     * expression will be shown
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable Pattern getNamePattern() {
+        return mNamePattern;
+    }
+
+    /**
+     * If set, only devices with BSSID matching the given one will be shown
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable MacAddress getBssid() {
+        return mBssid;
+    }
+
+    /**
+     * If set, only bits at positions set in this mask, will be compared to the given
+     * {@link Builder#setBssid BSSID} filter.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @NonNull MacAddress getBssidMask() {
+        return mBssidMask;
+    }
+
     @Override
-    public boolean equals(Object o) {
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(WifiDeviceFilter other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
         WifiDeviceFilter that = (WifiDeviceFilter) o;
-        return Objects.equals(mNamePattern, that.mNamePattern);
+        //noinspection PointlessBooleanExpression
+        return true
+                && Objects.equals(mNamePattern, that.mNamePattern)
+                && Objects.equals(mBssid, that.mBssid)
+                && Objects.equals(mBssidMask, that.mBssidMask);
     }
 
     @Override
+    @DataClass.Generated.Member
     public int hashCode() {
-        return Objects.hash(mNamePattern);
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Objects.hashCode(mNamePattern);
+        _hash = 31 * _hash + Objects.hashCode(mBssid);
+        _hash = 31 * _hash + Objects.hashCode(mBssidMask);
+        return _hash;
     }
 
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(patternToString(getNamePattern()));
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Creator<WifiDeviceFilter> CREATOR
-            = new Creator<WifiDeviceFilter>() {
-        @Override
-        public WifiDeviceFilter createFromParcel(Parcel in) {
-            return new WifiDeviceFilter(in);
+    @DataClass.Generated.Member
+    static Parcelling<Pattern> sParcellingForNamePattern =
+            Parcelling.Cache.get(
+                    Parcelling.BuiltIn.ForPattern.class);
+    static {
+        if (sParcellingForNamePattern == null) {
+            sParcellingForNamePattern = Parcelling.Cache.put(
+                    new Parcelling.BuiltIn.ForPattern());
         }
+    }
 
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mNamePattern != null) flg |= 0x1;
+        if (mBssid != null) flg |= 0x2;
+        dest.writeByte(flg);
+        sParcellingForNamePattern.parcel(mNamePattern, dest, flags);
+        if (mBssid != null) dest.writeTypedObject(mBssid, flags);
+        dest.writeTypedObject(mBssidMask, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ WifiDeviceFilter(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        Pattern namePattern = sParcellingForNamePattern.unparcel(in);
+        MacAddress bssid = (flg & 0x2) == 0 ? null : (MacAddress) in.readTypedObject(MacAddress.CREATOR);
+        MacAddress bssidMask = (MacAddress) in.readTypedObject(MacAddress.CREATOR);
+
+        this.mNamePattern = namePattern;
+        this.mBssid = bssid;
+        this.mBssidMask = bssidMask;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mBssidMask);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<WifiDeviceFilter> CREATOR
+            = new Parcelable.Creator<WifiDeviceFilter>() {
         @Override
         public WifiDeviceFilter[] newArray(int size) {
             return new WifiDeviceFilter[size];
         }
+
+        @Override
+        public WifiDeviceFilter createFromParcel(@NonNull Parcel in) {
+            return new WifiDeviceFilter(in);
+        }
     };
 
     /**
-     * Builder for {@link WifiDeviceFilter}
+     * A builder for {@link WifiDeviceFilter}
      */
-    public static final class Builder extends OneTimeUseBuilder<WifiDeviceFilter> {
-        private Pattern mNamePattern;
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder {
+
+        private @Nullable Pattern mNamePattern;
+        private @Nullable MacAddress mBssid;
+        private @NonNull MacAddress mBssidMask;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
 
         /**
-         * @param regex if set, only devices with {@link BluetoothDevice#getName name} matching the
-         *              given regular expression will be shown
-         * @return self for chaining
+         * If set, only devices with {@link BluetoothDevice#getName name} matching the given regular
+         * expression will be shown
          */
-        public Builder setNamePattern(@Nullable Pattern regex) {
+        @DataClass.Generated.Member
+        public @NonNull Builder setNamePattern(@Nullable Pattern value) {
             checkNotUsed();
-            mNamePattern = regex;
+            mBuilderFieldsSet |= 0x1;
+            mNamePattern = value;
             return this;
         }
 
-        /** @inheritDoc */
-        @Override
-        @NonNull
-        public WifiDeviceFilter build() {
-            markUsed();
-            return new WifiDeviceFilter(mNamePattern);
+        /**
+         * If set, only devices with BSSID matching the given one will be shown
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setBssid(@Nullable MacAddress value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mBssid = value;
+            return this;
+        }
+
+        /**
+         * If set, only bits at positions set in this mask, will be compared to the given
+         * {@link Builder#setBssid BSSID} filter.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setBssidMask(@NonNull MacAddress value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mBssidMask = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull WifiDeviceFilter build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mNamePattern = null;
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mBssid = null;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mBssidMask = MacAddress.BROADCAST_ADDRESS;
+            }
+            WifiDeviceFilter o = new WifiDeviceFilter(
+                    mNamePattern,
+                    mBssid,
+                    mBssidMask);
+            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 = 1571960300742L,
+            codegenVersion = "1.0.11",
+            sourceFile = "frameworks/base/core/java/android/companion/WifiDeviceFilter.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.Nullable java.util.regex.Pattern mNamePattern\nprivate @android.annotation.Nullable android.net.MacAddress mBssid\nprivate @android.annotation.NonNull android.net.MacAddress mBssidMask\npublic @java.lang.Override boolean matches(android.net.wifi.ScanResult)\npublic @java.lang.Override java.lang.String getDeviceDisplayName(android.net.wifi.ScanResult)\npublic @java.lang.Override int getMediumType()\nclass WifiDeviceFilter extends java.lang.Object implements [android.companion.DeviceFilter<android.net.wifi.ScanResult>]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=false, genBuilder=true, genEqualsHashCode=true, genHiddenGetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
 }
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 431f378..bd32f9c 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -212,13 +212,15 @@
         "Object"
     }
 
+    val maybeFinal = if_(classAst.isFinal, "final ")
+
     +"/**"
     +" * A builder for {@link $ClassName}"
     if (FeatureFlag.BUILDER.hidden) +" * @hide"
     +" */"
     +"@SuppressWarnings(\"WeakerAccess\")"
     +GENERATED_MEMBER_HEADER
-    !"public static class $BuilderClass$genericArgs"
+    !"public static ${maybeFinal}class $BuilderClass$genericArgs"
     if (BuilderSupertype != "Object") {
         appendSameLine(" extends $BuilderSupertype")
     }
@@ -359,7 +361,7 @@
 
 private fun ClassPrinter.generateBuilderBuild() {
     +"/** Builds the instance. This builder should not be touched after calling this! */"
-    "public $ClassType build()" {
+    "public @$NonNull $ClassType build()" {
         +"checkNotUsed();"
         +"mBuilderFieldsSet |= ${bitAtExpr(fields.size)}; // Mark builder used"
         +""
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 3eb9e7b..270d34a 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.9"
+const val CODEGEN_VERSION = "1.0.11"
 
 const val CANONICAL_BUILDER_CLASS = "Builder"
 const val BASE_BUILDER_CLASS = "BaseBuilder"