Merge "Add camera.metadata@3.3"
diff --git a/audio/2.0/default/StreamOut.cpp b/audio/2.0/default/StreamOut.cpp
index 0bedc74..49a6b12 100644
--- a/audio/2.0/default/StreamOut.cpp
+++ b/audio/2.0/default/StreamOut.cpp
@@ -164,6 +164,9 @@
     }
     mCallback.clear();
     mDevice->closeOutputStream(mStream);
+    // Closing the output stream in the HAL waits for the callback to finish,
+    // and joins the callback thread. Thus is it guaranteed that the callback
+    // thread will not be accessing our object anymore.
     mStream = nullptr;
 }
 
@@ -404,6 +407,8 @@
 
 Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
     if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
+    // Safe to pass 'this' because it is guaranteed that the callback thread
+    // is joined prior to exit from StreamOut's destructor.
     int result = mStream->set_callback(mStream, StreamOut::asyncCallback, this);
     if (result == 0) {
         mCallback = callback;
@@ -420,19 +425,27 @@
 // static
 int StreamOut::asyncCallback(stream_callback_event_t event, void*,
                              void* cookie) {
-    wp<StreamOut> weakSelf(reinterpret_cast<StreamOut*>(cookie));
-    sp<StreamOut> self = weakSelf.promote();
-    if (self == nullptr || self->mCallback == nullptr) return 0;
+    // It is guaranteed that the callback thread is joined prior
+    // to exiting from StreamOut's destructor. Must *not* use sp<StreamOut>
+    // here because it can make this code the last owner of StreamOut,
+    // and an attempt to run the destructor on the callback thread
+    // will cause a deadlock in the legacy HAL code.
+    StreamOut *self = reinterpret_cast<StreamOut*>(cookie);
+    // It's correct to hold an sp<> to callback because the reference
+    // in the StreamOut instance can be cleared in the meantime. There is
+    // no difference on which thread to run IStreamOutCallback's destructor.
+    sp<IStreamOutCallback> callback = self->mCallback;
+    if (callback.get() == nullptr) return 0;
     ALOGV("asyncCallback() event %d", event);
     switch (event) {
         case STREAM_CBK_EVENT_WRITE_READY:
-            self->mCallback->onWriteReady();
+            callback->onWriteReady();
             break;
         case STREAM_CBK_EVENT_DRAIN_READY:
-            self->mCallback->onDrainReady();
+            callback->onDrainReady();
             break;
         case STREAM_CBK_EVENT_ERROR:
-            self->mCallback->onError();
+            callback->onError();
             break;
         default:
             ALOGW("asyncCallback() unknown event %d", event);
diff --git a/broadcastradio/2.0/Android.bp b/broadcastradio/2.0/Android.bp
index 5146932..afbd6d4 100644
--- a/broadcastradio/2.0/Android.bp
+++ b/broadcastradio/2.0/Android.bp
@@ -16,6 +16,7 @@
         "android.hidl.base@1.0",
     ],
     types: [
+        "ConfigFlag",
         "Constants",
         "IdentifierType",
         "Metadata",
diff --git a/broadcastradio/2.0/ITunerSession.hal b/broadcastradio/2.0/ITunerSession.hal
index ae6cbb5..8a21768 100644
--- a/broadcastradio/2.0/ITunerSession.hal
+++ b/broadcastradio/2.0/ITunerSession.hal
@@ -77,6 +77,32 @@
     cancel();
 
     /**
+     * Fetches the current setting of a given config flag.
+     *
+     * The success/failure result must be consistent with setConfigFlag.
+     *
+     * @param flag Flag to fetch.
+     * @return result OK successfully fetched the flag.
+     *                INVALID_STATE if the flag is not applicable right now.
+     *                NOT_SUPPORTED if the flag is not supported at all.
+     * @return value The current value of the flag, if result is OK.
+     */
+    getConfigFlag(ConfigFlag flag) generates (Result result, bool value);
+
+    /**
+     * Sets the config flag.
+     *
+     * The success/failure result must be consistent with getConfigFlag.
+     *
+     * @param flag Flag to set.
+     * @param value The new value of a given flag.
+     * @return result OK successfully set the flag.
+     *                INVALID_STATE if the flag is not applicable right now.
+     *                NOT_SUPPORTED if the flag is not supported at all.
+     */
+    setConfigFlag(ConfigFlag flag, bool value) generates (Result result);
+
+    /**
      * Generic method for setting vendor-specific parameter values.
      * The framework does not interpret the parameters, they are passed
      * in an opaque manner between a vendor application and HAL.
diff --git a/broadcastradio/2.0/default/TunerSession.cpp b/broadcastradio/2.0/default/TunerSession.cpp
index f0b98b8..54af3389 100644
--- a/broadcastradio/2.0/default/TunerSession.cpp
+++ b/broadcastradio/2.0/default/TunerSession.cpp
@@ -205,6 +205,19 @@
     return {};
 }
 
+Return<void> TunerSession::getConfigFlag(ConfigFlag flag, getConfigFlag_cb _hidl_cb) {
+    ALOGV("%s(%s)", __func__, toString(flag).c_str());
+
+    _hidl_cb(Result::NOT_SUPPORTED, false);
+    return {};
+}
+
+Return<Result> TunerSession::setConfigFlag(ConfigFlag flag, bool value) {
+    ALOGV("%s(%s, %d)", __func__, toString(flag).c_str(), value);
+
+    return Result::NOT_SUPPORTED;
+}
+
 Return<void> TunerSession::setParameters(const hidl_vec<VendorKeyValue>& /* parameters */,
                                          setParameters_cb _hidl_cb) {
     ALOGV("%s", __func__);
diff --git a/broadcastradio/2.0/default/TunerSession.h b/broadcastradio/2.0/default/TunerSession.h
index 08a7588..9a72182 100644
--- a/broadcastradio/2.0/default/TunerSession.h
+++ b/broadcastradio/2.0/default/TunerSession.h
@@ -38,6 +38,8 @@
     virtual Return<Result> scan(bool directionUp, bool skipSubChannel) override;
     virtual Return<Result> step(bool directionUp) override;
     virtual Return<void> cancel() override;
+    virtual Return<void> getConfigFlag(ConfigFlag flag, getConfigFlag_cb _hidl_cb);
+    virtual Return<Result> setConfigFlag(ConfigFlag flag, bool value);
     virtual Return<void> setParameters(const hidl_vec<VendorKeyValue>& parameters,
                                        setParameters_cb _hidl_cb) override;
     virtual Return<void> getParameters(const hidl_vec<hidl_string>& keys,
diff --git a/broadcastradio/2.0/types.hal b/broadcastradio/2.0/types.hal
index 4b9878b..dacc820 100644
--- a/broadcastradio/2.0/types.hal
+++ b/broadcastradio/2.0/types.hal
@@ -37,6 +37,69 @@
 };
 
 /**
+ * Configuration flags to be used with getConfigFlag and setConfigFlag methods
+ * of ITunerSession.
+ */
+enum ConfigFlag : uint32_t {
+    /**
+     * Forces mono audio stream reception.
+     *
+     * Analog broadcasts can recover poor reception conditions by jointing
+     * stereo channels into one. Mainly for, but not limited to AM/FM.
+     */
+    FORCE_MONO = 1,
+
+    /**
+     * Forces the analog playback for the supporting radio technology.
+     *
+     * User may disable digital playback for FM HD Radio or hybrid FM/DAB with
+     * this option. This is purely user choice, ie. does not reflect digital-
+     * analog handover state managed from the HAL implementation side.
+     *
+     * Some radio technologies may not support this, ie. DAB.
+     */
+    FORCE_ANALOG,
+
+    /**
+     * Forces the digital playback for the supporting radio technology.
+     *
+     * User may disable digital-analog handover that happens with poor
+     * receiption conditions. With digital forced, the radio will remain silent
+     * instead of switching to analog channel if it's available. This is purely
+     * user choice, it does not reflect the actual state of handover.
+     */
+    FORCE_DIGITAL,
+
+    /**
+     * RDS Alternative Frequencies.
+     *
+     * If set, radio tuner automatically switches to the best available
+     * frequency that currently listened RDS station broadcasts.
+     */
+    RDS_AF,
+
+    /**
+     * RDS region-specific program lock-down.
+     *
+     * Allows user to lock to the current region as they move into the
+     * other region.
+     */
+    RDS_REG,
+
+    /** Enables DAB-DAB hard- and implicit-linking (the same content). */
+    DAB_DAB_LINKING,
+
+    /** Enables DAB-FM hard- and implicit-linking (the same content). */
+    DAB_FM_LINKING,
+
+    /** Enables DAB-DAB soft-linking (related content). */
+    DAB_DAB_SOFT_LINKING,
+
+    /** Enables DAB-FM soft-linking (related content). */
+    DAB_FM_SOFT_LINKING,
+};
+
+/**
  * A key-value pair for vendor-specific information to be passed as-is through
  * Android framework to the front-end application.
  */
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index a12afd6..c9e83f0 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -57,6 +57,18 @@
 
 }  // namespace timeout
 
+static const ConfigFlag gConfigFlagValues[] = {
+    ConfigFlag::FORCE_MONO,
+    ConfigFlag::FORCE_ANALOG,
+    ConfigFlag::FORCE_DIGITAL,
+    ConfigFlag::RDS_AF,
+    ConfigFlag::RDS_REG,
+    ConfigFlag::DAB_DAB_LINKING,
+    ConfigFlag::DAB_FM_LINKING,
+    ConfigFlag::DAB_DAB_SOFT_LINKING,
+    ConfigFlag::DAB_FM_SOFT_LINKING,
+};
+
 struct TunerCallbackMock : public ITunerCallback {
     TunerCallbackMock() {
         // we expect the antenna is connected through the whole test
@@ -378,6 +390,85 @@
     ASSERT_EQ(0u, len);
 }
 
+/**
+ * Test getting config flags.
+ *
+ * Verifies that:
+ * - getConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - call success or failure is consistent with setConfigFlag.
+ */
+TEST_F(BroadcastRadioHalTest, GetConfigFlags) {
+    ASSERT_TRUE(openSession());
+
+    for (auto flag : gConfigFlagValues) {
+        auto halResult = Result::UNKNOWN_ERROR;
+        auto cb = [&](Result result, bool) { halResult = result; };
+        auto hidlResult = mSession->getConfigFlag(flag, cb);
+        EXPECT_TRUE(hidlResult.isOk());
+
+        if (halResult != Result::NOT_SUPPORTED && halResult != Result::INVALID_STATE) {
+            ASSERT_EQ(Result::OK, halResult);
+        }
+
+        // set must fail or succeed the same way as get
+        auto setResult = mSession->setConfigFlag(flag, false);
+        EXPECT_EQ(halResult, setResult);
+        setResult = mSession->setConfigFlag(flag, true);
+        EXPECT_EQ(halResult, setResult);
+    }
+}
+
+/**
+ * Test setting config flags.
+ *
+ * Verifies that:
+ * - setConfigFlag either succeeds or ends with NOT_SUPPORTED or INVALID_STATE;
+ * - getConfigFlag reflects the state requested immediately after the set call.
+ */
+TEST_F(BroadcastRadioHalTest, SetConfigFlags) {
+    ASSERT_TRUE(openSession());
+
+    auto get = [&](ConfigFlag flag) {
+        auto halResult = Result::UNKNOWN_ERROR;
+        bool gotValue = false;
+        auto cb = [&](Result result, bool value) {
+            halResult = result;
+            gotValue = value;
+        };
+        auto hidlResult = mSession->getConfigFlag(flag, cb);
+        EXPECT_TRUE(hidlResult.isOk());
+        EXPECT_EQ(Result::OK, halResult);
+        return gotValue;
+    };
+
+    for (auto flag : gConfigFlagValues) {
+        auto result = mSession->setConfigFlag(flag, false);
+        if (result == Result::NOT_SUPPORTED || result == Result::INVALID_STATE) {
+            // setting to true must result in the same error as false
+            auto secondResult = mSession->setConfigFlag(flag, true);
+            EXPECT_EQ(result, secondResult);
+            continue;
+        }
+        ASSERT_EQ(Result::OK, result);
+
+        // verify false is set
+        auto value = get(flag);
+        EXPECT_FALSE(value);
+
+        // try setting true this time
+        result = mSession->setConfigFlag(flag, true);
+        ASSERT_EQ(Result::OK, result);
+        value = get(flag);
+        EXPECT_TRUE(value);
+
+        // false again
+        result = mSession->setConfigFlag(flag, false);
+        ASSERT_EQ(Result::OK, result);
+        value = get(flag);
+        EXPECT_FALSE(value);
+    }
+}
+
 }  // namespace vts
 }  // namespace V2_0
 }  // namespace broadcastradio
diff --git a/camera/device/1.0/default/Android.bp b/camera/device/1.0/default/Android.bp
index e0b31f0..4a7fc9c 100644
--- a/camera/device/1.0/default/Android.bp
+++ b/camera/device/1.0/default/Android.bp
@@ -29,9 +29,6 @@
     header_libs: [
         "media_plugin_headers",
     ],
-    include_dirs: [
-        "frameworks/native/include/media/openmax"
-    ],
     export_include_dirs: ["."]
 }
 
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index e4cf9af..2586980 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -1905,6 +1905,19 @@
                     // characteristics keys we've defined.
                     ASSERT_GT(entryCount, 0u);
                     ALOGI("getCameraCharacteristics metadata entry count is %zu", entryCount);
+
+                    camera_metadata_ro_entry entry;
+                    int retcode = find_camera_metadata_ro_entry(metadata,
+                            ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL, &entry);
+                    if ((0 == retcode) && (entry.count > 0)) {
+                        uint8_t hardwareLevel = entry.data.u8[0];
+                        ASSERT_TRUE(
+                                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED ||
+                                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
+                                hardwareLevel == ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_3);
+                    } else {
+                        ADD_FAILURE() << "Get camera hardware level failed!";
+                    }
                 });
                 ASSERT_TRUE(ret.isOk());
             }
diff --git a/current.txt b/current.txt
index 4a943b9..c18153a 100644
--- a/current.txt
+++ b/current.txt
@@ -27,14 +27,6 @@
 c93cb25a1a92d07aa80a617c01e8d22fc97bf8cefd3962b6a5be386ad4704d89 android.hardware.audio.effect@2.0::IVirtualizerEffect
 918f331780c9c7b04f2151a2e563aab088198ede8e6f865302ebaa13905bd9ce android.hardware.audio.effect@2.0::IVisualizerEffect
 4caad099f8fc00262b6c03ba41271808b37cea90ac98b534299bbf4ee823af02 android.hardware.audio.effect@2.0::types
-f2904a4c108ad1b93eb2fa4e43b82bd01ce1ff26156316e49d1d9fc80dfecaad android.hardware.automotive.evs@1.0::IEvsCamera
-94cba6ad04c83aa840de2ed52b74ba2126a26dd960225e61ac36703315279a80 android.hardware.automotive.evs@1.0::IEvsCameraStream
-5ea36fb043d9e3b413219de3dfd7b046b48af4fda39f167f3528652e986cb76d android.hardware.automotive.evs@1.0::IEvsDisplay
-14ef8e993a4a7c899b19bb5e39b5b0cafd28312ea2b127e35b3be8f08e23fe8e android.hardware.automotive.evs@1.0::IEvsEnumerator
-3b17c1fdfc389e0abe626c37054954b07201127d890c2bc05d47613ec1f4de4f android.hardware.automotive.evs@1.0::types
-cde0787e4bf4b450a9ceb9011d2698c0061322eb882621e89b70594b0b7c65c5 android.hardware.automotive.vehicle@2.0::IVehicle
-80fb4156fa91ce86e49bd2cabe215078f6b69591d416a09e914532eae6712052 android.hardware.automotive.vehicle@2.0::IVehicleCallback
-248004f0832b48199558bd5d1aacc29e7e6423dd0ff6012be142e23621f03f4b android.hardware.automotive.vehicle@2.0::types
 1fbdc1f852f8bd2e4a6c5cb30ac2b78668c98dce118a61762d4034ae859f43d8 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint
 aabb5c3c585592d71ee57b77298c14993d77914ddeaa64b2c5109a602b02ea47 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprintClientCallback
 1ec60d4efddae9a7b2469278a576967b4751e88de5b8d7e9df6eff6bc0da7bc9 android.hardware.biometrics.fingerprint@2.1::types
@@ -189,7 +181,6 @@
 
 # ABI preserving changes to HALs during Android O MR1 (Initial Set)
 
-26a4dd19a71f3a28249100af29be470f80e08355165fe6a7173aaa1ef264640d android.hardware.automotive.vehicle@2.0::types
 150a338ce11fcec70757c9675d83cf6a5d7b40d0c812741b91671fecce59eac9 android.hardware.broadcastradio@1.0::types
 dc7e6d4f537b9943e27edc4f86c5a03bb643b18f18f866f8c3c71c0ac4ea8cbc android.hardware.broadcastradio@1.0::types
 760485232f6cce07f8bb05e3475509956996b702f77415ee5bff05e2ec5a5bcc android.hardware.dumpstate@1.0::IDumpstateDevice
@@ -199,7 +190,6 @@
 c2c50ec74c87a583c683b4493f8f9f2e454a8d41c57af5b3eb88823a999f0ea4 android.hardware.radio@1.0::IRadioResponse # Added for b/65230472 for Android O
 4922dd58e89a03181ed1c48a6e118e47633b73b11090bdfed5aa920d25a7592b android.hardware.radio@1.0::IRadioResponse # Added for b/65230472 for Android O DR
 28e929b453df3d9f5060af2764e6cdb123ddb893e3e86923c877f6ff7e5f02c9 android.hardware.wifi@1.0::types
-bfd81bcafa3c97519cd56ad29e9fa48e23d1d323b89dbcc85899282a8c628194 android.hardware.automotive.vehicle@2.0::types
 df1d7b27e644bfed0a4f606a8c44d35d45cafce82c7c648494c8a25c7cd4a949 android.hardware.wifi@1.0::types
 
 # HALs released in Android O MR1 (Initial Set)
@@ -260,6 +250,9 @@
 c8bc853546dd55584611def2a9fa1d99f657e3366c976d2f60fe6b8aa6d2cb87 android.hardware.thermal@1.1::IThermalCallback
 
 # ABI preserving changes to HALs during Android P
-fb92e2b40f8e9d494e8fd3b4ac18499a3216342e7cff160714c3bbf3660b6e79 android.hardware.gnss@1.0::IGnssConfiguration
-d4c10cb28318dba8efb22231a8c23e86ad8853f85775187c40b42a878a5ef4d5 android.hardware.automotive.vehicle@2.0::types
 cf72ff5a52bfa4d08e9e1000cf3ab5952a2d280c7f13cdad5ab7905c08050766 android.hardware.camera.metadata@3.2::types
+6fa9804a17a8bb7923a56bd10493a5483c20007e4c9026fd04287bee7c945a8c android.hardware.gnss@1.0::IGnssCallback
+fb92e2b40f8e9d494e8fd3b4ac18499a3216342e7cff160714c3bbf3660b6e79 android.hardware.gnss@1.0::IGnssConfiguration
+251594ea9b27447bfa005ebd806e58fb0ae4aad84a69938129c9800ec0c64eda android.hardware.gnss@1.0::IGnssMeasurementCallback
+4e7169919d24fbe5573e5bcd683d0bd7abf553a4e6c34c41f9dfc1e12050db07 android.hardware.gnss@1.0::IGnssNavigationMessageCallback
+
diff --git a/gnss/1.0/IGnssCallback.hal b/gnss/1.0/IGnssCallback.hal
index 89e5e0e..7fb38c5 100644
--- a/gnss/1.0/IGnssCallback.hal
+++ b/gnss/1.0/IGnssCallback.hal
@@ -76,9 +76,9 @@
 
     struct GnssSvInfo {
         /**
-         * Pseudo-random number for the SV, or FCN/OSN number for Glonass. The
-         * distinction is made by looking at constellation field. Values must be
-         * in the range of:
+         * Pseudo-random or satellite ID number for the satellite, a.k.a. Space Vehicle (SV), or
+         * FCN/OSN number for Glonass. The distinction is made by looking at constellation field.
+         * Values must be in the range of:
          *
          * - GNSS:    1-32
          * - SBAS:    120-151, 183-192
diff --git a/gnss/1.0/IGnssMeasurementCallback.hal b/gnss/1.0/IGnssMeasurementCallback.hal
index 4031664..b27c2e0 100644
--- a/gnss/1.0/IGnssMeasurementCallback.hal
+++ b/gnss/1.0/IGnssMeasurementCallback.hal
@@ -496,7 +496,7 @@
          * to L1 must be filled, and in the other all of the values related to
          * L5 must be filled.
          *
-         * If the data is available, gnssClockFlags must contain
+         * If the data is available, gnssMeasurementFlags must contain
          * HAS_CARRIER_FREQUENCY.
          */
         float carrierFrequencyHz;
@@ -508,7 +508,7 @@
          * resets in the accumulation of this value can be inferred from the
          * accumulatedDeltaRangeState flags.
          *
-         * If the data is available, gnssClockFlags must contain
+         * If the data is available, gnssMeasurementFlags must contain
          * HAS_CARRIER_CYCLES.
          */
         int64_t carrierCycles;
@@ -521,14 +521,14 @@
          * The reference frequency is given by the field 'carrierFrequencyHz'.
          * The value contains the 'carrier-phase uncertainty' in it.
          *
-         * If the data is available, gnssClockFlags must contain
+         * If the data is available, gnssMeasurementFlags must contain
          * HAS_CARRIER_PHASE.
          */
         double carrierPhase;
 
         /**
          * 1-Sigma uncertainty of the carrier-phase.
-         * If the data is available, gnssClockFlags must contain
+         * If the data is available, gnssMeasurementFlags must contain
          * HAS_CARRIER_PHASE_UNCERTAINTY.
          */
         double carrierPhaseUncertainty;
diff --git a/gnss/1.0/IGnssNavigationMessageCallback.hal b/gnss/1.0/IGnssNavigationMessageCallback.hal
index 3fdae9f..24ee708 100644
--- a/gnss/1.0/IGnssNavigationMessageCallback.hal
+++ b/gnss/1.0/IGnssNavigationMessageCallback.hal
@@ -119,7 +119,8 @@
          *
          * - For Galileo F/NAV, this refers to the page type in the range 1-6
          *
-         * - For Galileo I/NAV, this refers to the word type in the range 1-10+
+         * - For Galileo I/NAV, this refers to the word type in the range 0-10+
+         * A value of 0 is only allowed if the Satellite is transmiting a Spare Word.
          */
         int16_t submessageId;
 
diff --git a/graphics/composer/2.1/default/ComposerBase.h b/graphics/composer/2.1/default/ComposerBase.h
index 85b1a4d..e1c9d33 100644
--- a/graphics/composer/2.1/default/ComposerBase.h
+++ b/graphics/composer/2.1/default/ComposerBase.h
@@ -38,6 +38,8 @@
 public:
     virtual ~ComposerBase() {};
 
+    virtual bool hasCapability(hwc2_capability_t capability) = 0;
+
     virtual void removeClient() = 0;
     virtual void enableCallback(bool enable) = 0;
     virtual uint32_t getMaxVirtualDisplayCount() = 0;
diff --git a/graphics/composer/2.1/default/ComposerClient.cpp b/graphics/composer/2.1/default/ComposerClient.cpp
index e792034..4e6dd4f 100644
--- a/graphics/composer/2.1/default/ComposerClient.cpp
+++ b/graphics/composer/2.1/default/ComposerClient.cpp
@@ -748,15 +748,17 @@
     }
 
     // First try to Present as is.
-    int presentFence = -1;
-    std::vector<Layer> layers;
-    std::vector<int> fences;
-    auto err = mHal.presentDisplay(mDisplay, &presentFence, &layers, &fences);
-    if (err == Error::NONE) {
-        mWriter.setPresentOrValidateResult(1);
-        mWriter.setPresentFence(presentFence);
-        mWriter.setReleaseFences(layers, fences);
-        return true;
+    if (mHal.hasCapability(HWC2_CAPABILITY_SKIP_VALIDATE)) {
+        int presentFence = -1;
+        std::vector<Layer> layers;
+        std::vector<int> fences;
+        auto err = mHal.presentDisplay(mDisplay, &presentFence, &layers, &fences);
+        if (err == Error::NONE) {
+            mWriter.setPresentOrValidateResult(1);
+            mWriter.setPresentFence(presentFence);
+            mWriter.setReleaseFences(layers, fences);
+            return true;
+        }
     }
 
     // Present has failed. We need to fallback to validate
@@ -766,9 +768,8 @@
     std::vector<Layer> requestedLayers;
     std::vector<uint32_t> requestMasks;
 
-    err = mHal.validateDisplay(mDisplay, &changedLayers,
-                               &compositionTypes, &displayRequestMask,
-                               &requestedLayers, &requestMasks);
+    auto err = mHal.validateDisplay(mDisplay, &changedLayers, &compositionTypes,
+                                    &displayRequestMask, &requestedLayers, &requestMasks);
     if (err == Error::NONE) {
         mWriter.setPresentOrValidateResult(0);
         mWriter.setChangedCompositionTypes(changedLayers,
diff --git a/graphics/composer/2.1/default/Hwc.cpp b/graphics/composer/2.1/default/Hwc.cpp
index fdb4af8..cb393ec 100644
--- a/graphics/composer/2.1/default/Hwc.cpp
+++ b/graphics/composer/2.1/default/Hwc.cpp
@@ -47,8 +47,7 @@
     }
 
     initCapabilities();
-    if (majorVersion >= 2 &&
-        hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+    if (majorVersion >= 2 && hasCapability(HWC2_CAPABILITY_PRESENT_FENCE_IS_NOT_RELIABLE)) {
         ALOGE("Present fence must be reliable from HWC2 on.");
         abort();
     }
@@ -114,12 +113,14 @@
     uint32_t count = 0;
     mDevice->getCapabilities(mDevice, &count, nullptr);
 
-    std::vector<Capability> caps(count);
-    mDevice->getCapabilities(mDevice, &count, reinterpret_cast<
-              std::underlying_type<Capability>::type*>(caps.data()));
+    std::vector<int32_t> caps(count);
+    mDevice->getCapabilities(mDevice, &count, caps.data());
     caps.resize(count);
 
-    mCapabilities.insert(caps.cbegin(), caps.cend());
+    mCapabilities.reserve(count);
+    for (auto cap : caps) {
+        mCapabilities.insert(static_cast<hwc2_capability_t>(cap));
+    }
 }
 
 template<typename T>
@@ -188,7 +189,7 @@
     initDispatch(HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA,
             &mDispatch.setLayerPlaneAlpha);
 
-    if (hasCapability(Capability::SIDEBAND_STREAM)) {
+    if (hasCapability(HWC2_CAPABILITY_SIDEBAND_STREAM)) {
         initDispatch(HWC2_FUNCTION_SET_LAYER_SIDEBAND_STREAM,
                 &mDispatch.setLayerSidebandStream);
     }
@@ -208,15 +209,26 @@
     initDispatch(HWC2_FUNCTION_VALIDATE_DISPLAY, &mDispatch.validateDisplay);
 }
 
-bool HwcHal::hasCapability(Capability capability) const
-{
+bool HwcHal::hasCapability(hwc2_capability_t capability) {
     return (mCapabilities.count(capability) > 0);
 }
 
 Return<void> HwcHal::getCapabilities(getCapabilities_cb hidl_cb)
 {
-    std::vector<Capability> caps(
-            mCapabilities.cbegin(), mCapabilities.cend());
+    std::vector<Capability> caps;
+    caps.reserve(mCapabilities.size());
+    for (auto cap : mCapabilities) {
+        switch (cap) {
+            case HWC2_CAPABILITY_SIDEBAND_STREAM:
+            case HWC2_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM:
+            case HWC2_CAPABILITY_PRESENT_FENCE_IS_NOT_RELIABLE:
+                caps.push_back(static_cast<Capability>(cap));
+                break;
+            default:
+                // not all HWC2 caps are defined in HIDL
+                break;
+        }
+    }
 
     hidl_vec<Capability> caps_reply;
     caps_reply.setToExternal(caps.data(), caps.size());
diff --git a/graphics/composer/2.1/default/Hwc.h b/graphics/composer/2.1/default/Hwc.h
index 32c6b0b..e3f5ce6 100644
--- a/graphics/composer/2.1/default/Hwc.h
+++ b/graphics/composer/2.1/default/Hwc.h
@@ -58,14 +58,13 @@
     HwcHal(const hw_module_t* module);
     virtual ~HwcHal();
 
-    bool hasCapability(Capability capability) const;
-
     // IComposer interface
     Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
     Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
     Return<void> createClient(createClient_cb hidl_cb) override;
 
     // ComposerBase interface
+    bool hasCapability(hwc2_capability_t capability) override;
     void removeClient() override;
     void enableCallback(bool enable) override;
     uint32_t getMaxVirtualDisplayCount() override;
@@ -168,7 +167,7 @@
 
     hwc2_device_t* mDevice;
 
-    std::unordered_set<Capability> mCapabilities;
+    std::unordered_set<hwc2_capability_t> mCapabilities;
 
     struct {
         HWC2_PFN_ACCEPT_DISPLAY_CHANGES acceptDisplayChanges;
diff --git a/keymaster/3.0/default/Android.mk b/keymaster/3.0/default/Android.mk
index 6b66f7d..9e7d04a 100644
--- a/keymaster/3.0/default/Android.mk
+++ b/keymaster/3.0/default/Android.mk
@@ -12,7 +12,6 @@
     libsoftkeymasterdevice \
     libcrypto \
     libkeymaster_portable \
-    libkeymaster_staging \
     libpuresoftkeymasterdevice \
     libkeymaster3device \
     libhidlbase \
diff --git a/light/2.0/default/Android.bp b/light/2.0/default/Android.bp
index 8d4eca1..8b5f780 100644
--- a/light/2.0/default/Android.bp
+++ b/light/2.0/default/Android.bp
@@ -15,7 +15,7 @@
 cc_library_shared {
     name: "android.hardware.light@2.0-impl",
     defaults: ["hidl_defaults"],
-    proprietary: true,
+    vendor: true,
     relative_install_path: "hw",
     srcs: ["Light.cpp"],
 
@@ -32,10 +32,10 @@
 }
 
 cc_binary {
+    name: "android.hardware.light@2.0-service",
     relative_install_path: "hw",
     defaults: ["hidl_defaults"],
-    proprietary: true,
-    name: "android.hardware.light@2.0-service",
+    vendor: true,
     init_rc: ["android.hardware.light@2.0-service.rc"],
     srcs: ["service.cpp"],
 
diff --git a/media/1.0/xml/Android.mk b/media/1.0/xml/Android.mk
new file mode 100644
index 0000000..bc44b9e
--- /dev/null
+++ b/media/1.0/xml/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH := $(call my-dir)
+
+#######################################
+# media_profiles_V1_0.dtd
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := media_profiles_V1_0.dtd
+LOCAL_SRC_FILES := media_profiles.dtd
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+
+include $(BUILD_PREBUILT)
+
diff --git a/media/1.0/media_profiles.dtd b/media/1.0/xml/media_profiles.dtd
similarity index 100%
rename from media/1.0/media_profiles.dtd
rename to media/1.0/xml/media_profiles.dtd
diff --git a/radio/1.2/types.hal b/radio/1.2/types.hal
index 6b57ae3..52af575 100644
--- a/radio/1.2/types.hal
+++ b/radio/1.2/types.hal
@@ -35,7 +35,7 @@
 import @1.1::ScanType;
 
 enum RadioConst : @1.0::RadioConst {
-    RADIO_ACCESS_SPEFICIER_MAX_SIZE = 8,
+    RADIO_ACCESS_SPECIFIER_MAX_SIZE = 8,
 };
 
 /**
@@ -74,7 +74,7 @@
     /**
      * Networks with bands/channels to scan
      * Maximum length of the vector is
-     * RadioConst:RADIO_ACCESS_SPEFICIER_MAX_SIZE
+     * RadioConst:RADIO_ACCESS_SPECIFIER_MAX_SIZE
      */
     vec<RadioAccessSpecifier> specifiers;
 
@@ -220,4 +220,4 @@
      * Valid only if type = tdscdma and size = 1 else must be empty.
      */
     vec<CellInfoTdscdma> tdscdma;
-};
\ No newline at end of file
+};
diff --git a/tests/bar/1.0/default/Bar.cpp b/tests/bar/1.0/default/Bar.cpp
index 6a8a360..4614428 100644
--- a/tests/bar/1.0/default/Bar.cpp
+++ b/tests/bar/1.0/default/Bar.cpp
@@ -19,6 +19,11 @@
 }
 
 // Methods from ::android::hardware::tests::foo::V1_0::IFoo follow.
+Return<void> Bar::convertToBoolIfSmall(Discriminator d, const hidl_vec<Union>& u,
+                                       convertToBoolIfSmall_cb _hidl_cb) {
+    return mFoo->convertToBoolIfSmall(d, u, _hidl_cb);
+}
+
 Return<void> Bar::doThis(float param) {
     return mFoo->doThis(param);
 }
diff --git a/tests/bar/1.0/default/Bar.h b/tests/bar/1.0/default/Bar.h
index b276823..acb976f 100644
--- a/tests/bar/1.0/default/Bar.h
+++ b/tests/bar/1.0/default/Bar.h
@@ -31,6 +31,8 @@
     Bar();
 
     // Methods from ::android::hardware::tests::foo::V1_0::IFoo follow.
+    virtual Return<void> convertToBoolIfSmall(Discriminator d, const hidl_vec<Union>& u,
+                                              convertToBoolIfSmall_cb _hidl_cb) override;
     virtual Return<void> doThis(float param)  override;
     virtual Return<int32_t> doThatAndReturnSomething(int64_t param)  override;
     virtual Return<double> doQuiteABit(int32_t a, int64_t b, float c, double d)  override;
diff --git a/tests/foo/1.0/IFoo.hal b/tests/foo/1.0/IFoo.hal
index c1ce7b0..4a930a2 100644
--- a/tests/foo/1.0/IFoo.hal
+++ b/tests/foo/1.0/IFoo.hal
@@ -122,6 +122,19 @@
         bitfield<BitField> bf;
     };
 
+    enum Discriminator : uint8_t {
+        BOOL,
+        INT,
+    };
+    union Union {
+        bool boolValue;
+        int64_t intValue;
+    };
+    struct ContainsUnion {
+        Discriminator discriminator;
+        Union value;
+    };
+
     typedef int32_t[5][6][7] multidimArrayOne;
     typedef multidimArrayOne[8][9][10] multidimArrayTwo;
     typedef multidimArrayTwo[2][3][4] multidimArrayThree;
@@ -144,6 +157,16 @@
         InnerTestEnumTypedef foo;
     };
 
+    /**
+     * If d is INT, converts all values to bools which are small enough (0 or 1).
+     * If d is BOOL, should leave all values as BOOLs.
+     *
+     * @param d discriminator for all values in u
+     * @param u values to be expanded
+     * @return c must have same length as u unless there is an error in which case it will be empty.
+     */
+    convertToBoolIfSmall(Discriminator d, vec<Union> u) generates (vec<ContainsUnion> c);
+
     doThis(float param);
     doThatAndReturnSomething(int64_t param) generates (int32_t result);
     doQuiteABit(int32_t a, int64_t b, float c, double d) generates (double something);
diff --git a/tests/foo/1.0/default/Foo.cpp b/tests/foo/1.0/default/Foo.cpp
index a860ce7..a31ab9f 100644
--- a/tests/foo/1.0/default/Foo.cpp
+++ b/tests/foo/1.0/default/Foo.cpp
@@ -15,6 +15,30 @@
 namespace implementation {
 
 // Methods from ::android::hardware::tests::foo::V1_0::IFoo follow.
+Return<void> Foo::convertToBoolIfSmall(Discriminator d, const hidl_vec<Union>& u,
+                                       convertToBoolIfSmall_cb _hidl_cb) {
+    hidl_vec<ContainsUnion> res(u.size());
+    for (size_t i = 0; i < u.size(); i++) {
+        ContainsUnion& outValue = res[i];
+
+        if (d == Discriminator::BOOL) {
+            outValue.discriminator = Discriminator::BOOL;
+            outValue.value.boolValue = u[i].boolValue;
+        } else {
+            uint64_t value = u[i].intValue;
+            if (value == 0 || value == 1) {
+                outValue.discriminator = Discriminator::BOOL;
+                outValue.value.boolValue = static_cast<bool>(value);
+            } else {
+                outValue.discriminator = Discriminator::INT;
+                outValue.value.intValue = value;
+            }
+        }
+    }
+    _hidl_cb(res);
+    return Void();
+}
+
 Return<void> Foo::doThis(float param) {
     LOG(INFO) << "SERVER(Foo) doThis(" << param << ")";
 
diff --git a/tests/foo/1.0/default/Foo.h b/tests/foo/1.0/default/Foo.h
index 4558a73..7dd672b 100644
--- a/tests/foo/1.0/default/Foo.h
+++ b/tests/foo/1.0/default/Foo.h
@@ -22,9 +22,14 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::hidl_string;
 using ::android::sp;
+using ContainsUnion = ::android::hardware::tests::foo::V1_0::IFoo::ContainsUnion;
+using Discriminator = ::android::hardware::tests::foo::V1_0::IFoo::Discriminator;
+using Union = ::android::hardware::tests::foo::V1_0::IFoo::Union;
 
 struct Foo : public IFoo {
     // Methods from ::android::hardware::tests::foo::V1_0::IFoo follow.
+    virtual Return<void> convertToBoolIfSmall(Discriminator d, const hidl_vec<Union>& u,
+                                              convertToBoolIfSmall_cb _hidl_cb) override;
     virtual Return<void> doThis(float param)  override;
     virtual Return<int32_t> doThatAndReturnSomething(int64_t param)  override;
     virtual Return<double> doQuiteABit(int32_t a, int64_t b, float c, double d)  override;
diff --git a/wifi/1.2/default/Android.mk b/wifi/1.2/default/Android.mk
index b242cfc..95414bc 100644
--- a/wifi/1.2/default/Android.mk
+++ b/wifi/1.2/default/Android.mk
@@ -13,21 +13,27 @@
 # limitations under the License.
 LOCAL_PATH := $(call my-dir)
 
+###
+### android.hardware.wifi static library
+###
 include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.wifi@1.0-service
+LOCAL_MODULE := android.hardware.wifi@1.0-service-lib
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_CPPFLAGS := -Wall -Werror -Wextra
 ifdef WIFI_HIDL_FEATURE_AWARE
 LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_AWARE
 endif
+ifdef WIFI_HIDL_FEATURE_DUAL_INTERFACE
+LOCAL_CPPFLAGS += -DWIFI_HIDL_FEATURE_DUAL_INTERFACE
+endif
 LOCAL_SRC_FILES := \
     hidl_struct_util.cpp \
     hidl_sync_util.cpp \
-    service.cpp \
     wifi.cpp \
     wifi_ap_iface.cpp \
     wifi_chip.cpp \
+    wifi_feature_flags.cpp \
     wifi_legacy_hal.cpp \
     wifi_legacy_hal_stubs.cpp \
     wifi_mode_controller.cpp \
@@ -49,5 +55,64 @@
     android.hardware.wifi@1.0 \
     android.hardware.wifi@1.1 \
     android.hardware.wifi@1.2
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+include $(BUILD_STATIC_LIBRARY)
+
+###
+### android.hardware.wifi daemon
+###
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.wifi@1.0-service
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_CPPFLAGS := -Wall -Werror -Wextra
+LOCAL_SRC_FILES := \
+    service.cpp
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    libhidlbase \
+    libhidltransport \
+    liblog \
+    libnl \
+    libutils \
+    libwifi-hal \
+    libwifi-system-iface \
+    android.hardware.wifi@1.0 \
+    android.hardware.wifi@1.1 \
+    android.hardware.wifi@1.2
+LOCAL_STATIC_LIBRARIES := \
+    android.hardware.wifi@1.0-service-lib
 LOCAL_INIT_RC := android.hardware.wifi@1.0-service.rc
 include $(BUILD_EXECUTABLE)
+
+###
+### android.hardware.wifi unit tests.
+###
+include $(CLEAR_VARS)
+LOCAL_MODULE := android.hardware.wifi@1.0-service-tests
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_SRC_FILES := \
+    tests/main.cpp \
+    tests/mock_wifi_feature_flags.cpp \
+    tests/mock_wifi_legacy_hal.cpp \
+    tests/mock_wifi_mode_controller.cpp \
+    tests/wifi_chip_unit_tests.cpp
+LOCAL_STATIC_LIBRARIES := \
+    libgmock \
+    libgtest \
+    android.hardware.wifi@1.0-service-lib
+LOCAL_SHARED_LIBRARIES := \
+    libbase \
+    libcutils \
+    libhidlbase \
+    libhidltransport \
+    liblog \
+    libnl \
+    libutils \
+    libwifi-hal \
+    libwifi-system-iface \
+    android.hardware.wifi@1.0 \
+    android.hardware.wifi@1.1 \
+    android.hardware.wifi@1.2
+include $(BUILD_NATIVE_TEST)
diff --git a/wifi/1.2/default/hidl_struct_util.cpp b/wifi/1.2/default/hidl_struct_util.cpp
index 1359498..5d48109 100644
--- a/wifi/1.2/default/hidl_struct_util.cpp
+++ b/wifi/1.2/default/hidl_struct_util.cpp
@@ -1250,10 +1250,10 @@
         hidl_request.baseConfigs.rangingIntervalMsec;
     legacy_request->ranging_cfg.config_ranging_indications =
         hidl_request.baseConfigs.configRangingIndications;
-    legacy_request->ranging_cfg.distance_ingress_cm =
-        hidl_request.baseConfigs.distanceIngressCm;
-    legacy_request->ranging_cfg.distance_egress_cm =
-        hidl_request.baseConfigs.distanceEgressCm;
+    legacy_request->ranging_cfg.distance_ingress_mm =
+        hidl_request.baseConfigs.distanceIngressCm * 10;
+    legacy_request->ranging_cfg.distance_egress_mm =
+        hidl_request.baseConfigs.distanceEgressCm * 10;
     legacy_request->ranging_auto_response =
         hidl_request.baseConfigs.rangingRequired
             ? legacy_hal::NAN_RANGING_AUTO_RESPONSE_ENABLE
@@ -1402,10 +1402,10 @@
         hidl_request.baseConfigs.rangingIntervalMsec;
     legacy_request->ranging_cfg.config_ranging_indications =
         hidl_request.baseConfigs.configRangingIndications;
-    legacy_request->ranging_cfg.distance_ingress_cm =
-        hidl_request.baseConfigs.distanceIngressCm;
-    legacy_request->ranging_cfg.distance_egress_cm =
-        hidl_request.baseConfigs.distanceEgressCm;
+    legacy_request->ranging_cfg.distance_ingress_mm =
+        hidl_request.baseConfigs.distanceIngressCm * 10;
+    legacy_request->ranging_cfg.distance_egress_mm =
+        hidl_request.baseConfigs.distanceEgressCm * 10;
     legacy_request->ranging_auto_response =
         hidl_request.baseConfigs.rangingRequired
             ? legacy_hal::NAN_RANGING_AUTO_RESPONSE_ENABLE
@@ -1847,7 +1847,7 @@
     hidl_ind->peerRequiresRanging = legacy_ind.peer_sdea_params.ranging_state ==
                                     legacy_hal::NAN_RANGING_ENABLE;
     hidl_ind->rangingMeasurementInCm =
-        legacy_ind.range_info.range_measurement_cm;
+        legacy_ind.range_info.range_measurement_mm / 10;
     hidl_ind->rangingIndicationType = legacy_ind.range_info.ranging_event_type;
 
     return true;
diff --git a/wifi/1.2/default/service.cpp b/wifi/1.2/default/service.cpp
index 6297cf2..01d22bd 100644
--- a/wifi/1.2/default/service.cpp
+++ b/wifi/1.2/default/service.cpp
@@ -20,9 +20,17 @@
 #include <utils/StrongPointer.h>
 
 #include "wifi.h"
+#include "wifi_feature_flags.h"
+#include "wifi_legacy_hal.h"
+#include "wifi_mode_controller.h"
 
 using android::hardware::configureRpcThreadpool;
 using android::hardware::joinRpcThreadpool;
+using android::hardware::wifi::V1_2::implementation::feature_flags::
+    WifiFeatureFlags;
+using android::hardware::wifi::V1_2::implementation::legacy_hal::WifiLegacyHal;
+using android::hardware::wifi::V1_2::implementation::mode_controller::
+    WifiModeController;
 
 int main(int /*argc*/, char** argv) {
     android::base::InitLogging(
@@ -33,7 +41,10 @@
 
     // Setup hwbinder service
     android::sp<android::hardware::wifi::V1_2::IWifi> service =
-        new android::hardware::wifi::V1_2::implementation::Wifi();
+        new android::hardware::wifi::V1_2::implementation::Wifi(
+            std::make_shared<WifiLegacyHal>(),
+            std::make_shared<WifiModeController>(),
+            std::make_shared<WifiFeatureFlags>());
     CHECK_EQ(service->registerAsService(), android::NO_ERROR)
         << "Failed to register wifi HAL";
 
diff --git a/wifi/1.2/default/tests/main.cpp b/wifi/1.2/default/tests/main.cpp
new file mode 100644
index 0000000..9aac837
--- /dev/null
+++ b/wifi/1.2/default/tests/main.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ::testing::InitGoogleMock(&argc, argv);
+    // Force ourselves to always log to stderr
+    android::base::InitLogging(argv, android::base::StderrLogger);
+    return RUN_ALL_TESTS();
+}
diff --git a/wifi/1.2/default/tests/mock_wifi_feature_flags.cpp b/wifi/1.2/default/tests/mock_wifi_feature_flags.cpp
new file mode 100644
index 0000000..8d0b192
--- /dev/null
+++ b/wifi/1.2/default/tests/mock_wifi_feature_flags.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <gmock/gmock.h>
+
+#include "mock_wifi_feature_flags.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace feature_flags {
+
+MockWifiFeatureFlags::MockWifiFeatureFlags() {}
+
+}  // namespace feature_flags
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/tests/mock_wifi_feature_flags.h b/wifi/1.2/default/tests/mock_wifi_feature_flags.h
new file mode 100644
index 0000000..8cf1d4b
--- /dev/null
+++ b/wifi/1.2/default/tests/mock_wifi_feature_flags.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef MOCK_WIFI_FEATURE_FLAGS_H_
+#define MOCK_WIFI_FEATURE_FLAGS_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_feature_flags.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace feature_flags {
+
+class MockWifiFeatureFlags : public WifiFeatureFlags {
+   public:
+    MockWifiFeatureFlags();
+
+    MOCK_METHOD0(isAwareSupported, bool());
+    MOCK_METHOD0(isDualInterfaceSupported, bool());
+};
+
+}  // namespace feature_flags
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+
+#endif  // MOCK_WIFI_FEATURE_FLAGS_H_
diff --git a/wifi/1.2/default/tests/mock_wifi_legacy_hal.cpp b/wifi/1.2/default/tests/mock_wifi_legacy_hal.cpp
new file mode 100644
index 0000000..8381dde
--- /dev/null
+++ b/wifi/1.2/default/tests/mock_wifi_legacy_hal.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#undef NAN  // This is weird, NAN is defined in bionic/libc/include/math.h:38
+#include "mock_wifi_legacy_hal.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace legacy_hal {
+
+MockWifiLegacyHal::MockWifiLegacyHal() : WifiLegacyHal() {}
+}  // namespace legacy_hal
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/tests/mock_wifi_legacy_hal.h b/wifi/1.2/default/tests/mock_wifi_legacy_hal.h
new file mode 100644
index 0000000..8e1696e
--- /dev/null
+++ b/wifi/1.2/default/tests/mock_wifi_legacy_hal.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef MOCK_WIFI_LEGACY_HAL_H_
+#define MOCK_WIFI_LEGACY_HAL_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_legacy_hal.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace legacy_hal {
+
+class MockWifiLegacyHal : public WifiLegacyHal {
+   public:
+    MockWifiLegacyHal();
+    MOCK_METHOD0(initialize, wifi_error());
+    MOCK_METHOD0(start, wifi_error());
+    MOCK_METHOD2(stop, wifi_error(std::unique_lock<std::recursive_mutex>*,
+                                  const std::function<void()>&));
+    MOCK_METHOD2(setDfsFlag, wifi_error(const std::string&, bool));
+    MOCK_METHOD2(nanRegisterCallbackHandlers,
+                 wifi_error(const std::string&, const NanCallbackHandlers&));
+    MOCK_METHOD2(nanDisableRequest,
+                 wifi_error(const std::string&, transaction_id));
+    MOCK_METHOD3(nanDataInterfaceDelete,
+                 wifi_error(const std::string&, transaction_id,
+                            const std::string&));
+};
+}  // namespace legacy_hal
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+
+#endif  // MOCK_WIFI_LEGACY_HAL_H_
diff --git a/wifi/1.2/default/tests/mock_wifi_mode_controller.cpp b/wifi/1.2/default/tests/mock_wifi_mode_controller.cpp
new file mode 100644
index 0000000..461a581
--- /dev/null
+++ b/wifi/1.2/default/tests/mock_wifi_mode_controller.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#undef NAN  // This is weird, NAN is defined in bionic/libc/include/math.h:38
+#include "mock_wifi_mode_controller.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace mode_controller {
+
+MockWifiModeController::MockWifiModeController() : WifiModeController() {}
+}  // namespace mode_controller
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/tests/mock_wifi_mode_controller.h b/wifi/1.2/default/tests/mock_wifi_mode_controller.h
new file mode 100644
index 0000000..50c3e35
--- /dev/null
+++ b/wifi/1.2/default/tests/mock_wifi_mode_controller.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef MOCK_WIFI_MODE_CONTROLLER_H_
+#define MOCK_WIFI_MODE_CONTROLLER_H_
+
+#include <gmock/gmock.h>
+
+#include "wifi_mode_controller.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace mode_controller {
+
+class MockWifiModeController : public WifiModeController {
+   public:
+    MockWifiModeController();
+    MOCK_METHOD0(initialize, bool());
+    MOCK_METHOD1(changeFirmwareMode, bool(IfaceType));
+    MOCK_METHOD1(isFirmwareModeChangeNeeded, bool(IfaceType));
+    MOCK_METHOD0(deinitialize, bool());
+};
+}  // namespace mode_controller
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+
+#endif  // MOCK_WIFI_MODE_CONTROLLER_H_
diff --git a/wifi/1.2/default/tests/runtests.sh b/wifi/1.2/default/tests/runtests.sh
new file mode 100755
index 0000000..966a6a7
--- /dev/null
+++ b/wifi/1.2/default/tests/runtests.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+# Copyright(C) 2017 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.
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+  echo "You need to source and lunch before you can use this script"
+  exit 1
+fi
+
+echo "Running tests"
+set -e # fail early
+
+#NOTE We can't actually run these commands, since they rely on functions added by
+#build / envsetup.sh to the bash shell environment.
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/"
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk \
+    MODULES-IN-hardware-interfaces-wifi-1.2-default
+
+set -x # print commands
+
+adb wait-for-device
+adb root
+adb wait-for-device
+
+#'disable-verity' will appear in 'adb remount' output if
+#dm - verity is enabled and needs to be disabled.
+if adb remount | grep 'disable-verity'; then
+  adb disable-verity
+  adb reboot
+  adb wait-for-device
+  adb root
+  adb wait-for-device
+  adb remount
+fi
+
+adb sync
+
+adb shell /data/nativetest/vendor/android.hardware.wifi@1.0-service-tests/android.hardware.wifi@1.0-service-tests
diff --git a/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp b/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
new file mode 100644
index 0000000..f73869b
--- /dev/null
+++ b/wifi/1.2/default/tests/wifi_chip_unit_tests.cpp
@@ -0,0 +1,547 @@
+/*
+ * Copyright (C) 2017, 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.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <gmock/gmock.h>
+
+#undef NAN  // This is weird, NAN is defined in bionic/libc/include/math.h:38
+#include "wifi_chip.h"
+
+#include "mock_wifi_feature_flags.h"
+#include "mock_wifi_legacy_hal.h"
+#include "mock_wifi_mode_controller.h"
+
+using testing::NiceMock;
+using testing::Return;
+using testing::Test;
+
+namespace {
+using android::hardware::wifi::V1_0::ChipId;
+
+constexpr ChipId kFakeChipId = 5;
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+
+class WifiChipTest : public Test {
+   protected:
+    void setupV1IfaceCombination() {
+        EXPECT_CALL(*feature_flags_, isAwareSupported())
+            .WillRepeatedly(testing::Return(false));
+        EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
+            .WillRepeatedly(testing::Return(false));
+    }
+
+    void setupV1_AwareIfaceCombination() {
+        EXPECT_CALL(*feature_flags_, isAwareSupported())
+            .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
+            .WillRepeatedly(testing::Return(false));
+    }
+
+    void setupV2_AwareIfaceCombination() {
+        EXPECT_CALL(*feature_flags_, isAwareSupported())
+            .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*feature_flags_, isDualInterfaceSupported())
+            .WillRepeatedly(testing::Return(true));
+    }
+
+    void assertNumberOfModes(uint32_t num_modes) {
+        chip_->getAvailableModes(
+            [num_modes](const WifiStatus& status,
+                        const std::vector<WifiChip::ChipMode>& modes) {
+                ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+                // V2_Aware has 1 mode of operation.
+                ASSERT_EQ(num_modes, modes.size());
+            });
+    }
+
+    void findModeAndConfigureForIfaceType(const IfaceType& type) {
+        // This should be aligned with kInvalidModeId in wifi_chip.cpp.
+        ChipModeId mode_id = UINT32_MAX;
+        chip_->getAvailableModes(
+            [&mode_id, &type](const WifiStatus& status,
+                              const std::vector<WifiChip::ChipMode>& modes) {
+                ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+                for (const auto& mode : modes) {
+                    for (const auto& combination : mode.availableCombinations) {
+                        for (const auto& limit : combination.limits) {
+                            if (limit.types.end() !=
+                                std::find(limit.types.begin(),
+                                          limit.types.end(), type)) {
+                                mode_id = mode.id;
+                            }
+                        }
+                    }
+                }
+            });
+        ASSERT_NE(UINT32_MAX, mode_id);
+
+        chip_->configureChip(mode_id, [](const WifiStatus& status) {
+            ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+        });
+    }
+
+    // Returns an empty string on error.
+    std::string createIface(const IfaceType& type) {
+        std::string iface_name;
+        if (type == IfaceType::AP) {
+            chip_->createApIface([&iface_name](const WifiStatus& status,
+                                               const sp<IWifiApIface>& iface) {
+                if (WifiStatusCode::SUCCESS == status.code) {
+                    ASSERT_NE(iface.get(), nullptr);
+                    iface->getName([&iface_name](const WifiStatus& status,
+                                                 const hidl_string& name) {
+                        ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+                        iface_name = name.c_str();
+                    });
+                }
+            });
+        } else if (type == IfaceType::NAN) {
+            chip_->createNanIface(
+                [&iface_name](const WifiStatus& status,
+                              const sp<IWifiNanIface>& iface) {
+                    if (WifiStatusCode::SUCCESS == status.code) {
+                        ASSERT_NE(iface.get(), nullptr);
+                        iface->getName([&iface_name](const WifiStatus& status,
+                                                     const hidl_string& name) {
+                            ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+                            iface_name = name.c_str();
+                        });
+                    }
+                });
+        } else if (type == IfaceType::P2P) {
+            chip_->createP2pIface(
+                [&iface_name](const WifiStatus& status,
+                              const sp<IWifiP2pIface>& iface) {
+                    if (WifiStatusCode::SUCCESS == status.code) {
+                        ASSERT_NE(iface.get(), nullptr);
+                        iface->getName([&iface_name](const WifiStatus& status,
+                                                     const hidl_string& name) {
+                            ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+                            iface_name = name.c_str();
+                        });
+                    }
+                });
+        } else if (type == IfaceType::STA) {
+            chip_->createStaIface(
+                [&iface_name](const WifiStatus& status,
+                              const sp<IWifiStaIface>& iface) {
+                    if (WifiStatusCode::SUCCESS == status.code) {
+                        ASSERT_NE(iface.get(), nullptr);
+                        iface->getName([&iface_name](const WifiStatus& status,
+                                                     const hidl_string& name) {
+                            ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+                            iface_name = name.c_str();
+                        });
+                    }
+                });
+        }
+        return iface_name;
+    }
+
+    void removeIface(const IfaceType& type, const std::string& iface_name) {
+        if (type == IfaceType::AP) {
+            chip_->removeApIface(iface_name, [](const WifiStatus& status) {
+                ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+            });
+        } else if (type == IfaceType::NAN) {
+            chip_->removeNanIface(iface_name, [](const WifiStatus& status) {
+                ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+            });
+        } else if (type == IfaceType::P2P) {
+            chip_->removeP2pIface(iface_name, [](const WifiStatus& status) {
+                ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+            });
+        } else if (type == IfaceType::STA) {
+            chip_->removeStaIface(iface_name, [](const WifiStatus& status) {
+                ASSERT_EQ(WifiStatusCode::SUCCESS, status.code);
+            });
+        }
+    }
+
+   public:
+    void SetUp() override {
+        chip_ = new WifiChip(chip_id_, legacy_hal_, mode_controller_,
+                             feature_flags_);
+
+        EXPECT_CALL(*mode_controller_, changeFirmwareMode(testing::_))
+            .WillRepeatedly(testing::Return(true));
+        EXPECT_CALL(*legacy_hal_, start())
+            .WillRepeatedly(testing::Return(legacy_hal::WIFI_SUCCESS));
+    }
+
+   private:
+    sp<WifiChip> chip_;
+    ChipId chip_id_ = kFakeChipId;
+    std::shared_ptr<NiceMock<legacy_hal::MockWifiLegacyHal>> legacy_hal_{
+        new NiceMock<legacy_hal::MockWifiLegacyHal>};
+    std::shared_ptr<NiceMock<mode_controller::MockWifiModeController>>
+        mode_controller_{new NiceMock<mode_controller::MockWifiModeController>};
+    std::shared_ptr<NiceMock<feature_flags::MockWifiFeatureFlags>>
+        feature_flags_{new NiceMock<feature_flags::MockWifiFeatureFlags>};
+};
+
+////////// V1 Iface Combinations ////////////
+// Mode 1 - STA + P2P
+// Mode 2 - AP
+class WifiChipV1IfaceCombinationTest : public WifiChipTest {
+   public:
+    void SetUp() override {
+        setupV1IfaceCombination();
+        WifiChipTest::SetUp();
+        // V1 has 2 modes of operation.
+        assertNumberOfModes(2u);
+    }
+};
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateP2p_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateNan_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateAp_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, StaMode_CreateStaP2p_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateAp_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateSta_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateP2p_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1IfaceCombinationTest, ApMode_CreateNan_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+}
+
+////////// V1 + Aware Iface Combinations ////////////
+// Mode 1 - STA + P2P/NAN
+// Mode 2 - AP
+class WifiChipV1_AwareIfaceCombinationTest : public WifiChipTest {
+   public:
+    void SetUp() override {
+        setupV1_AwareIfaceCombination();
+        WifiChipTest::SetUp();
+        // V1_Aware has 2 modes of operation.
+        assertNumberOfModes(2u);
+    }
+};
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateSta_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateP2p_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateNan_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, StaMode_CreateAp_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest,
+       StaMode_CreateStaP2p_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest,
+       StaMode_CreateStaNan_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest,
+       StaMode_CreateStaP2PNan_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest,
+       StaMode_CreateStaNan_AfterP2pRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto p2p_iface_name = createIface(IfaceType::P2P);
+    ASSERT_FALSE(p2p_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+
+    // After removing P2P iface, NAN iface creation should succeed.
+    removeIface(IfaceType::P2P, p2p_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest,
+       StaMode_CreateStaP2p_AfterNanRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto nan_iface_name = createIface(IfaceType::NAN);
+    ASSERT_FALSE(nan_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+
+    // After removing NAN iface, P2P iface creation should succeed.
+    removeIface(IfaceType::NAN, nan_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateAp_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateSta_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateP2p_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_TRUE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV1_AwareIfaceCombinationTest, ApMode_CreateNan_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+}
+
+////////// V2 + Aware Iface Combinations ////////////
+// Mode 1 - STA + STA/AP
+//        - STA + P2P/NAN
+class WifiChipV2_AwareIfaceCombinationTest : public WifiChipTest {
+   public:
+    void SetUp() override {
+        setupV2_AwareIfaceCombination();
+        WifiChipTest::SetUp();
+        // V2_Aware has 1 mode of operation.
+        assertNumberOfModes(1u);
+    }
+};
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateSta_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateP2p_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateNan_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateAp_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaSta_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaAp_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaStaAp_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_TRUE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       CreateStaAp_AfterStaRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto sta_iface_name = createIface(IfaceType::STA);
+    ASSERT_FALSE(sta_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::AP).empty());
+
+    // After removing STA iface, AP iface creation should succeed.
+    removeIface(IfaceType::STA, sta_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       CreateStaSta_AfterApRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto ap_iface_name = createIface(IfaceType::AP);
+    ASSERT_FALSE(ap_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::STA).empty());
+
+    // After removing AP  iface, STA iface creation should succeed.
+    removeIface(IfaceType::AP, ap_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaP2p_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaNan_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateStaP2PNan_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       CreateStaNan_AfterP2pRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto p2p_iface_name = createIface(IfaceType::P2P);
+    ASSERT_FALSE(p2p_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+
+    // After removing P2P iface, NAN iface creation should succeed.
+    removeIface(IfaceType::P2P, p2p_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       CreateStaP2p_AfterNanRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto nan_iface_name = createIface(IfaceType::NAN);
+    ASSERT_FALSE(nan_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+
+    // After removing NAN iface, P2P iface creation should succeed.
+    removeIface(IfaceType::NAN, nan_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateApNan_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest, CreateApP2p_ShouldFail) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    ASSERT_FALSE(createIface(IfaceType::AP).empty());
+    ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       StaMode_CreateStaNan_AfterP2pRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto p2p_iface_name = createIface(IfaceType::P2P);
+    ASSERT_FALSE(p2p_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::NAN).empty());
+
+    // After removing P2P iface, NAN iface creation should succeed.
+    removeIface(IfaceType::P2P, p2p_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::NAN).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       StaMode_CreateStaP2p_AfterNanRemove_ShouldSucceed) {
+    findModeAndConfigureForIfaceType(IfaceType::STA);
+    ASSERT_FALSE(createIface(IfaceType::STA).empty());
+    const auto nan_iface_name = createIface(IfaceType::NAN);
+    ASSERT_FALSE(nan_iface_name.empty());
+    ASSERT_TRUE(createIface(IfaceType::P2P).empty());
+
+    // After removing NAN iface, P2P iface creation should succeed.
+    removeIface(IfaceType::NAN, nan_iface_name);
+    ASSERT_FALSE(createIface(IfaceType::P2P).empty());
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       CreateStaSta_EnsureDifferentIfaceNames) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    const auto sta1_iface_name = createIface(IfaceType::STA);
+    const auto sta2_iface_name = createIface(IfaceType::STA);
+    ASSERT_FALSE(sta1_iface_name.empty());
+    ASSERT_FALSE(sta2_iface_name.empty());
+    ASSERT_NE(sta1_iface_name, sta2_iface_name);
+}
+
+TEST_F(WifiChipV2_AwareIfaceCombinationTest,
+       CreateStaAp_EnsureDifferentIfaceNames) {
+    findModeAndConfigureForIfaceType(IfaceType::AP);
+    const auto sta_iface_name = createIface(IfaceType::STA);
+    const auto ap_iface_name = createIface(IfaceType::AP);
+    ASSERT_FALSE(sta_iface_name.empty());
+    ASSERT_FALSE(ap_iface_name.empty());
+    ASSERT_NE(sta_iface_name, ap_iface_name);
+}
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/wifi.cpp b/wifi/1.2/default/wifi.cpp
index a2d8f9f..06f5058 100644
--- a/wifi/1.2/default/wifi.cpp
+++ b/wifi/1.2/default/wifi.cpp
@@ -33,9 +33,13 @@
 using hidl_return_util::validateAndCall;
 using hidl_return_util::validateAndCallWithLock;
 
-Wifi::Wifi()
-    : legacy_hal_(new legacy_hal::WifiLegacyHal()),
-      mode_controller_(new mode_controller::WifiModeController()),
+Wifi::Wifi(
+    const std::shared_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+    const std::shared_ptr<mode_controller::WifiModeController> mode_controller,
+    const std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags)
+    : legacy_hal_(legacy_hal),
+      mode_controller_(mode_controller),
+      feature_flags_(feature_flags),
       run_state_(RunState::STOPPED) {}
 
 bool Wifi::isValid() {
@@ -88,10 +92,11 @@
         return createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE,
                                 "HAL is stopping");
     }
-    WifiStatus wifi_status = initializeLegacyHal();
+    WifiStatus wifi_status = initializeModeControllerAndLegacyHal();
     if (wifi_status.code == WifiStatusCode::SUCCESS) {
         // Create the chip instance once the HAL is started.
-        chip_ = new WifiChip(kChipId, legacy_hal_, mode_controller_);
+        chip_ = new WifiChip(kChipId, legacy_hal_, mode_controller_,
+                             feature_flags_);
         run_state_ = RunState::STARTED;
         for (const auto& callback : event_cb_handler_.getCallbacks()) {
             if (!callback->onStart().isOk()) {
@@ -161,7 +166,11 @@
     return {createWifiStatus(WifiStatusCode::SUCCESS), chip_};
 }
 
-WifiStatus Wifi::initializeLegacyHal() {
+WifiStatus Wifi::initializeModeControllerAndLegacyHal() {
+    if (!mode_controller_->initialize()) {
+        LOG(ERROR) << "Failed to initialize firmware mode controller";
+        return createWifiStatus(WifiStatusCode::ERROR_UNKNOWN);
+    }
     legacy_hal::wifi_error legacy_status = legacy_hal_->initialize();
     if (legacy_status != legacy_hal::WIFI_SUCCESS) {
         LOG(ERROR) << "Failed to initialize legacy HAL: "
diff --git a/wifi/1.2/default/wifi.h b/wifi/1.2/default/wifi.h
index 114d774..440c3c7 100644
--- a/wifi/1.2/default/wifi.h
+++ b/wifi/1.2/default/wifi.h
@@ -25,6 +25,7 @@
 
 #include "hidl_callback_util.h"
 #include "wifi_chip.h"
+#include "wifi_feature_flags.h"
 #include "wifi_legacy_hal.h"
 #include "wifi_mode_controller.h"
 
@@ -39,7 +40,10 @@
  */
 class Wifi : public V1_2::IWifi {
    public:
-    Wifi();
+    Wifi(const std::shared_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+         const std::shared_ptr<mode_controller::WifiModeController>
+             mode_controller,
+         const std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags);
 
     bool isValid();
 
@@ -64,7 +68,7 @@
     std::pair<WifiStatus, std::vector<ChipId>> getChipIdsInternal();
     std::pair<WifiStatus, sp<IWifiChip>> getChipInternal(ChipId chip_id);
 
-    WifiStatus initializeLegacyHal();
+    WifiStatus initializeModeControllerAndLegacyHal();
     WifiStatus stopLegacyHalAndDeinitializeModeController(
         std::unique_lock<std::recursive_mutex>* lock);
 
@@ -72,6 +76,7 @@
     // and shared with all the child HIDL interface objects.
     std::shared_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
     std::shared_ptr<mode_controller::WifiModeController> mode_controller_;
+    std::shared_ptr<feature_flags::WifiFeatureFlags> feature_flags_;
     RunState run_state_;
     sp<WifiChip> chip_;
     hidl_callback_util::HidlCallbackHandler<IWifiEventCallback>
diff --git a/wifi/1.2/default/wifi_ap_iface.cpp b/wifi/1.2/default/wifi_ap_iface.cpp
index 284f9b9..92b7b48 100644
--- a/wifi/1.2/default/wifi_ap_iface.cpp
+++ b/wifi/1.2/default/wifi_ap_iface.cpp
@@ -40,6 +40,8 @@
 
 bool WifiApIface::isValid() { return is_valid_; }
 
+std::string WifiApIface::getName() { return ifname_; }
+
 Return<void> WifiApIface::getName(getName_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
                            &WifiApIface::getNameInternal, hidl_status_cb);
diff --git a/wifi/1.2/default/wifi_ap_iface.h b/wifi/1.2/default/wifi_ap_iface.h
index 6375121..5363ec2 100644
--- a/wifi/1.2/default/wifi_ap_iface.h
+++ b/wifi/1.2/default/wifi_ap_iface.h
@@ -39,6 +39,7 @@
     // Refer to |WifiChip::invalidate()|.
     void invalidate();
     bool isValid();
+    std::string getName();
 
     // HIDL methods exposed.
     Return<void> getName(getName_cb hidl_status_cb) override;
diff --git a/wifi/1.2/default/wifi_chip.cpp b/wifi/1.2/default/wifi_chip.cpp
index 4e2191d..adba054 100644
--- a/wifi/1.2/default/wifi_chip.cpp
+++ b/wifi/1.2/default/wifi_chip.cpp
@@ -20,27 +20,59 @@
 #include "hidl_return_util.h"
 #include "hidl_struct_util.h"
 #include "wifi_chip.h"
-#include "wifi_feature_flags.h"
 #include "wifi_status_util.h"
 
 namespace {
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::wifi::V1_0::ChipModeId;
-using android::hardware::wifi::V1_0::IWifiChip;
 using android::hardware::wifi::V1_0::IfaceType;
+using android::hardware::wifi::V1_0::IWifiChip;
 using android::sp;
 
-constexpr ChipModeId kStaChipModeId = 0;
-constexpr ChipModeId kApChipModeId = 1;
 constexpr ChipModeId kInvalidModeId = UINT32_MAX;
+// These mode ID's should be unique (even across combo versions). Refer to
+// handleChipConfiguration() for it's usage.
+// Mode ID's for V1
+constexpr ChipModeId kV1StaChipModeId = 0;
+constexpr ChipModeId kV1ApChipModeId = 1;
+// Mode ID for V2
+constexpr ChipModeId kV2ChipModeId = 2;
 
 template <typename Iface>
-void invalidateAndClear(sp<Iface>& iface) {
-    if (iface.get()) {
+void invalidateAndClear(std::vector<sp<Iface>>& ifaces, sp<Iface> iface) {
+    iface->invalidate();
+    ifaces.erase(std::remove(ifaces.begin(), ifaces.end(), iface),
+                 ifaces.end());
+}
+
+template <typename Iface>
+void invalidateAndClearAll(std::vector<sp<Iface>>& ifaces) {
+    for (const auto& iface : ifaces) {
         iface->invalidate();
-        iface.clear();
     }
+    ifaces.clear();
+}
+
+template <typename Iface>
+std::vector<hidl_string> getNames(std::vector<sp<Iface>>& ifaces) {
+    std::vector<hidl_string> names;
+    for (const auto& iface : ifaces) {
+        names.emplace_back(iface->getName());
+    }
+    return names;
+}
+
+template <typename Iface>
+sp<Iface> findUsingName(std::vector<sp<Iface>>& ifaces,
+                        const std::string& name) {
+    std::vector<hidl_string> names;
+    for (const auto& iface : ifaces) {
+        if (name == iface->getName()) {
+            return iface;
+        }
+    }
+    return nullptr;
 }
 
 std::string getWlan0IfaceName() {
@@ -49,13 +81,11 @@
     return buffer.data();
 }
 
-/** Not used yet.
 std::string getWlan1IfaceName() {
-  std::array<char, PROPERTY_VALUE_MAX> buffer;
-  property_get("wifi.concurrent.interface", buffer.data(), "wlan1");
-  return buffer.data();
+    std::array<char, PROPERTY_VALUE_MAX> buffer;
+    property_get("wifi.concurrent.interface", buffer.data(), "wlan1");
+    return buffer.data();
 }
-*/
 
 std::string getP2pIfaceName() {
     std::array<char, PROPERTY_VALUE_MAX> buffer;
@@ -75,13 +105,17 @@
 
 WifiChip::WifiChip(
     ChipId chip_id, const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
-    const std::weak_ptr<mode_controller::WifiModeController> mode_controller)
+    const std::weak_ptr<mode_controller::WifiModeController> mode_controller,
+    const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags)
     : chip_id_(chip_id),
       legacy_hal_(legacy_hal),
       mode_controller_(mode_controller),
+      feature_flags_(feature_flags),
       is_valid_(true),
       current_mode_id_(kInvalidModeId),
-      debug_ring_buffer_cb_registered_(false) {}
+      debug_ring_buffer_cb_registered_(false) {
+    populateModes();
+}
 
 void WifiChip::invalidate() {
     invalidateAndRemoveAllIfaces();
@@ -317,10 +351,10 @@
 }
 
 void WifiChip::invalidateAndRemoveAllIfaces() {
-    invalidateAndClear(ap_iface_);
-    invalidateAndClear(nan_iface_);
-    invalidateAndClear(p2p_iface_);
-    invalidateAndClear(sta_iface_);
+    invalidateAndClearAll(ap_ifaces_);
+    invalidateAndClearAll(nan_ifaces_);
+    invalidateAndClearAll(p2p_ifaces_);
+    invalidateAndClearAll(sta_ifaces_);
     // Since all the ifaces are invalid now, all RTT controller objects
     // using those ifaces also need to be invalidated.
     for (const auto& rtt : rtt_controllers_) {
@@ -365,43 +399,13 @@
 
 std::pair<WifiStatus, std::vector<IWifiChip::ChipMode>>
 WifiChip::getAvailableModesInternal() {
-    // The chip combination supported for current devices is fixed for now with
-    // 2 separate modes of operation:
-    // Mode 1 (STA mode): Will support 1 STA and 1 P2P or NAN iface operations
-    // concurrently [NAN conditional on wifiHidlFeatureAware]
-    // Mode 2 (AP mode): Will support 1 AP iface operations.
-    // TODO (b/32997844): Read this from some device specific flags in the
-    // makefile.
-    // STA mode iface combinations.
-    const IWifiChip::ChipIfaceCombinationLimit
-        sta_chip_iface_combination_limit_1 = {{IfaceType::STA}, 1};
-    IWifiChip::ChipIfaceCombinationLimit sta_chip_iface_combination_limit_2;
-    if (WifiFeatureFlags::wifiHidlFeatureAware) {
-        sta_chip_iface_combination_limit_2 = {{IfaceType::P2P, IfaceType::NAN},
-                                              1};
-    } else {
-        sta_chip_iface_combination_limit_2 = {{IfaceType::P2P}, 1};
-    }
-    const IWifiChip::ChipIfaceCombination sta_chip_iface_combination = {
-        {sta_chip_iface_combination_limit_1,
-         sta_chip_iface_combination_limit_2}};
-    const IWifiChip::ChipMode sta_chip_mode = {kStaChipModeId,
-                                               {sta_chip_iface_combination}};
-    // AP mode iface combinations.
-    const IWifiChip::ChipIfaceCombinationLimit ap_chip_iface_combination_limit =
-        {{IfaceType::AP}, 1};
-    const IWifiChip::ChipIfaceCombination ap_chip_iface_combination = {
-        {ap_chip_iface_combination_limit}};
-    const IWifiChip::ChipMode ap_chip_mode = {kApChipModeId,
-                                              {ap_chip_iface_combination}};
-    return {createWifiStatus(WifiStatusCode::SUCCESS),
-            {sta_chip_mode, ap_chip_mode}};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), modes_};
 }
 
 WifiStatus WifiChip::configureChipInternal(
     /* NONNULL */ std::unique_lock<std::recursive_mutex>* lock,
     ChipModeId mode_id) {
-    if (mode_id != kStaChipModeId && mode_id != kApChipModeId) {
+    if (!isValidModeId(mode_id)) {
         return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
     }
     if (mode_id == current_mode_id_) {
@@ -429,7 +433,7 @@
 }
 
 std::pair<WifiStatus, uint32_t> WifiChip::getModeInternal() {
-    if (current_mode_id_ == kInvalidModeId) {
+    if (!isValidModeId(current_mode_id_)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE),
                 current_mode_id_};
     }
@@ -497,40 +501,43 @@
 }
 
 std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::createApIfaceInternal() {
-    if (current_mode_id_ != kApChipModeId || ap_iface_.get()) {
+    if (!canCurrentModeSupportIfaceOfType(IfaceType::AP)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
-    std::string ifname = getWlan0IfaceName();
-    ap_iface_ = new WifiApIface(ifname, legacy_hal_);
+    std::string ifname = allocateApOrStaIfaceName();
+    sp<WifiApIface> iface = new WifiApIface(ifname, legacy_hal_);
+    ap_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceAdded(IfaceType::AP, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
         }
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), ap_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 std::pair<WifiStatus, std::vector<hidl_string>>
 WifiChip::getApIfaceNamesInternal() {
-    if (!ap_iface_.get()) {
+    if (ap_ifaces_.empty()) {
         return {createWifiStatus(WifiStatusCode::SUCCESS), {}};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), {getWlan0IfaceName()}};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(ap_ifaces_)};
 }
 
 std::pair<WifiStatus, sp<IWifiApIface>> WifiChip::getApIfaceInternal(
     const std::string& ifname) {
-    if (!ap_iface_.get() || (ifname != getWlan0IfaceName())) {
+    const auto iface = findUsingName(ap_ifaces_, ifname);
+    if (!iface.get()) {
         return {createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS), nullptr};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), ap_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 WifiStatus WifiChip::removeApIfaceInternal(const std::string& ifname) {
-    if (!ap_iface_.get() || (ifname != getWlan0IfaceName())) {
+    const auto iface = findUsingName(ap_ifaces_, ifname);
+    if (!iface.get()) {
         return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
     }
-    invalidateAndClear(ap_iface_);
+    invalidateAndClear(ap_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::AP, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
@@ -540,46 +547,44 @@
 }
 
 std::pair<WifiStatus, sp<IWifiNanIface>> WifiChip::createNanIfaceInternal() {
-    // Only 1 of NAN or P2P iface can be active at a time.
-    if (WifiFeatureFlags::wifiHidlFeatureAware) {
-        if (current_mode_id_ != kStaChipModeId || nan_iface_.get() ||
-            p2p_iface_.get()) {
-            return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
-        }
-        std::string ifname = getWlan0IfaceName();
-        nan_iface_ = new WifiNanIface(ifname, legacy_hal_);
-        for (const auto& callback : event_cb_handler_.getCallbacks()) {
-            if (!callback->onIfaceAdded(IfaceType::NAN, ifname).isOk()) {
-                LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
-            }
-        }
-        return {createWifiStatus(WifiStatusCode::SUCCESS), nan_iface_};
-    } else {
+    if (!canCurrentModeSupportIfaceOfType(IfaceType::NAN)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
+    // These are still assumed to be based on wlan0.
+    std::string ifname = getWlan0IfaceName();
+    sp<WifiNanIface> iface = new WifiNanIface(ifname, legacy_hal_);
+    nan_ifaces_.push_back(iface);
+    for (const auto& callback : event_cb_handler_.getCallbacks()) {
+        if (!callback->onIfaceAdded(IfaceType::NAN, ifname).isOk()) {
+            LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
+        }
+    }
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 std::pair<WifiStatus, std::vector<hidl_string>>
 WifiChip::getNanIfaceNamesInternal() {
-    if (!nan_iface_.get()) {
+    if (nan_ifaces_.empty()) {
         return {createWifiStatus(WifiStatusCode::SUCCESS), {}};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), {getWlan0IfaceName()}};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(nan_ifaces_)};
 }
 
 std::pair<WifiStatus, sp<IWifiNanIface>> WifiChip::getNanIfaceInternal(
     const std::string& ifname) {
-    if (!nan_iface_.get() || (ifname != getWlan0IfaceName())) {
+    const auto iface = findUsingName(nan_ifaces_, ifname);
+    if (!iface.get()) {
         return {createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS), nullptr};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), nan_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 WifiStatus WifiChip::removeNanIfaceInternal(const std::string& ifname) {
-    if (!nan_iface_.get() || (ifname != getWlan0IfaceName())) {
+    const auto iface = findUsingName(nan_ifaces_, ifname);
+    if (!iface.get()) {
         return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
     }
-    invalidateAndClear(nan_iface_);
+    invalidateAndClear(nan_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::NAN, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
@@ -589,42 +594,43 @@
 }
 
 std::pair<WifiStatus, sp<IWifiP2pIface>> WifiChip::createP2pIfaceInternal() {
-    // Only 1 of NAN or P2P iface can be active at a time.
-    if (current_mode_id_ != kStaChipModeId || p2p_iface_.get() ||
-        nan_iface_.get()) {
+    if (!canCurrentModeSupportIfaceOfType(IfaceType::P2P)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
     std::string ifname = getP2pIfaceName();
-    p2p_iface_ = new WifiP2pIface(ifname, legacy_hal_);
+    sp<WifiP2pIface> iface = new WifiP2pIface(ifname, legacy_hal_);
+    p2p_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceAdded(IfaceType::P2P, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
         }
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), p2p_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 std::pair<WifiStatus, std::vector<hidl_string>>
 WifiChip::getP2pIfaceNamesInternal() {
-    if (!p2p_iface_.get()) {
+    if (p2p_ifaces_.empty()) {
         return {createWifiStatus(WifiStatusCode::SUCCESS), {}};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), {getP2pIfaceName()}};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(p2p_ifaces_)};
 }
 
 std::pair<WifiStatus, sp<IWifiP2pIface>> WifiChip::getP2pIfaceInternal(
     const std::string& ifname) {
-    if (!p2p_iface_.get() || (ifname != getP2pIfaceName())) {
+    const auto iface = findUsingName(p2p_ifaces_, ifname);
+    if (!iface.get()) {
         return {createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS), nullptr};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), p2p_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 WifiStatus WifiChip::removeP2pIfaceInternal(const std::string& ifname) {
-    if (!p2p_iface_.get() || (ifname != getP2pIfaceName())) {
+    const auto iface = findUsingName(p2p_ifaces_, ifname);
+    if (!iface.get()) {
         return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
     }
-    invalidateAndClear(p2p_iface_);
+    invalidateAndClear(p2p_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::P2P, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
@@ -634,40 +640,43 @@
 }
 
 std::pair<WifiStatus, sp<IWifiStaIface>> WifiChip::createStaIfaceInternal() {
-    if (current_mode_id_ != kStaChipModeId || sta_iface_.get()) {
+    if (!canCurrentModeSupportIfaceOfType(IfaceType::STA)) {
         return {createWifiStatus(WifiStatusCode::ERROR_NOT_AVAILABLE), {}};
     }
-    std::string ifname = getWlan0IfaceName();
-    sta_iface_ = new WifiStaIface(ifname, legacy_hal_);
+    std::string ifname = allocateApOrStaIfaceName();
+    sp<WifiStaIface> iface = new WifiStaIface(ifname, legacy_hal_);
+    sta_ifaces_.push_back(iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceAdded(IfaceType::STA, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceAdded callback";
         }
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), sta_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 std::pair<WifiStatus, std::vector<hidl_string>>
 WifiChip::getStaIfaceNamesInternal() {
-    if (!sta_iface_.get()) {
+    if (sta_ifaces_.empty()) {
         return {createWifiStatus(WifiStatusCode::SUCCESS), {}};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), {getWlan0IfaceName()}};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), getNames(sta_ifaces_)};
 }
 
 std::pair<WifiStatus, sp<IWifiStaIface>> WifiChip::getStaIfaceInternal(
     const std::string& ifname) {
-    if (!sta_iface_.get() || (ifname != getWlan0IfaceName())) {
+    const auto iface = findUsingName(sta_ifaces_, ifname);
+    if (!iface.get()) {
         return {createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS), nullptr};
     }
-    return {createWifiStatus(WifiStatusCode::SUCCESS), sta_iface_};
+    return {createWifiStatus(WifiStatusCode::SUCCESS), iface};
 }
 
 WifiStatus WifiChip::removeStaIfaceInternal(const std::string& ifname) {
-    if (!sta_iface_.get() || (ifname != getWlan0IfaceName())) {
+    const auto iface = findUsingName(sta_ifaces_, ifname);
+    if (!iface.get()) {
         return createWifiStatus(WifiStatusCode::ERROR_INVALID_ARGS);
     }
-    invalidateAndClear(sta_iface_);
+    invalidateAndClear(sta_ifaces_, iface);
     for (const auto& callback : event_cb_handler_.getCallbacks()) {
         if (!callback->onIfaceRemoved(IfaceType::STA, ifname).isOk()) {
             LOG(ERROR) << "Failed to invoke onIfaceRemoved callback";
@@ -801,7 +810,7 @@
     ChipModeId mode_id) {
     // If the chip is already configured in a different mode, stop
     // the legacy HAL and then start it after firmware mode change.
-    if (current_mode_id_ != kInvalidModeId) {
+    if (isValidModeId(current_mode_id_)) {
         LOG(INFO) << "Reconfiguring chip from mode " << current_mode_id_
                   << " to mode " << mode_id;
         invalidateAndRemoveAllIfaces();
@@ -813,10 +822,11 @@
             return createWifiStatusFromLegacyError(legacy_status);
         }
     }
-    bool success;
-    if (mode_id == kStaChipModeId) {
+    // Firmware mode change not needed for V2 devices.
+    bool success = true;
+    if (mode_id == kV1StaChipModeId) {
         success = mode_controller_.lock()->changeFirmwareMode(IfaceType::STA);
-    } else {
+    } else if (mode_id == kV1ApChipModeId) {
         success = mode_controller_.lock()->changeFirmwareMode(IfaceType::AP);
     }
     if (!success) {
@@ -871,6 +881,204 @@
     return createWifiStatusFromLegacyError(legacy_status);
 }
 
+void WifiChip::populateModes() {
+    // The chip combination supported for current devices is fixed.
+    // They can be one of the following based on device features:
+    // a) 2 separate modes of operation with 1 interface combination each:
+    //    Mode 1 (STA mode): Will support 1 STA and 1 P2P or NAN(optional)
+    //                       concurrent iface operations.
+    //    Mode 2 (AP mode): Will support 1 AP iface operation.
+    //
+    // b) 1 mode of operation with 2 interface combinations
+    // (conditional on isDualInterfaceSupported()):
+    //    Interface Combination 1: Will support 1 STA and 1 P2P or NAN(optional)
+    //                             concurrent iface operations.
+    //    Interface Combination 2: Will support 1 STA and 1 STA or AP concurrent
+    //                             iface operations.
+    // If Aware is enabled (conditional on isAwareSupported()), the iface
+    // combination will be modified to support either P2P or NAN in place of
+    // just P2P.
+    if (feature_flags_.lock()->isDualInterfaceSupported()) {
+        // V2 Iface combinations for Mode Id = 2.
+        const IWifiChip::ChipIfaceCombinationLimit
+            chip_iface_combination_limit_1 = {{IfaceType::STA}, 1};
+        const IWifiChip::ChipIfaceCombinationLimit
+            chip_iface_combination_limit_2 = {{IfaceType::STA, IfaceType::AP},
+                                              1};
+        IWifiChip::ChipIfaceCombinationLimit chip_iface_combination_limit_3;
+        if (feature_flags_.lock()->isAwareSupported()) {
+            chip_iface_combination_limit_3 = {{IfaceType::P2P, IfaceType::NAN},
+                                              1};
+        } else {
+            chip_iface_combination_limit_3 = {{IfaceType::P2P}, 1};
+        }
+        const IWifiChip::ChipIfaceCombination chip_iface_combination_1 = {
+            {chip_iface_combination_limit_1, chip_iface_combination_limit_2}};
+        const IWifiChip::ChipIfaceCombination chip_iface_combination_2 = {
+            {chip_iface_combination_limit_1, chip_iface_combination_limit_3}};
+        const IWifiChip::ChipMode chip_mode = {
+            kV2ChipModeId,
+            {chip_iface_combination_1, chip_iface_combination_2}};
+        modes_ = {chip_mode};
+    } else {
+        // V1 Iface combinations for Mode Id = 0. (STA Mode)
+        const IWifiChip::ChipIfaceCombinationLimit
+            sta_chip_iface_combination_limit_1 = {{IfaceType::STA}, 1};
+        IWifiChip::ChipIfaceCombinationLimit sta_chip_iface_combination_limit_2;
+        if (feature_flags_.lock()->isAwareSupported()) {
+            sta_chip_iface_combination_limit_2 = {
+                {IfaceType::P2P, IfaceType::NAN}, 1};
+        } else {
+            sta_chip_iface_combination_limit_2 = {{IfaceType::P2P}, 1};
+        }
+        const IWifiChip::ChipIfaceCombination sta_chip_iface_combination = {
+            {sta_chip_iface_combination_limit_1,
+             sta_chip_iface_combination_limit_2}};
+        const IWifiChip::ChipMode sta_chip_mode = {
+            kV1StaChipModeId, {sta_chip_iface_combination}};
+        // Iface combinations for Mode Id = 1. (AP Mode)
+        const IWifiChip::ChipIfaceCombinationLimit
+            ap_chip_iface_combination_limit = {{IfaceType::AP}, 1};
+        const IWifiChip::ChipIfaceCombination ap_chip_iface_combination = {
+            {ap_chip_iface_combination_limit}};
+        const IWifiChip::ChipMode ap_chip_mode = {kV1ApChipModeId,
+                                                  {ap_chip_iface_combination}};
+        modes_ = {sta_chip_mode, ap_chip_mode};
+    }
+}
+
+std::vector<IWifiChip::ChipIfaceCombination>
+WifiChip::getCurrentModeIfaceCombinations() {
+    if (!isValidModeId(current_mode_id_)) {
+        LOG(ERROR) << "Chip not configured in a mode yet";
+        return {};
+    }
+    for (const auto& mode : modes_) {
+        if (mode.id == current_mode_id_) {
+            return mode.availableCombinations;
+        }
+    }
+    CHECK(0) << "Expected to find iface combinations for current mode!";
+    return {};
+}
+
+// Returns a map indexed by IfaceType with the number of ifaces currently
+// created of the corresponding type.
+std::map<IfaceType, size_t> WifiChip::getCurrentIfaceCombination() {
+    std::map<IfaceType, size_t> iface_counts;
+    iface_counts[IfaceType::AP] = ap_ifaces_.size();
+    iface_counts[IfaceType::NAN] = nan_ifaces_.size();
+    iface_counts[IfaceType::P2P] = p2p_ifaces_.size();
+    iface_counts[IfaceType::STA] = sta_ifaces_.size();
+    return iface_counts;
+}
+
+// This expands the provided iface combinations to a more parseable
+// form. Returns a vector of available combinations possible with the number
+// of ifaces of each type in the combination.
+// This method is a port of HalDeviceManager.expandIfaceCombos() from framework.
+std::vector<std::map<IfaceType, size_t>> WifiChip::expandIfaceCombinations(
+    const IWifiChip::ChipIfaceCombination& combination) {
+    uint32_t num_expanded_combos = 1;
+    for (const auto& limit : combination.limits) {
+        for (uint32_t i = 0; i < limit.maxIfaces; i++) {
+            num_expanded_combos *= limit.types.size();
+        }
+    }
+
+    // Allocate the vector of expanded combos and reset all iface counts to 0
+    // in each combo.
+    std::vector<std::map<IfaceType, size_t>> expanded_combos;
+    expanded_combos.resize(num_expanded_combos);
+    for (auto& expanded_combo : expanded_combos) {
+        for (const auto type :
+             {IfaceType::AP, IfaceType::NAN, IfaceType::P2P, IfaceType::STA}) {
+            expanded_combo[type] = 0;
+        }
+    }
+    uint32_t span = num_expanded_combos;
+    for (const auto& limit : combination.limits) {
+        for (uint32_t i = 0; i < limit.maxIfaces; i++) {
+            span /= limit.types.size();
+            for (uint32_t k = 0; k < num_expanded_combos; ++k) {
+                const auto iface_type =
+                    limit.types[(k / span) % limit.types.size()];
+                expanded_combos[k][iface_type]++;
+            }
+        }
+    }
+    return expanded_combos;
+}
+
+bool WifiChip::canExpandedIfaceCombinationSupportIfaceOfType(
+    const std::map<IfaceType, size_t>& combo, IfaceType requested_type) {
+    const auto current_combo = getCurrentIfaceCombination();
+
+    // Check if we have space for 1 more iface of |type| in this combo
+    for (const auto type :
+         {IfaceType::AP, IfaceType::NAN, IfaceType::P2P, IfaceType::STA}) {
+        size_t num_ifaces_needed = current_combo.at(type);
+        if (type == requested_type) {
+            num_ifaces_needed++;
+        }
+        size_t num_ifaces_allowed = combo.at(type);
+        if (num_ifaces_needed > num_ifaces_allowed) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// This method does the following:
+// a) Enumerate all possible iface combos by expanding the current
+//    ChipIfaceCombination.
+// b) Check if the requested iface type can be added to the current mode.
+bool WifiChip::canCurrentModeSupportIfaceOfType(IfaceType type) {
+    if (!isValidModeId(current_mode_id_)) {
+        LOG(ERROR) << "Chip not configured in a mode yet";
+        return false;
+    }
+    const auto combinations = getCurrentModeIfaceCombinations();
+    for (const auto& combination : combinations) {
+        const auto expanded_combos = expandIfaceCombinations(combination);
+        for (const auto& expanded_combo : expanded_combos) {
+            if (canExpandedIfaceCombinationSupportIfaceOfType(expanded_combo,
+                                                              type)) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool WifiChip::isValidModeId(ChipModeId mode_id) {
+    for (const auto& mode : modes_) {
+        if (mode.id == mode_id) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Return "wlan0", if "wlan0" is not already in use, else return "wlan1".
+// This is based on the assumption that we'll have a max of 2 concurrent
+// AP/STA ifaces.
+std::string WifiChip::allocateApOrStaIfaceName() {
+    auto ap_iface = findUsingName(ap_ifaces_, getWlan0IfaceName());
+    auto sta_iface = findUsingName(sta_ifaces_, getWlan0IfaceName());
+    if (!ap_iface.get() && !sta_iface.get()) {
+        return getWlan0IfaceName();
+    }
+    ap_iface = findUsingName(ap_ifaces_, getWlan1IfaceName());
+    sta_iface = findUsingName(sta_ifaces_, getWlan1IfaceName());
+    if (!ap_iface.get() && !sta_iface.get()) {
+        return getWlan1IfaceName();
+    }
+    // This should never happen. We screwed up somewhere if it did.
+    CHECK(0) << "wlan0 and wlan1 in use already!";
+    return {};
+}
+
 }  // namespace implementation
 }  // namespace V1_2
 }  // namespace wifi
diff --git a/wifi/1.2/default/wifi_chip.h b/wifi/1.2/default/wifi_chip.h
index ac59d59..b5dcc8c 100644
--- a/wifi/1.2/default/wifi_chip.h
+++ b/wifi/1.2/default/wifi_chip.h
@@ -20,10 +20,11 @@
 #include <map>
 
 #include <android-base/macros.h>
-#include <android/hardware/wifi/1.1/IWifiChip.h>
+#include <android/hardware/wifi/1.2/IWifiChip.h>
 
 #include "hidl_callback_util.h"
 #include "wifi_ap_iface.h"
+#include "wifi_feature_flags.h"
 #include "wifi_legacy_hal.h"
 #include "wifi_mode_controller.h"
 #include "wifi_nan_iface.h"
@@ -43,12 +44,14 @@
  * Since there is only a single chip instance used today, there is no
  * identifying handle information stored here.
  */
-class WifiChip : public V1_1::IWifiChip {
+class WifiChip : public V1_2::IWifiChip {
    public:
-    WifiChip(ChipId chip_id,
-             const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
-             const std::weak_ptr<mode_controller::WifiModeController>
-                 mode_controller);
+    WifiChip(
+        ChipId chip_id,
+        const std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal,
+        const std::weak_ptr<mode_controller::WifiModeController>
+            mode_controller,
+        const std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags);
     // HIDL does not provide a built-in mechanism to let the server invalidate
     // a HIDL interface object after creation. If any client process holds onto
     // a reference to the object in their context, any method calls on that
@@ -190,16 +193,31 @@
         std::unique_lock<std::recursive_mutex>* lock, ChipModeId mode_id);
     WifiStatus registerDebugRingBufferCallback();
 
+    void populateModes();
+    std::vector<IWifiChip::ChipIfaceCombination>
+    getCurrentModeIfaceCombinations();
+    std::map<IfaceType, size_t> getCurrentIfaceCombination();
+    std::vector<std::map<IfaceType, size_t>> expandIfaceCombinations(
+        const IWifiChip::ChipIfaceCombination& combination);
+    bool canExpandedIfaceCombinationSupportIfaceOfType(
+        const std::map<IfaceType, size_t>& combo, IfaceType type);
+    bool canCurrentModeSupportIfaceOfType(IfaceType type);
+    bool isValidModeId(ChipModeId mode_id);
+    std::string allocateApOrStaIfaceName();
+
     ChipId chip_id_;
     std::weak_ptr<legacy_hal::WifiLegacyHal> legacy_hal_;
     std::weak_ptr<mode_controller::WifiModeController> mode_controller_;
-    sp<WifiApIface> ap_iface_;
-    sp<WifiNanIface> nan_iface_;
-    sp<WifiP2pIface> p2p_iface_;
-    sp<WifiStaIface> sta_iface_;
+    std::weak_ptr<feature_flags::WifiFeatureFlags> feature_flags_;
+    std::vector<sp<WifiApIface>> ap_ifaces_;
+    std::vector<sp<WifiNanIface>> nan_ifaces_;
+    std::vector<sp<WifiP2pIface>> p2p_ifaces_;
+    std::vector<sp<WifiStaIface>> sta_ifaces_;
     std::vector<sp<WifiRttController>> rtt_controllers_;
     bool is_valid_;
+    // Members pertaining to chip configuration.
     uint32_t current_mode_id_;
+    std::vector<IWifiChip::ChipMode> modes_;
     // The legacy ring buffer callback API has only a global callback
     // registration mechanism. Use this to check if we have already
     // registered a callback.
diff --git a/wifi/1.2/default/wifi_feature_flags.cpp b/wifi/1.2/default/wifi_feature_flags.cpp
new file mode 100644
index 0000000..554d4d5
--- /dev/null
+++ b/wifi/1.2/default/wifi_feature_flags.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "wifi_feature_flags.h"
+
+namespace {
+#ifdef WIFI_HIDL_FEATURE_AWARE
+static const bool wifiHidlFeatureAware = true;
+#else
+static const bool wifiHidlFeatureAware = false;
+#endif  // WIFI_HIDL_FEATURE_AWARE
+#ifdef WIFI_HIDL_FEATURE_DUAL_INTERFACE
+static const bool wifiHidlFeatureDualInterface = true;
+#else
+static const bool wifiHidlFeatureDualInterface = false;
+#endif  // WIFI_HIDL_FEATURE_DUAL_INTERFACE
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace V1_2 {
+namespace implementation {
+namespace feature_flags {
+
+WifiFeatureFlags::WifiFeatureFlags() {}
+bool WifiFeatureFlags::isAwareSupported() { return wifiHidlFeatureAware; }
+bool WifiFeatureFlags::isDualInterfaceSupported() {
+    return wifiHidlFeatureDualInterface;
+}
+
+}  // namespace feature_flags
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
diff --git a/wifi/1.2/default/wifi_feature_flags.h b/wifi/1.2/default/wifi_feature_flags.h
index da4ca37..dc0c1ff 100644
--- a/wifi/1.2/default/wifi_feature_flags.h
+++ b/wifi/1.2/default/wifi_feature_flags.h
@@ -22,16 +22,18 @@
 namespace wifi {
 namespace V1_2 {
 namespace implementation {
+namespace feature_flags {
 
 class WifiFeatureFlags {
    public:
-#ifdef WIFI_HIDL_FEATURE_AWARE
-    static const bool wifiHidlFeatureAware = true;
-#else
-    static const bool wifiHidlFeatureAware = false;
-#endif  // WIFI_HIDL_FEATURE_AWARE
+    WifiFeatureFlags();
+    virtual ~WifiFeatureFlags() = default;
+
+    virtual bool isAwareSupported();
+    virtual bool isDualInterfaceSupported();
 };
 
+}  // namespace feature_flags
 }  // namespace implementation
 }  // namespace V1_2
 }  // namespace wifi
diff --git a/wifi/1.2/default/wifi_legacy_hal.cpp b/wifi/1.2/default/wifi_legacy_hal.cpp
index 24b80d5..9abe514 100644
--- a/wifi/1.2/default/wifi_legacy_hal.cpp
+++ b/wifi/1.2/default/wifi_legacy_hal.cpp
@@ -455,7 +455,7 @@
     const std::string& iface_name) {
     feature_set set;
     static_assert(sizeof(set) == sizeof(uint32_t),
-                  "Some features can not be represented in output");
+                  "Some feature_flags can not be represented in output");
     wifi_error status = global_func_table_.wifi_get_supported_feature_set(
         getIfaceHandle(iface_name), &set);
     return {status, static_cast<uint32_t>(set)};
@@ -759,11 +759,11 @@
 
 std::pair<wifi_error, uint32_t> WifiLegacyHal::getLoggerSupportedFeatureSet(
     const std::string& iface_name) {
-    uint32_t supported_features;
+    uint32_t supported_feature_flags;
     wifi_error status =
         global_func_table_.wifi_get_logger_supported_feature_set(
-            getIfaceHandle(iface_name), &supported_features);
-    return {status, supported_features};
+            getIfaceHandle(iface_name), &supported_feature_flags);
+    return {status, supported_feature_flags};
 }
 
 wifi_error WifiLegacyHal::startPktFateMonitoring(
diff --git a/wifi/1.2/default/wifi_legacy_hal.h b/wifi/1.2/default/wifi_legacy_hal.h
index 05f700e..da88f6b 100644
--- a/wifi/1.2/default/wifi_legacy_hal.h
+++ b/wifi/1.2/default/wifi_legacy_hal.h
@@ -142,15 +142,16 @@
 class WifiLegacyHal {
    public:
     WifiLegacyHal();
+    virtual ~WifiLegacyHal() = default;
 
     // Initialize the legacy HAL function table.
-    wifi_error initialize();
+    virtual wifi_error initialize();
     // Start the legacy HAL and the event looper thread.
-    wifi_error start();
+    virtual wifi_error start();
     // Deinitialize the legacy HAL and wait for the event loop thread to exit
     // using a predefined timeout.
-    wifi_error stop(std::unique_lock<std::recursive_mutex>* lock,
-                    const std::function<void()>& on_complete_callback);
+    virtual wifi_error stop(std::unique_lock<std::recursive_mutex>* lock,
+                            const std::function<void()>& on_complete_callback);
     // Wrappers for all the functions in the legacy HAL function table.
     std::pair<wifi_error, std::string> getDriverVersion(
         const std::string& iface_name);
@@ -190,7 +191,7 @@
     wifi_error stopGscan(const std::string& iface_name, wifi_request_id id);
     std::pair<wifi_error, std::vector<uint32_t>> getValidFrequenciesForBand(
         const std::string& iface_name, wifi_band band);
-    wifi_error setDfsFlag(const std::string& iface_name, bool dfs_on);
+    virtual wifi_error setDfsFlag(const std::string& iface_name, bool dfs_on);
     // Link layer stats functions.
     wifi_error enableLinkLayerStats(const std::string& iface_name, bool debug);
     wifi_error disableLinkLayerStats(const std::string& iface_name);
@@ -276,12 +277,12 @@
     wifi_error setRttLcr(const std::string& iface_name, wifi_request_id id,
                          const wifi_lcr_information& info);
     // NAN functions.
-    wifi_error nanRegisterCallbackHandlers(
+    virtual wifi_error nanRegisterCallbackHandlers(
         const std::string& iface_name, const NanCallbackHandlers& callbacks);
     wifi_error nanEnableRequest(const std::string& iface_name,
                                 transaction_id id, const NanEnableRequest& msg);
-    wifi_error nanDisableRequest(const std::string& iface_name,
-                                 transaction_id id);
+    virtual wifi_error nanDisableRequest(const std::string& iface_name,
+                                         transaction_id id);
     wifi_error nanPublishRequest(const std::string& iface_name,
                                  transaction_id id,
                                  const NanPublishRequest& msg);
@@ -312,9 +313,9 @@
     wifi_error nanDataInterfaceCreate(const std::string& iface_name,
                                       transaction_id id,
                                       const std::string& data_iface_name);
-    wifi_error nanDataInterfaceDelete(const std::string& iface_name,
-                                      transaction_id id,
-                                      const std::string& data_iface_name);
+    virtual wifi_error nanDataInterfaceDelete(
+        const std::string& iface_name, transaction_id id,
+        const std::string& data_iface_name);
     wifi_error nanDataRequestInitiator(const std::string& iface_name,
                                        transaction_id id,
                                        const NanDataPathInitiatorRequest& msg);
diff --git a/wifi/1.2/default/wifi_mode_controller.cpp b/wifi/1.2/default/wifi_mode_controller.cpp
index 8a89e81..c286d24 100644
--- a/wifi/1.2/default/wifi_mode_controller.cpp
+++ b/wifi/1.2/default/wifi_mode_controller.cpp
@@ -59,11 +59,15 @@
         convertIfaceTypeToFirmwareMode(type));
 }
 
-bool WifiModeController::changeFirmwareMode(IfaceType type) {
+bool WifiModeController::initialize() {
     if (!driver_tool_->LoadDriver()) {
         LOG(ERROR) << "Failed to load WiFi driver";
         return false;
     }
+    return true;
+}
+
+bool WifiModeController::changeFirmwareMode(IfaceType type) {
     if (!driver_tool_->ChangeFirmwareMode(
             convertIfaceTypeToFirmwareMode(type))) {
         LOG(ERROR) << "Failed to change firmware mode";
diff --git a/wifi/1.2/default/wifi_mode_controller.h b/wifi/1.2/default/wifi_mode_controller.h
index 839bf9e..395aa5d 100644
--- a/wifi/1.2/default/wifi_mode_controller.h
+++ b/wifi/1.2/default/wifi_mode_controller.h
@@ -37,15 +37,17 @@
 class WifiModeController {
    public:
     WifiModeController();
+    virtual ~WifiModeController() = default;
 
     // Checks if a firmware mode change is necessary to support the specified
     // iface type operations.
-    bool isFirmwareModeChangeNeeded(IfaceType type);
+    virtual bool isFirmwareModeChangeNeeded(IfaceType type);
+    virtual bool initialize();
     // Change the firmware mode to support the specified iface type operations.
-    bool changeFirmwareMode(IfaceType type);
+    virtual bool changeFirmwareMode(IfaceType type);
     // Unload the driver. This should be invoked whenever |IWifi.stop()| is
     // invoked.
-    bool deinitialize();
+    virtual bool deinitialize();
 
    private:
     std::unique_ptr<wifi_hal::DriverTool> driver_tool_;
diff --git a/wifi/1.2/default/wifi_nan_iface.cpp b/wifi/1.2/default/wifi_nan_iface.cpp
index e7bbfaa..12e4d7b 100644
--- a/wifi/1.2/default/wifi_nan_iface.cpp
+++ b/wifi/1.2/default/wifi_nan_iface.cpp
@@ -494,6 +494,8 @@
 
 bool WifiNanIface::isValid() { return is_valid_; }
 
+std::string WifiNanIface::getName() { return ifname_; }
+
 std::set<sp<IWifiNanIfaceEventCallback>> WifiNanIface::getEventCallbacks() {
     return event_cb_handler_.getCallbacks();
 }
diff --git a/wifi/1.2/default/wifi_nan_iface.h b/wifi/1.2/default/wifi_nan_iface.h
index 2ca2e45..6fa7b0c 100644
--- a/wifi/1.2/default/wifi_nan_iface.h
+++ b/wifi/1.2/default/wifi_nan_iface.h
@@ -41,6 +41,7 @@
     // Refer to |WifiChip::invalidate()|.
     void invalidate();
     bool isValid();
+    std::string getName();
 
     // HIDL methods exposed.
     Return<void> getName(getName_cb hidl_status_cb) override;
diff --git a/wifi/1.2/default/wifi_p2p_iface.cpp b/wifi/1.2/default/wifi_p2p_iface.cpp
index f1fbf2c..92bbaee 100644
--- a/wifi/1.2/default/wifi_p2p_iface.cpp
+++ b/wifi/1.2/default/wifi_p2p_iface.cpp
@@ -39,6 +39,8 @@
 
 bool WifiP2pIface::isValid() { return is_valid_; }
 
+std::string WifiP2pIface::getName() { return ifname_; }
+
 Return<void> WifiP2pIface::getName(getName_cb hidl_status_cb) {
     return validateAndCall(this, WifiStatusCode::ERROR_WIFI_IFACE_INVALID,
                            &WifiP2pIface::getNameInternal, hidl_status_cb);
diff --git a/wifi/1.2/default/wifi_p2p_iface.h b/wifi/1.2/default/wifi_p2p_iface.h
index 839d3c3..76120b1 100644
--- a/wifi/1.2/default/wifi_p2p_iface.h
+++ b/wifi/1.2/default/wifi_p2p_iface.h
@@ -39,6 +39,7 @@
     // Refer to |WifiChip::invalidate()|.
     void invalidate();
     bool isValid();
+    std::string getName();
 
     // HIDL methods exposed.
     Return<void> getName(getName_cb hidl_status_cb) override;
diff --git a/wifi/1.2/default/wifi_sta_iface.cpp b/wifi/1.2/default/wifi_sta_iface.cpp
index c8605ee..6faf009 100644
--- a/wifi/1.2/default/wifi_sta_iface.cpp
+++ b/wifi/1.2/default/wifi_sta_iface.cpp
@@ -49,6 +49,8 @@
 
 bool WifiStaIface::isValid() { return is_valid_; }
 
+std::string WifiStaIface::getName() { return ifname_; }
+
 std::set<sp<IWifiStaIfaceEventCallback>> WifiStaIface::getEventCallbacks() {
     return event_cb_handler_.getCallbacks();
 }
diff --git a/wifi/1.2/default/wifi_sta_iface.h b/wifi/1.2/default/wifi_sta_iface.h
index 6731864..423365c 100644
--- a/wifi/1.2/default/wifi_sta_iface.h
+++ b/wifi/1.2/default/wifi_sta_iface.h
@@ -42,6 +42,7 @@
     void invalidate();
     bool isValid();
     std::set<sp<IWifiStaIfaceEventCallback>> getEventCallbacks();
+    std::string getName();
 
     // HIDL methods exposed.
     Return<void> getName(getName_cb hidl_status_cb) override;