Merge "Fix typo in HostEndpointInfo definition"
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index 5d92304..74d93b3 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -429,7 +429,7 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.neuralnetworks</name>
-        <version>1-2</version>
+        <version>1-3</version>
         <interface>
             <name>IDevice</name>
             <regex-instance>.*</regex-instance>
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
index 52276b4..a3cd8ae 100644
--- a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnss.aidl
@@ -41,6 +41,7 @@
   android.hardware.gnss.IGnssMeasurementInterface getExtensionGnssMeasurement();
   android.hardware.gnss.IGnssPowerIndication getExtensionGnssPowerIndication();
   @nullable android.hardware.gnss.IGnssBatching getExtensionGnssBatching();
+  @nullable android.hardware.gnss.IGnssGeofence getExtensionGnssGeofence();
   const int ERROR_INVALID_ARGUMENT = 1;
   const int ERROR_ALREADY_INIT = 2;
   const int ERROR_GENERIC = 3;
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssGeofence.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssGeofence.aidl
new file mode 100644
index 0000000..50da5bf
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssGeofence.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssGeofence {
+  void setCallback(in android.hardware.gnss.IGnssGeofenceCallback callback);
+  void addGeofence(in int geofenceId, in double latitudeDegrees, in double longitudeDegrees, in double radiusMeters, in int lastTransition, in int monitorTransitions, in int notificationResponsivenessMs, in int unknownTimerMs);
+  void pauseGeofence(in int geofenceId);
+  void resumeGeofence(in int geofenceId, in int monitorTransitions);
+  void removeGeofence(in int geofenceId);
+}
diff --git a/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssGeofenceCallback.aidl b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssGeofenceCallback.aidl
new file mode 100644
index 0000000..26482ea
--- /dev/null
+++ b/gnss/aidl/aidl_api/android.hardware.gnss/current/android/hardware/gnss/IGnssGeofenceCallback.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.gnss;
+@VintfStability
+interface IGnssGeofenceCallback {
+  void gnssGeofenceTransitionCb(in int geofenceId, in android.hardware.gnss.GnssLocation location, in int transition, in long timestampMillis);
+  void gnssGeofenceStatusCb(in int availability, in android.hardware.gnss.GnssLocation lastLocation);
+  void gnssGeofenceAddCb(in int geofenceId, in int status);
+  void gnssGeofenceRemoveCb(in int geofenceId, in int status);
+  void gnssGeofencePauseCb(in int geofenceId, in int status);
+  void gnssGeofenceResumeCb(in int geofenceId, in int status);
+  const int ENTERED = 1;
+  const int EXITED = 2;
+  const int UNCERTAIN = 4;
+  const int UNAVAILABLE = 1;
+  const int AVAILABLE = 2;
+  const int OPERATION_SUCCESS = 0;
+  const int ERROR_TOO_MANY_GEOFENCES = -100;
+  const int ERROR_ID_EXISTS = -101;
+  const int ERROR_ID_UNKNOWN = -102;
+  const int ERROR_INVALID_TRANSITION = -103;
+  const int ERROR_GENERIC = -149;
+}
diff --git a/gnss/aidl/android/hardware/gnss/IGnss.aidl b/gnss/aidl/android/hardware/gnss/IGnss.aidl
index b12fb82..e019d35 100644
--- a/gnss/aidl/android/hardware/gnss/IGnss.aidl
+++ b/gnss/aidl/android/hardware/gnss/IGnss.aidl
@@ -19,6 +19,7 @@
 import android.hardware.gnss.IGnssBatching;
 import android.hardware.gnss.IGnssCallback;
 import android.hardware.gnss.IGnssConfiguration;
+import android.hardware.gnss.IGnssGeofence;
 import android.hardware.gnss.IGnssMeasurementInterface;
 import android.hardware.gnss.IGnssPowerIndication;
 import android.hardware.gnss.IGnssPsds;
@@ -110,4 +111,11 @@
      * @return Handle to the IGnssBatching interface.
      */
     @nullable IGnssBatching getExtensionGnssBatching();
+
+    /**
+     * This method returns the IGnssGeofence interface.
+     *
+     * @return Handle to the IGnssGeofence interface.
+     */
+    @nullable IGnssGeofence getExtensionGnssGeofence();
 }
diff --git a/gnss/aidl/android/hardware/gnss/IGnssGeofence.aidl b/gnss/aidl/android/hardware/gnss/IGnssGeofence.aidl
new file mode 100644
index 0000000..bb4ff93
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssGeofence.aidl
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.IGnssGeofenceCallback;
+
+/** Extended interface for GNSS Geofence support. */
+@VintfStability
+interface IGnssGeofence {
+    /**
+     * Opens the geofence interface and provides the callback routines to the HAL.
+     *
+     * @param callback Handle to the IGnssGeofenceCallback interface.
+     */
+    void setCallback(in IGnssGeofenceCallback callback);
+
+    /**
+     * Add a geofence area. This api currently supports circular geofences.
+     *
+     * @param geofenceId The id for the geofence. If a geofence with this id already exists, an
+     * error value (ERROR_ID_EXISTS) must be returned.
+     * @param latitudeDegrees The latitude(in degrees) for the geofence lastTransition.
+     * @param longitudeDegrees The longitude(in degrees) for the geofence lastTransition.
+     * @param radiusMeters The radius(in meters) for the geofence lastTransition.
+     * @param lastTransition The current state of the geofence. It can be one of the transition
+     * states (ENTERED, EXITED, UNCERTAIN) as defined in IGnssGeofenceCallback. For example, if
+     * the system already knows that the user is inside the geofence, this will be set to ENTERED.
+     * In most cases, it will be UNCERTAIN.
+     * @param monitorTransitions A bitfield of ENTERED, EXITED and UNCERTAIN. It represents which
+     * transitions to monitor.
+     * @param notificationResponsivenessMs - Defines the best-effort description of how soon must
+     * the callback be called when the transition associated with the Geofence is triggered. For
+     * instance, if set to 1000 milliseconds with ENTERED, the callback must be called 1000
+     * milliseconds within entering the geofence. This parameter is defined in milliseconds.
+     * NOTE: This is not to be confused with the rate that the GNSS is polled at. It is acceptable
+     * to dynamically vary the rate of sampling the GNSS for power-saving reasons; thus the rate of
+     * sampling may be faster or slower than this.
+     * @param unknownTimerMs - The time limit in millisecondsafter which the UNCERTAIN transition
+     * must be triggered.
+     */
+    void addGeofence(in int geofenceId, in double latitudeDegrees, in double longitudeDegrees,
+            in double radiusMeters, in int lastTransition, in int monitorTransitions,
+            in int notificationResponsivenessMs, in int unknownTimerMs);
+
+    /**
+     * Pause monitoring a particular geofence.
+     *
+     * @param geofenceId The id for the geofence.
+     */
+    void pauseGeofence(in int geofenceId);
+
+    /**
+     * Resume monitoring a particular geofence.
+     *
+     * @param geofenceId - The id for the geofence.
+     * @param monitorTransitions Specifies which transitions to monitor. It can be a bitwise OR of
+     * ENTERED, EXITED and UNCERTAIN. This supersedes the value associated provided in the
+     * addGeofence call.
+     */
+    void resumeGeofence(in int geofenceId, in int monitorTransitions);
+
+    /**
+     * Remove a geofence area. After the function returns, no notifications must be sent.
+     *
+     * @param geofenceId The id of the geofence.
+     */
+    void removeGeofence(in int geofenceId);
+}
diff --git a/gnss/aidl/android/hardware/gnss/IGnssGeofenceCallback.aidl b/gnss/aidl/android/hardware/gnss/IGnssGeofenceCallback.aidl
new file mode 100644
index 0000000..c176965
--- /dev/null
+++ b/gnss/aidl/android/hardware/gnss/IGnssGeofenceCallback.aidl
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.gnss;
+
+import android.hardware.gnss.GnssLocation;
+
+/**
+ * The callback interface to report GNSS geofence events from the HAL.
+ *
+ * There are 3 states associated with a Geofence: Inside, Outside, Unknown.
+ * There are 3 transitions: ENTERED, EXITED, UNCERTAIN.
+ *
+ * An example state diagram with confidence level: 95% and Unknown time limit
+ * set as 30 secs is shown below. (confidence level and Unknown time limit are
+ * explained latter).
+ *                         ____________________________
+ *                        |       Unknown (30 secs)   |
+ *                         """"""""""""""""""""""""""""
+ *                            ^ |                  |  ^
+ *                   UNCERTAIN| |ENTERED     EXITED|  |UNCERTAIN
+ *                            | v                  v  |
+ *                        ________    EXITED     _________
+ *                       | Inside | -----------> | Outside |
+ *                       |        | <----------- |         |
+ *                        """"""""    ENTERED    """""""""
+ *
+ * Inside state: We are 95% confident that the user is inside the geofence.
+ * Outside state: We are 95% confident that the user is outside the geofence
+ * Unknown state: Rest of the time.
+ *
+ * The Unknown state is better explained with an example:
+ *
+ *                            __________
+ *                           |         c|
+ *                           |  ___     |    _______
+ *                           |  |a|     |   |   b   |
+ *                           |  """     |    """""""
+ *                           |          |
+ *                            """"""""""
+ * In the diagram above, "a" and "b" are 2 geofences and "c" is the accuracy
+ * circle reported by the GNSS subsystem. Now with regard to "b", the system is
+ * confident that the user is outside. But with regard to "a" is not confident
+ * whether it is inside or outside the geofence. If the accuracy remains the
+ * same for a sufficient period of time, the UNCERTAIN transition must be
+ * triggered with the state set to Unknown. If the accuracy improves later, an
+ * appropriate transition must be triggered.  This "sufficient period of time"
+ * is defined by the parameter in the addGeofenceArea API.
+ * In other words, Unknown state can be interpreted as a state in which the
+ * GNSS subsystem isn't confident enough that the user is either inside or
+ * outside the Geofence. It moves to Unknown state only after the expiry of the
+ * timeout.
+ *
+ * The geofence callback needs to be triggered for the ENTERED and EXITED
+ * transitions, when the GNSS system is confident that the user has entered
+ * (Inside state) or exited (Outside state) the Geofence. An implementation
+ * which uses a value of 95% as the confidence is recommended. The callback
+ * must be triggered only for the transitions requested by the
+ * addGeofenceArea method.
+ *
+ * Even though the diagram and explanation talks about states and transitions,
+ * the callee is only interested in the transitions. The states are mentioned
+ * here for illustrative purposes.
+ *
+ * Startup Scenario: When the device boots up, if an application adds geofences,
+ * and then we get an accurate GNSS location fix, it needs to trigger the
+ * appropriate (ENTERED or EXITED) transition for every Geofence it knows about.
+ * By default, all the Geofences will be in the Unknown state.
+ *
+ * When the GNSS system is unavailable, gnssGeofenceStatusCb must be
+ * called to inform the upper layers of the same. Similarly, when it becomes
+ * available the callback must be called. This is a global state while the
+ * UNKNOWN transition described above is per geofence.
+ *
+ * An important aspect to note is that users of this API (framework), will use
+ * other subsystems like wifi, sensors, cell to handle Unknown case and
+ * hopefully provide a definitive state transition to the third party
+ * application. GNSS Geofence will just be a signal indicating what the GNSS
+ * subsystem knows about the Geofence.
+ */
+@VintfStability
+interface IGnssGeofenceCallback {
+    // Geofence transition status
+    const int ENTERED = 1 << 0;
+    const int EXITED = 1 << 1;
+    const int UNCERTAIN = 1 << 2;
+
+    // Geofence availability status
+    const int UNAVAILABLE = 1 << 0;
+    const int AVAILABLE = 1 << 1;
+
+    // Geofence operation status
+    const int OPERATION_SUCCESS = 0;
+    const int ERROR_TOO_MANY_GEOFENCES = -100;
+    const int ERROR_ID_EXISTS = -101;
+    const int ERROR_ID_UNKNOWN = -102;
+    const int ERROR_INVALID_TRANSITION = -103;
+    const int ERROR_GENERIC = -149;
+
+    /**
+     * The callback associated with the geofence transition.
+     *
+     * The callback must only be called when the caller is interested in that particular transition.
+     * For instance, if the caller is interested only in ENTERED transition, then the callback must
+     * not be called with the EXITED transition.
+     *
+     * IMPORTANT: If a transition is triggered resulting in this callback, the GNSS subsystem will
+     * wake up the application processor, if it is in suspend state.
+     *
+     * @param geofenceId The id associated with the addGeofenceArea.
+     * @param location The current GNSS location.
+     * @param transition Can be one of ENTERED, EXITED or UNCERTAIN.
+     * @param timestamp Timestamp (in UTC milliseconds) when the transition was detected.
+     */
+    void gnssGeofenceTransitionCb(in int geofenceId, in GnssLocation location, in int transition,
+            in long timestampMillis);
+
+    /**
+     * The callback associated with the availability of the GNSS system for geofencing monitoring.
+     * If the GNSS system determines that it cannot monitor geofences because of lack of reliability
+     * or unavailability of the GNSS signals, it will call this callback with UNAVAILABLE parameter.
+     *
+     * @param status - UNAVAILABLE or AVAILABLE.
+     * @param lastLocation - Last known location.
+     */
+    void gnssGeofenceStatusCb(in int availability, in GnssLocation lastLocation);
+
+    /**
+     * The callback associated with the addGeofence call.
+     *
+     * @param geofenceId Id of the geofence.
+     * @param status Returns OPERATION_SUCCESS if the geofence add was successful,
+     *               returns ERROR_TOO_MANY_GEOFENCES if the geofence limit has been reached,
+     *               returns ERROR_ID_EXISTS if geofence with id already exists,
+     *               returns ERROR_INVALID_TRANSITION if the monitorTransition contains an invalid
+     *               transition, and
+     *               returns ERROR_GENERIC for other errors.
+     */
+    void gnssGeofenceAddCb(in int geofenceId, in int status);
+
+    /**
+     * The callback associated with the removeGeofence call.
+     *
+     * @param geofenceId Id of the geofence.
+     * @param status Returns OPERATION_SUCCESS if successful,
+     *               returns ERROR_ID_UNKNOWN for invalid id and
+     *               returns ERROR_GENERIC for others.
+     */
+    void gnssGeofenceRemoveCb(in int geofenceId, in int status);
+
+    /**
+     * The callback associated with the pauseGeofence call.
+     *
+     * @param geofenceId Id of the geofence.
+     * @param status Returns OPERATION_SUCCESS if success,
+     *               returns ERROR_ID_UNKNOWN for invalid id,
+     *               returns ERROR_INVALID_TRANSITION when monitorTransitions is invalid, and
+     *               returns ERROR_GENERIC for other err errors.
+     */
+    void gnssGeofencePauseCb(in int geofenceId, in int status);
+
+    /**
+     * The callback associated with the resumeGeofence call.
+     *
+     * @param geofenceId - Id of the geofence.
+     * @param status Returns OPERATION_SUCCESS if successful,
+     *               returns ERROR_ID_UNKNOWN for invalid id, and
+     *               returns ERROR_GENERIC for others.
+     */
+    void gnssGeofenceResumeCb(in int geofenceId, in int status);
+}
diff --git a/gnss/aidl/default/Android.bp b/gnss/aidl/default/Android.bp
index 892ad15..29bc5c4 100644
--- a/gnss/aidl/default/Android.bp
+++ b/gnss/aidl/default/Android.bp
@@ -57,6 +57,7 @@
     srcs: [
         "Gnss.cpp",
         "GnssBatching.cpp",
+        "GnssGeofence.cpp",
         "GnssHidlHal.cpp",
         "GnssPowerIndication.cpp",
         "GnssPsds.cpp",
diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp
index fbfa2bb..03d9e31 100644
--- a/gnss/aidl/default/Gnss.cpp
+++ b/gnss/aidl/default/Gnss.cpp
@@ -20,6 +20,7 @@
 #include <log/log.h>
 #include "GnssBatching.h"
 #include "GnssConfiguration.h"
+#include "GnssGeofence.h"
 #include "GnssMeasurementInterface.h"
 #include "GnssPsds.h"
 
@@ -96,4 +97,11 @@
     return ndk::ScopedAStatus::ok();
 }
 
+ndk::ScopedAStatus Gnss::getExtensionGnssGeofence(std::shared_ptr<IGnssGeofence>* iGnssGeofence) {
+    ALOGD("Gnss::getExtensionGnssGeofence");
+
+    *iGnssGeofence = SharedRefBase::make<GnssGeofence>();
+    return ndk::ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h
index 3959ef8..8e573b5 100644
--- a/gnss/aidl/default/Gnss.h
+++ b/gnss/aidl/default/Gnss.h
@@ -40,6 +40,8 @@
             std::shared_ptr<IGnssMeasurementInterface>* iGnssMeasurement) override;
     ndk::ScopedAStatus getExtensionGnssBatching(
             std::shared_ptr<IGnssBatching>* iGnssBatching) override;
+    ndk::ScopedAStatus getExtensionGnssGeofence(
+            std::shared_ptr<IGnssGeofence>* iGnssGeofence) override;
 
     std::shared_ptr<GnssConfiguration> mGnssConfiguration;
     std::shared_ptr<GnssPowerIndication> mGnssPowerIndication;
diff --git a/gnss/aidl/default/GnssGeofence.cpp b/gnss/aidl/default/GnssGeofence.cpp
new file mode 100644
index 0000000..eda33cc
--- /dev/null
+++ b/gnss/aidl/default/GnssGeofence.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "GnssGeofenceAidl"
+
+#include "GnssGeofence.h"
+#include <aidl/android/hardware/gnss/BnGnssGeofence.h>
+#include <log/log.h>
+
+namespace aidl::android::hardware::gnss {
+
+std::shared_ptr<IGnssGeofenceCallback> GnssGeofence::sCallback = nullptr;
+
+ndk::ScopedAStatus GnssGeofence::setCallback(
+        const std::shared_ptr<IGnssGeofenceCallback>& callback) {
+    ALOGD("setCallback");
+    std::unique_lock<std::mutex> lock(mMutex);
+    sCallback = callback;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssGeofence::addGeofence(int geofenceId, double latitudeDegrees,
+                                             double longitudeDegrees, double radiusMeters,
+                                             int lastTransition, int monitorTransitions,
+                                             int notificationResponsivenessMs, int unknownTimerMs) {
+    ALOGD("addGeofence. geofenceId=%d, lat=%lf, lng=%lf, rad=%lf, lastTransition=%d, "
+          "monitorTransitions=%d, notificationResponsivenessMs=%d, unknownTimerMs=%d",
+          geofenceId, latitudeDegrees, longitudeDegrees, radiusMeters, lastTransition,
+          monitorTransitions, notificationResponsivenessMs, unknownTimerMs);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssGeofence::pauseGeofence(int geofenceId) {
+    ALOGD("pauseGeofence. id=%d", geofenceId);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssGeofence::resumeGeofence(int geofenceId, int monitorTransitions) {
+    ALOGD("resumeGeofence. id=%d, monitorTransitions=%d", geofenceId, monitorTransitions);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus GnssGeofence::removeGeofence(int geofenceId) {
+    ALOGD("removeGeofence. id=%d", geofenceId);
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/default/GnssGeofence.h b/gnss/aidl/default/GnssGeofence.h
new file mode 100644
index 0000000..313e832
--- /dev/null
+++ b/gnss/aidl/default/GnssGeofence.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/gnss/BnGnssGeofence.h>
+
+namespace aidl::android::hardware::gnss {
+
+struct GnssGeofence : public BnGnssGeofence {
+  public:
+    ndk::ScopedAStatus setCallback(const std::shared_ptr<IGnssGeofenceCallback>& callback) override;
+    ndk::ScopedAStatus addGeofence(int geofenceId, double latitudeDegrees, double longitudeDegrees,
+                                   double radiusMeters, int lastTransition, int monitorTransitions,
+                                   int notificationResponsivenessMs, int unknownTimerMs) override;
+    ndk::ScopedAStatus pauseGeofence(int geofenceId) override;
+    ndk::ScopedAStatus resumeGeofence(int geofenceId, int monitorTransitions) override;
+    ndk::ScopedAStatus removeGeofence(int geofenceId) override;
+
+  private:
+    // Guarded by mMutex
+    static std::shared_ptr<IGnssGeofenceCallback> sCallback;
+
+    // Synchronization lock for sCallback
+    mutable std::mutex mMutex;
+};
+
+}  // namespace aidl::android::hardware::gnss
diff --git a/gnss/aidl/vts/Android.bp b/gnss/aidl/vts/Android.bp
index 6096d4d..8582b62 100644
--- a/gnss/aidl/vts/Android.bp
+++ b/gnss/aidl/vts/Android.bp
@@ -32,6 +32,7 @@
         "gnss_hal_test_cases.cpp",
         "GnssBatchingCallback.cpp",
         "GnssCallbackAidl.cpp",
+        "GnssGeofenceCallback.cpp",
         "GnssMeasurementCallbackAidl.cpp",
         "GnssPowerIndicationCallback.cpp",
         "VtsHalGnssTargetTest.cpp",
diff --git a/gnss/aidl/vts/GnssGeofenceCallback.cpp b/gnss/aidl/vts/GnssGeofenceCallback.cpp
new file mode 100644
index 0000000..b841cb9
--- /dev/null
+++ b/gnss/aidl/vts/GnssGeofenceCallback.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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 "GnssGeofenceCallback.h"
+#include <log/log.h>
+
+using android::binder::Status;
+using android::hardware::gnss::GnssLocation;
+
+Status GnssGeofenceCallback::gnssGeofenceTransitionCb(int, const GnssLocation&, int, int64_t) {
+    // To implement
+    return Status::ok();
+}
+Status GnssGeofenceCallback::gnssGeofenceStatusCb(int, const GnssLocation&) {
+    // To implement
+    return Status::ok();
+}
+Status GnssGeofenceCallback::gnssGeofenceAddCb(int, int) {
+    // To implement
+    return Status::ok();
+}
+Status GnssGeofenceCallback::gnssGeofenceRemoveCb(int, int) {
+    // To implement
+    return Status::ok();
+}
+Status GnssGeofenceCallback::gnssGeofencePauseCb(int, int) {
+    // To implement
+    return Status::ok();
+}
+Status GnssGeofenceCallback::gnssGeofenceResumeCb(int, int) {
+    // To implement
+    return Status::ok();
+}
diff --git a/gnss/aidl/vts/GnssGeofenceCallback.h b/gnss/aidl/vts/GnssGeofenceCallback.h
new file mode 100644
index 0000000..b105518
--- /dev/null
+++ b/gnss/aidl/vts/GnssGeofenceCallback.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <android/hardware/gnss/BnGnssGeofenceCallback.h>
+#include <vector>
+#include "GnssCallbackEventQueue.h"
+
+/** Implementation for IGnssGeofenceCallback. */
+class GnssGeofenceCallback : public android::hardware::gnss::BnGnssGeofenceCallback {
+  public:
+    GnssGeofenceCallback() {}
+    ~GnssGeofenceCallback() {}
+
+    android::binder::Status gnssGeofenceTransitionCb(
+            int geofenceId, const android::hardware::gnss::GnssLocation& location, int transition,
+            int64_t timestampMillis) override;
+    android::binder::Status gnssGeofenceStatusCb(
+            int availability, const android::hardware::gnss::GnssLocation& lastLocation) override;
+    android::binder::Status gnssGeofenceAddCb(int geofenceId, int status) override;
+    android::binder::Status gnssGeofenceRemoveCb(int geofenceId, int status) override;
+    android::binder::Status gnssGeofencePauseCb(int geofenceId, int status) override;
+    android::binder::Status gnssGeofenceResumeCb(int geofenceId, int status) override;
+};
diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp
index 86140cc..09f9412 100644
--- a/gnss/aidl/vts/gnss_hal_test_cases.cpp
+++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp
@@ -23,6 +23,7 @@
 #include <android/hardware/gnss/IGnssPowerIndication.h>
 #include <android/hardware/gnss/IGnssPsds.h>
 #include "GnssBatchingCallback.h"
+#include "GnssGeofenceCallback.h"
 #include "GnssMeasurementCallbackAidl.h"
 #include "GnssPowerIndicationCallback.h"
 #include "gnss_hal_test.h"
@@ -38,6 +39,8 @@
 using android::hardware::gnss::IGnssBatching;
 using android::hardware::gnss::IGnssBatchingCallback;
 using android::hardware::gnss::IGnssConfiguration;
+using android::hardware::gnss::IGnssGeofence;
+using android::hardware::gnss::IGnssGeofenceCallback;
 using android::hardware::gnss::IGnssMeasurementCallback;
 using android::hardware::gnss::IGnssMeasurementInterface;
 using android::hardware::gnss::IGnssPowerIndication;
@@ -755,23 +758,25 @@
 }
 
 /*
- * TestGnssBatchingExtension:
- * 1. Gets the IGnssBatching extension.
- * 2. Initializes the interface with an IGnssBatchingCallback.
- * 3. Clean up.
+ * TestAllExtensions.
  */
-TEST_P(GnssHalTest, TestGnssBatchingExtension) {
+TEST_P(GnssHalTest, TestAllExtensions) {
     sp<IGnssBatching> iGnssBatching;
     auto status = aidl_gnss_hal_->getExtensionGnssBatching(&iGnssBatching);
-    if (!status.isOk() || iGnssBatching == nullptr) {
-        // Device doesn't support batching. Skip the test.
-        return;
+    if (status.isOk() && iGnssBatching != nullptr) {
+        auto gnssBatchingCallback = sp<GnssBatchingCallback>::make();
+        status = iGnssBatching->init(gnssBatchingCallback);
+        ASSERT_TRUE(status.isOk());
+
+        status = iGnssBatching->cleanup();
+        ASSERT_TRUE(status.isOk());
     }
 
-    sp<IGnssBatchingCallback> iGnssBatchingCallback;
-    status = iGnssBatching->init(iGnssBatchingCallback);
-    ASSERT_TRUE(status.isOk());
-
-    status = iGnssBatching->cleanup();
-    ASSERT_TRUE(status.isOk());
+    sp<IGnssGeofence> iGnssGeofence;
+    status = aidl_gnss_hal_->getExtensionGnssGeofence(&iGnssGeofence);
+    if (status.isOk() && iGnssGeofence != nullptr) {
+        auto gnssGeofenceCallback = sp<GnssGeofenceCallback>::make();
+        status = iGnssGeofence->setCallback(gnssGeofenceCallback);
+        ASSERT_TRUE(status.isOk());
+    }
 }
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
index 2eff11b..34506c8 100644
--- a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
@@ -138,4 +138,6 @@
   RANK = 101,
   BATCH_MATMUL = 102,
   PACK = 103,
+  MIRROR_PAD = 104,
+  REVERSE = 105,
 }
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
index 2ec91ac..aebe8d9 100644
--- a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -4318,6 +4318,8 @@
      * Supported tensor {@link OperandType}:
      * * {@link OperandType::TENSOR_FLOAT16}
      * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since NNAPI feature level 7)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since NNAPI feature level 7)
      *
      * Supported tensor rank: from 1.
      *
@@ -4326,6 +4328,9 @@
      *
      * Outputs:
      * * 0: The output tensor of same shape as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
      */
     RSQRT = 83,
 
@@ -5322,4 +5327,68 @@
      * * 0: The packed tensor.
      */
     PACK = 103,
+
+    /**
+     * Pads a tensor with mirrored values.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be padded.
+     * * 1: A 2-D tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. The shape of the
+     *      tensor must be {rank(input0), 2}.
+     *      padding[i, 0] specifies the number of elements to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of elements to be padded after the
+     *      end of dimension i.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the mode.
+     *      Options are 0:REFLECT and 1:SYMMETRIC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The
+     *      output tensor has the same rank as input0, and each
+     *      dimension of the output tensor has the same size as the
+     *      corresponding dimension of the input tensor plus the size
+     *      of the padding:
+     *          output0.dimension[i] =
+     *              padding[i, 0] + input0.dimension[i] + padding[i, 1]
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    MIRROR_PAD = 104,
+
+    /**
+     * Reverses a specified dimension of a tensor.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported tensor rank: up to 8.
+     *
+     * Inputs:
+     * * 0: Input tensor of rank n.
+     * * 1: Axis tensor of type {@link OperandType::TENSOR_INT32} and shape [1],
+     *      specifying which dimension of the input tensor is to be reversed. The dimension
+     *      must be in the range [0, n).
+     *
+     * Outputs:
+     * * 0: The reversed tensor.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensors,
+     *      the scales and zeroPoint must be the same as input0.
+     */
+    REVERSE = 105,
 }
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
index 1a4cd9a..e356104 100644
--- a/neuralnetworks/aidl/utils/Android.bp
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -56,13 +56,21 @@
 }
 
 cc_library_static {
-    name: "neuralnetworks_utils_hal_aidl",
+    name: "neuralnetworks_utils_hal_aidl_v2",
     defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
     shared_libs: [
         "android.hardware.neuralnetworks-V2-ndk",
     ],
 }
 
+cc_library_static {
+    name: "neuralnetworks_utils_hal_aidl",
+    defaults: ["neuralnetworks_utils_hal_aidl_defaults"],
+    shared_libs: [
+        "android.hardware.neuralnetworks-V3-ndk",
+    ],
+}
+
 // A cc_defaults that includes the latest non-experimental AIDL utilities and other AIDL libraries
 // that are commonly used together. Modules that always depend on the latest non-experimental
 // AIDL features can include this cc_defaults to avoid managing dependency versions explicitly.
@@ -71,7 +79,7 @@
     static_libs: [
         "android.hardware.common-V2-ndk",
         "android.hardware.graphics.common-V3-ndk",
-        "android.hardware.neuralnetworks-V2-ndk",
+        "android.hardware.neuralnetworks-V3-ndk",
         "neuralnetworks_utils_hal_aidl",
     ],
 }
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
index b4e747e..a27487e 100644
--- a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -36,6 +36,8 @@
             return nn::kVersionFeatureLevel5;
         case 2:
             return nn::kVersionFeatureLevel6;
+        case 3:
+            return nn::kVersionFeatureLevel7;
         default:
             return std::nullopt;
     }
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
index 4e9fc46..0366e7d 100644
--- a/neuralnetworks/aidl/utils/test/DeviceTest.cpp
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -159,6 +159,8 @@
             return "v1";
         case nn::Version::Level::FEATURE_LEVEL_6:
             return "v2";
+        case nn::Version::Level::FEATURE_LEVEL_7:
+            return "v3";
         default:
             LOG(FATAL) << "Invalid AIDL version: " << version;
             return "invalid";
@@ -893,7 +895,8 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(TestDevice, DeviceTest,
-                         ::testing::Values(nn::kVersionFeatureLevel5, nn::kVersionFeatureLevel6),
+                         ::testing::Values(nn::kVersionFeatureLevel5, nn::kVersionFeatureLevel6,
+                                           nn::kVersionFeatureLevel7),
                          printDeviceTest);
 
 }  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.cpp b/tv/tuner/aidl/vts/functional/FilterTests.cpp
index c53adb2..a5acdc1 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.cpp
+++ b/tv/tuner/aidl/vts/functional/FilterTests.cpp
@@ -23,6 +23,33 @@
 
 using ::aidl::android::hardware::common::NativeHandle;
 
+::ndk::ScopedAStatus FilterCallback::onFilterEvent(const vector<DemuxFilterEvent>& events) {
+    android::Mutex::Autolock autoLock(mMsgLock);
+    // Temprarily we treat the first coming back filter data on the matching pid a success
+    // once all of the MQ are cleared, means we got all the expected output
+    readFilterEventsData(events);
+    mPidFilterOutputCount++;
+    mMsgCondition.signal();
+
+    // HACK: we need to cast the const away as DemuxFilterEvent contains a ScopedFileDescriptor
+    // that cannot be copied.
+    for (auto&& e : const_cast<std::vector<DemuxFilterEvent>&>(events)) {
+        auto it = mFilterEventPromises.find(e.getTag());
+        if (it != mFilterEventPromises.cend()) {
+            it->second.set_value(std::move(e));
+            mFilterEventPromises.erase(it);
+        }
+    }
+
+    return ::ndk::ScopedAStatus::ok();
+}
+
+std::future<DemuxFilterEvent> FilterCallback::getNextFilterEventWithTag(DemuxFilterEvent::Tag tag) {
+    // Note: this currently only supports one future per DemuxFilterEvent::Tag.
+    mFilterEventPromises[tag] = std::promise<DemuxFilterEvent>();
+    return mFilterEventPromises[tag].get_future();
+}
+
 void FilterCallback::testFilterDataOutput() {
     android::Mutex::Autolock autoLock(mMsgLock);
     while (mPidFilterOutputCount < 1) {
diff --git a/tv/tuner/aidl/vts/functional/FilterTests.h b/tv/tuner/aidl/vts/functional/FilterTests.h
index c965d95..6258bac 100644
--- a/tv/tuner/aidl/vts/functional/FilterTests.h
+++ b/tv/tuner/aidl/vts/functional/FilterTests.h
@@ -24,7 +24,9 @@
 #include <log/log.h>
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
+#include <future>
 #include <map>
+#include <unordered_map>
 
 #include <fmq/AidlMessageQueue.h>
 
@@ -58,15 +60,9 @@
 
 class FilterCallback : public BnFilterCallback {
   public:
-    virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) override {
-        android::Mutex::Autolock autoLock(mMsgLock);
-        // Temprarily we treat the first coming back filter data on the matching pid a success
-        // once all of the MQ are cleared, means we got all the expected output
-        readFilterEventsData(events);
-        mPidFilterOutputCount++;
-        mMsgCondition.signal();
-        return ::ndk::ScopedAStatus::ok();
-    }
+    virtual ::ndk::ScopedAStatus onFilterEvent(const vector<DemuxFilterEvent>& events) override;
+
+    std::future<DemuxFilterEvent> getNextFilterEventWithTag(DemuxFilterEvent::Tag tag);
 
     virtual ::ndk::ScopedAStatus onFilterStatus(const DemuxFilterStatus /*status*/) override {
         return ::ndk::ScopedAStatus::ok();
@@ -89,6 +85,7 @@
     int32_t mFilterId;
     std::shared_ptr<IFilter> mFilter;
 
+    std::unordered_map<DemuxFilterEvent::Tag, std::promise<DemuxFilterEvent>> mFilterEventPromises;
     native_handle_t* mAvSharedHandle = nullptr;
     uint64_t mAvSharedMemSize = -1;
 
diff --git a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
index 85e5c68..88890e4 100644
--- a/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
+++ b/tv/tuner/aidl/vts/functional/VtsHalTvTunerTargetTest.cpp
@@ -640,6 +640,57 @@
     testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
 }
 
+// TODO: move boilerplate into text fixture
+TEST_P(TunerFilterAidlTest, FilterTimeDelayHintTest) {
+    description("Test filter delay hint.");
+
+    int32_t feId;
+    int32_t demuxId;
+    std::shared_ptr<IDemux> demux;
+    int64_t filterId;
+
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
+    ASSERT_TRUE(feId != INVALID_ID);
+    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    mFilterTests.setDemux(demux);
+
+    const auto& filterConf = filterMap[live.ipFilterId];
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
+    ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId_64bit(filterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
+    ASSERT_TRUE(mFilterTests.configIpFilterCid(filterConf.ipCid, filterId));
+
+    auto filter = mFilterTests.getFilterById(filterId);
+
+    auto delayValue = std::chrono::milliseconds(5000);
+    FilterDelayHint delayHint;
+    delayHint.hintType = FilterDelayHintType::TIME_DELAY_IN_MS;
+    delayHint.hintValue = delayValue.count();
+
+    auto status = filter->setDelayHint(delayHint);
+    ASSERT_TRUE(status.isOk());
+
+    auto startTime = std::chrono::steady_clock::now();
+    ASSERT_TRUE(mFilterTests.startFilter(filterId));
+
+    auto cb = mFilterTests.getFilterCallbacks().at(filterId);
+    auto future = cb->getNextFilterEventWithTag(DemuxFilterEvent::Tag::ipPayload);
+
+    // block and wait for callback to be received.
+    ASSERT_EQ(future.wait_for(std::chrono::seconds(10)), std::future_status::ready);
+    auto duration = std::chrono::steady_clock::now() - startTime;
+    ASSERT_GE(duration, delayValue);
+
+    // cleanup
+    ASSERT_TRUE(mFilterTests.stopFilter(filterId));
+    ASSERT_TRUE(mFilterTests.closeFilter(filterId));
+    ASSERT_TRUE(mDemuxTests.closeDemux());
+    ASSERT_TRUE(mFrontendTests.closeFrontend());
+}
+
 TEST_P(TunerPlaybackAidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
     description("Feed ts data from playback and configure Ts section filter to get output");
     if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) {