Merge "wificond.rc: Set CAP_NET_RAW and CAP_NET_ADMIN explicitly rather then implictly via group"
am: 7a83a7b6ae

Change-Id: Ief0640e87120c4aa3c7e5b1874f307a1baf43ef4
diff --git a/Android.mk b/Android.mk
index e293246..a5dc6e0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,6 +14,9 @@
 
 LOCAL_PATH := $(call my-dir)
 wificond_cpp_flags := -Wall -Werror -Wno-unused-parameter
+ifdef WIFI_OFFLOAD_SCANS
+wificond_cpp_flags += -DWIFI_OFFLOAD_SCANS=\"$(WIFI_OFFLOAD_SCANS)\"
+endif
 wificond_parent_dir := $(LOCAL_PATH)/../
 wificond_includes := \
     $(wificond_parent_dir)
@@ -30,12 +33,17 @@
 LOCAL_SRC_FILES := \
     main.cpp
 LOCAL_SHARED_LIBRARIES := \
+    android.hardware.wifi.offload@1.0 \
     libbinder \
     libbase \
     libcutils \
+    libhidlbase \
+    libhwbinder \
+    libhidltransport \
     libminijail \
     libutils \
-    libwifi-system
+    libwifi-system \
+    libwifi-system-iface
 LOCAL_STATIC_LIBRARIES := \
     libwificond
 include $(BUILD_EXECUTABLE)
@@ -61,14 +69,24 @@
     scanning/pno_network.cpp \
     scanning/pno_settings.cpp \
     scanning/scan_result.cpp \
+    scanning/offload/scan_stats.cpp \
     scanning/single_scan_settings.cpp \
     scanning/scan_utils.cpp \
     scanning/scanner_impl.cpp \
+    scanning/offload/offload_scan_manager.cpp \
+    scanning/offload/offload_callback.cpp \
+    scanning/offload/offload_service_utils.cpp \
+    scanning/offload/offload_scan_utils.cpp \
     server.cpp
 LOCAL_SHARED_LIBRARIES := \
+    android.hardware.wifi.offload@1.0 \
     libbase \
     libutils \
-    libwifi-system
+    libhidlbase \
+    libhwbinder \
+    libhidltransport \
+    libwifi-system \
+    libwifi-system-iface
 LOCAL_WHOLE_STATIC_LIBRARIES := \
     libwificond_ipc \
     libwificond_nl
@@ -152,29 +170,46 @@
     tests/client_interface_impl_unittest.cpp \
     tests/looper_backed_event_loop_unittest.cpp \
     tests/main.cpp \
+    tests/mock_client_interface_impl.cpp \
     tests/mock_netlink_manager.cpp \
     tests/mock_netlink_utils.cpp \
+    tests/mock_offload.cpp \
+    tests/mock_offload_callback_handlers.cpp \
+    tests/mock_offload_service_utils.cpp \
     tests/mock_scan_utils.cpp \
     tests/netlink_manager_unittest.cpp \
     tests/netlink_utils_unittest.cpp \
     tests/nl80211_attribute_unittest.cpp \
     tests/nl80211_packet_unittest.cpp \
+    tests/offload_callback_test.cpp \
+    tests/offload_hal_test_constants.cpp \
+    tests/offload_scan_manager_test.cpp \
+    tests/offload_scan_utils_test.cpp \
+    tests/offload_test_utils.cpp \
+    tests/scanner_unittest.cpp \
     tests/scan_result_unittest.cpp \
     tests/scan_settings_unittest.cpp \
+    tests/scan_stats_unittest.cpp \
     tests/scan_utils_unittest.cpp \
     tests/server_unittest.cpp
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
     libgtest \
     libwifi-system-test \
+    libwifi-system-iface-test \
     libwificond \
     libwificond_nl
 LOCAL_SHARED_LIBRARIES := \
+    android.hardware.wifi.offload@1.0 \
     libbase \
     libbinder \
+    libhidltransport \
+    libhidlbase \
+    libhwbinder \
     liblog \
     libutils \
-    libwifi-system
+    libwifi-system \
+    libwifi-system-iface
 include $(BUILD_NATIVE_TEST)
 
 ###
@@ -197,7 +232,8 @@
     libbinder \
     libcutils \
     libutils \
-    libwifi-system
+    libwifi-system \
+    libwifi-system-iface
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
     libwificond_ipc \
diff --git a/client_interface_impl.cpp b/client_interface_impl.cpp
index 405d1fd..974ff17 100644
--- a/client_interface_impl.cpp
+++ b/client_interface_impl.cpp
@@ -24,6 +24,7 @@
 #include "wificond/client_interface_binder.h"
 #include "wificond/net/mlme_event.h"
 #include "wificond/net/netlink_utils.h"
+#include "wificond/scanning/offload/offload_service_utils.h"
 #include "wificond/scanning/scan_result.h"
 #include "wificond/scanning/scan_utils.h"
 #include "wificond/scanning/scanner_impl.h"
@@ -116,6 +117,7 @@
       supplicant_manager_(supplicant_manager),
       netlink_utils_(netlink_utils),
       scan_utils_(scan_utils),
+      offload_service_utils_(new OffloadServiceUtils()),
       mlme_event_handler_(new MlmeEventHandlerImpl(this)),
       binder_(new ClientInterfaceBinder(this)),
       is_associated_(false) {
@@ -136,7 +138,8 @@
                              wiphy_features_,
                              this,
                              netlink_utils_,
-                             scan_utils_);
+                             scan_utils_,
+                             offload_service_utils_);
 }
 
 ClientInterfaceImpl::~ClientInterfaceImpl() {
@@ -161,6 +164,12 @@
       << static_cast<int>(scan_capabilities_.max_num_sched_scan_ssids) << endl;
   *ss << "Max number of match sets for scheduled scan: "
       << static_cast<int>(scan_capabilities_.max_match_sets) << endl;
+  *ss << "Maximum number of scan plans: "
+      << scan_capabilities_.max_num_scan_plans << endl;
+  *ss << "Max scan plan interval in seconds: "
+      << scan_capabilities_.max_scan_plan_interval << endl;
+  *ss << "Max scan plan iterations: "
+      << scan_capabilities_.max_scan_plan_iterations << endl;
   *ss << "Device supports random MAC for single shot scan: "
       << wiphy_features_.supports_random_mac_oneshot_scan << endl;
   *ss << "Device supports random MAC for scheduled scan: "
diff --git a/client_interface_impl.h b/client_interface_impl.h
index 41f6a09..6ce0547 100644
--- a/client_interface_impl.h
+++ b/client_interface_impl.h
@@ -27,6 +27,7 @@
 #include "android/net/wifi/IClientInterface.h"
 #include "wificond/net/mlme_event_handler.h"
 #include "wificond/net/netlink_utils.h"
+#include "wificond/scanning/offload/offload_service_utils.h"
 #include "wificond/scanning/scanner_impl.h"
 
 namespace android {
@@ -67,7 +68,7 @@
       android::wifi_system::SupplicantManager* supplicant_manager,
       NetlinkUtils* netlink_utils,
       ScanUtils* scan_utils);
-  ~ClientInterfaceImpl();
+  virtual ~ClientInterfaceImpl();
 
   // Get a pointer to the binder representing this ClientInterfaceImpl.
   android::sp<android::net::wifi::IClientInterface> GetBinder() const;
@@ -82,7 +83,7 @@
   bool requestANQP(
       const ::std::vector<uint8_t>& bssid,
       const ::android::sp<::android::net::wifi::IANQPDoneCallback>& callback);
-  bool IsAssociated() const;
+  virtual bool IsAssociated() const;
   void Dump(std::stringstream* ss) const;
 
  private:
@@ -96,6 +97,7 @@
   android::wifi_system::SupplicantManager* const supplicant_manager_;
   NetlinkUtils* const netlink_utils_;
   ScanUtils* const scan_utils_;
+  const std::shared_ptr<OffloadServiceUtils> offload_service_utils_;
   const std::unique_ptr<MlmeEventHandlerImpl> mlme_event_handler_;
   const android::sp<ClientInterfaceBinder> binder_;
   android::sp<ScannerImpl> scanner_;
diff --git a/main.cpp b/main.cpp
index 6e3f1d7..d2a9ba4 100644
--- a/main.cpp
+++ b/main.cpp
@@ -26,6 +26,8 @@
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
 #include <cutils/properties.h>
+#include <hwbinder/IPCThreadState.h>
+#include <hwbinder/ProcessState.h>
 #include <libminijail.h>
 #include <utils/String16.h>
 #include <wifi_system/interface_tool.h>
@@ -89,6 +91,16 @@
   return binder_fd;
 }
 
+// Setup our interface to the hw Binder driver or die trying.
+int SetupHwBinderOrCrash() {
+  int binder_fd = -1;
+  android::hardware::ProcessState::self()->setThreadPoolConfiguration(1, true);
+  int err = android::hardware::IPCThreadState::self()->setupPolling(&binder_fd);
+  CHECK_EQ(err, 0) << "Error setting up hw binder polling: " << strerror(-err);
+  CHECK_GE(binder_fd, 0) << "Invalid hw binder FD: " << binder_fd;
+  return binder_fd;
+}
+
 void RegisterServiceOrCrash(const android::sp<android::IBinder>& service) {
   android::sp<android::IServiceManager> sm = android::defaultServiceManager();
   CHECK_EQ(sm != NULL, true) << "Could not obtain IServiceManager";
@@ -103,6 +115,10 @@
   android::IPCThreadState::self()->handlePolledCommands();
 }
 
+void OnHwBinderReadReady(int fd) {
+  android::hardware::IPCThreadState::self()->handlePolledCommands();
+}
+
 int main(int argc, char** argv) {
   android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
   LOG(INFO) << "wificond is starting up...";
@@ -117,6 +133,11 @@
       android::wificond::EventLoop::kModeInput,
       &OnBinderReadReady)) << "Failed to watch binder FD";
 
+  int hw_binder_fd = SetupHwBinderOrCrash();
+  CHECK(event_dispatcher->WatchFileDescriptor(
+      hw_binder_fd, android::wificond::EventLoop::kModeInput,
+      &OnHwBinderReadReady)) << "Failed to watch Hw Binder FD";
+
   android::wificond::NetlinkManager netlink_manager(event_dispatcher.get());
   CHECK(netlink_manager.Start()) << "Failed to start netlink manager";
   android::wificond::NetlinkUtils netlink_utils(&netlink_manager);
diff --git a/net/netlink_utils.cpp b/net/netlink_utils.cpp
index 0fa0116..18246f7 100644
--- a/net/netlink_utils.cpp
+++ b/net/netlink_utils.cpp
@@ -246,6 +246,17 @@
     return false;
   }
 
+  // Use default value 0 for scan plan capabilities if attributes are missing.
+  uint32_t max_num_scan_plans = 0;
+  packet->GetAttributeValue(NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+                            &max_num_scan_plans);
+  uint32_t max_scan_plan_interval = 0;
+  packet->GetAttributeValue(NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+                            &max_scan_plan_interval);
+  uint32_t max_scan_plan_iterations = 0;
+  packet->GetAttributeValue(NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+                            &max_scan_plan_iterations);
+
   uint8_t max_match_sets;
   if (!packet->GetAttributeValue(NL80211_ATTR_MAX_MATCH_SETS,
                                    &max_match_sets)) {
@@ -255,7 +266,10 @@
   }
   *out_scan_capabilities = ScanCapabilities(max_num_scan_ssids,
                                             max_num_sched_scan_ssids,
-                                            max_match_sets);
+                                            max_match_sets,
+                                            max_num_scan_plans,
+                                            max_scan_plan_interval,
+                                            max_scan_plan_iterations);
   return true;
 }
 
diff --git a/net/netlink_utils.h b/net/netlink_utils.h
index f8b9c0e..f1e43b5 100644
--- a/net/netlink_utils.h
+++ b/net/netlink_utils.h
@@ -65,16 +65,30 @@
   ScanCapabilities() = default;
   ScanCapabilities(uint8_t max_num_scan_ssids_,
                    uint8_t max_num_sched_scan_ssids_,
-                   uint8_t max_match_sets_)
+                   uint8_t max_match_sets_,
+                   uint32_t max_num_scan_plans_,
+                   uint32_t max_scan_plan_interval_,
+                   uint32_t max_scan_plan_iterations_)
       : max_num_scan_ssids(max_num_scan_ssids_),
         max_num_sched_scan_ssids(max_num_sched_scan_ssids_),
-        max_match_sets(max_match_sets_) {}
+        max_match_sets(max_match_sets_),
+        max_num_scan_plans(max_num_scan_plans_),
+        max_scan_plan_interval(max_scan_plan_interval_),
+        max_scan_plan_iterations(max_scan_plan_iterations_) {}
   // Number of SSIDs you can scan with a single scan request.
   uint8_t max_num_scan_ssids;
   // Number of SSIDs you can scan with a single scheduled scan request.
   uint8_t max_num_sched_scan_ssids;
   // Maximum number of sets that can be used with NL80211_ATTR_SCHED_SCAN_MATCH.
   uint8_t max_match_sets;
+  // Maximum number of scan plans that can be specified.
+  uint32_t max_num_scan_plans;
+  // Maximum interval in seconds for a particular scan plan that can be
+  // specified.
+  uint32_t max_scan_plan_interval;
+  // Maximum number of iterations for a particular scan plan that can be
+  // specified.
+  uint32_t max_scan_plan_iterations;
 };
 
 struct WiphyFeatures {
diff --git a/scanning/offload/hidl_call_util.h b/scanning/offload/hidl_call_util.h
new file mode 100644
index 0000000..6303f1a
--- /dev/null
+++ b/scanning/offload/hidl_call_util.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+// This file is modified from
+// hardware/interfaces/wifi/1.0/vts/functional/wifi_hidl_call_util.h
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace {
+namespace detail {
+template <typename>
+struct functionArgSaver;
+
+// Provides a std::function that takes one argument, and a buffer
+// wherein the function will store its argument. The buffer has
+// the same type as the argument, but with const and reference
+// modifiers removed.
+template <typename ArgT>
+struct functionArgSaver<std::function<void(ArgT)>> final {
+  using StorageT = typename std::remove_const<
+      typename std::remove_reference<ArgT>::type>::type;
+
+  std::function<void(ArgT)> saveArgs = [this](ArgT arg) {
+    this->saved_values = arg;
+  };
+
+  StorageT saved_values;
+};
+
+// Provides a std::function that takes two arguments, and a buffer
+// wherein the function will store its arguments. The buffer is a
+// std::pair, whose elements have the same types as the arguments
+// (but with const and reference modifiers removed).
+template <typename Arg1T, typename Arg2T>
+struct functionArgSaver<std::function<void(Arg1T, Arg2T)>> final {
+  using StorageT =
+      std::pair<typename std::remove_const<
+                    typename std::remove_reference<Arg1T>::type>::type,
+                typename std::remove_const<
+                    typename std::remove_reference<Arg2T>::type>::type>;
+
+  std::function<void(Arg1T, Arg2T)> saveArgs = [this](Arg1T arg1, Arg2T arg2) {
+    this->saved_values = {arg1, arg2};
+  };
+
+  StorageT saved_values;
+};
+
+// Provides a std::function that takes three or more arguments, and a
+// buffer wherein the function will store its arguments. The buffer is a
+// std::tuple whose elements have the same types as the arguments (but
+// with const and reference modifiers removed).
+template <typename... ArgT>
+struct functionArgSaver<std::function<void(ArgT...)>> final {
+  using StorageT = std::tuple<typename std::remove_const<
+      typename std::remove_reference<ArgT>::type>::type...>;
+
+  std::function<void(ArgT...)> saveArgs = [this](ArgT... arg) {
+    this->saved_values = {arg...};
+  };
+
+  StorageT saved_values;
+};
+
+// Invokes |method| on |object|, providing |method| a CallbackT as the
+// final argument. Returns a copy of the parameters that |method| provided
+// to CallbackT. (The parameters are returned by value.)
+template <typename CallbackT, typename MethodT, typename ObjectT,
+          typename... ArgT>
+std::pair<typename functionArgSaver<CallbackT>::StorageT, bool> invokeMethod(
+    MethodT method, ObjectT object, ArgT&&... methodArg) {
+  functionArgSaver<CallbackT> result_buffer;
+  const auto& res = ((*object).*method)(std::forward<ArgT>(methodArg)...,
+                                        result_buffer.saveArgs);
+  bool transportStatus = true;
+  if (!res.isOk()) {
+    LOG(ERROR) << " Transport failed " << res.description();
+    transportStatus = false;
+  }
+  return std::make_pair(result_buffer.saved_values, transportStatus);
+}
+}  // namespace detail
+}  // namespace
+
+// Invokes |method| on |strong_pointer|, passing provided arguments through to
+// |method|.
+//
+// Returns either:
+// - A copy of the result callback parameter (for callbacks with a single
+//   parameter), OR
+// - A pair containing a copy of the result callback parameters (for callbacks
+//   with two parameters), OR
+// - A tuple containing a copy of the result callback paramters (for callbacks
+//   with three or more parameters).
+//
+// Example usage:
+//   EXPECT_EQ(WifiStatusCode::SUCCESS,
+//       HIDL_INVOKE(strong_pointer, methodReturningWifiStatus).code);
+//   EXPECT_EQ(WifiStatusCode::SUCCESS,
+//       HIDL_INVOKE(strong_pointer, methodReturningWifiStatusAndOneMore)
+//         .first.code);
+//   EXPECT_EQ(WifiStatusCode::SUCCESS, std::get<0>(
+//       HIDL_INVOKE(strong_pointer, methodReturningWifiStatusAndTwoMore))
+//         .code);
+#define HIDL_INVOKE(strong_pointer, method, ...)                            \
+  (detail::invokeMethod<                                                    \
+      std::remove_reference<decltype(*strong_pointer)>::type::method##_cb>( \
+      &std::remove_reference<decltype(*strong_pointer)>::type::method,      \
+      strong_pointer, ##__VA_ARGS__))
diff --git a/scanning/offload/offload_callback.cpp b/scanning/offload/offload_callback.cpp
new file mode 100644
index 0000000..b92a1ed
--- /dev/null
+++ b/scanning/offload/offload_callback.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <memory>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "wificond/scanning/offload/offload_callback.h"
+#include "wificond/scanning/offload/offload_scan_manager.h"
+#include "wificond/scanning/scan_result.h"
+
+using ::android::hardware::wifi::offload::V1_0::ScanResult;
+using ::android::hardware::wifi::offload::V1_0::OffloadStatus;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+namespace android {
+namespace wificond {
+
+OffloadCallback::OffloadCallback(OffloadCallbackHandlers* handlers)
+    : handlers_(handlers) {}
+
+// Methods from ::android::hardware::wifi::offload::V1_0::IOffloadCallback
+// follow.
+Return<void> OffloadCallback::onScanResult(
+    const hidl_vec<ScanResult>& scan_result) {
+  if (handlers_ != nullptr) {
+    handlers_->OnScanResultHandler(std::vector<ScanResult>(scan_result));
+  } else {
+    LOG(WARNING) << "No handler available for Offload scan results";
+  }
+  return Void();
+}
+
+Return<void> OffloadCallback::onError(const OffloadStatus& status) {
+  if (handlers_ != nullptr) {
+    handlers_->OnErrorHandler(status);
+  } else {
+    LOG(WARNING) << "No error handler for Offload";
+  }
+  return Void();
+}
+
+OffloadCallback::~OffloadCallback() {}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/scanning/offload/offload_callback.h b/scanning/offload/offload_callback.h
new file mode 100644
index 0000000..ae0b2f4
--- /dev/null
+++ b/scanning/offload/offload_callback.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_HARDWARE_WIFI_OFFLOAD_V1_0_OFFLOADCALLBACK_H
+#define ANDROID_HARDWARE_WIFI_OFFLOAD_V1_0_OFFLOADCALLBACK_H
+
+#include <android/hardware/wifi/offload/1.0/IOffloadCallback.h>
+#include <hidl/Status.h>
+#include <vector>
+#include "wificond/scanning/offload/offload_callback_handlers.h"
+
+namespace android {
+namespace wificond {
+
+class OffloadCallback
+    : public ::android::hardware::wifi::offload::V1_0::IOffloadCallback {
+ public:
+  explicit OffloadCallback(OffloadCallbackHandlers* handlers);
+  virtual ~OffloadCallback();
+
+  // Methods from ::android::hardware::wifi::offload::V1_0::IOffloadCallback
+  // follow.
+  ::android::hardware::Return<void> onScanResult(
+      const ::android::hardware::hidl_vec<
+          ::android::hardware::wifi::offload::V1_0::ScanResult>& scanResult)
+      override;
+  ::android::hardware::Return<void> onError(
+      const ::android::hardware::wifi::offload::V1_0::OffloadStatus& status)
+      override;
+  // Methods from ::android::hidl::base::V1_0::IBase follow.
+
+ private:
+  OffloadCallbackHandlers* handlers_;
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_WIFI_OFFLOAD_V1_0_OFFLOADCALLBACK_H
diff --git a/scanning/offload/offload_callback_handlers.h b/scanning/offload/offload_callback_handlers.h
new file mode 100644
index 0000000..a202cda
--- /dev/null
+++ b/scanning/offload/offload_callback_handlers.h
@@ -0,0 +1,40 @@
+/*
+ * 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 ANDROID_HARDWARE_WIFI_OFFLOAD_V1_0_OFFLOADCALLBACK_HANDLERS_H
+#define ANDROID_HARDWARE_WIFI_OFFLOAD_V1_0_OFFLOADCALLBACK_HANDLERS_H
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include <vector>
+
+namespace android {
+namespace wificond {
+
+class OffloadCallbackHandlers {
+ public:
+  virtual ~OffloadCallbackHandlers() {}
+
+  virtual void OnScanResultHandler(
+      const std::vector<::android::hardware::wifi::offload::V1_0::ScanResult>&
+          scanResult) = 0;
+  virtual void OnErrorHandler(
+      const ::android::hardware::wifi::offload::V1_0::OffloadStatus&
+          status) = 0;
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_WIFI_OFFLOAD_V1_0_OFFLOADCALLBACK_HANDLERS_H
diff --git a/scanning/offload/offload_scan_manager.cpp b/scanning/offload/offload_scan_manager.cpp
new file mode 100644
index 0000000..5e3d90d
--- /dev/null
+++ b/scanning/offload/offload_scan_manager.cpp
@@ -0,0 +1,275 @@
+/*
+ * 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 "wificond/scanning/offload/offload_scan_manager.h"
+
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "wificond/scanning/offload/hidl_call_util.h"
+#include "wificond/scanning/offload/offload_scan_utils.h"
+#include "wificond/scanning/offload/offload_service_utils.h"
+#include "wificond/scanning/offload/scan_stats.h"
+#include "wificond/scanning/scan_result.h"
+
+using ::android::hardware::hidl_vec;
+using android::hardware::wifi::offload::V1_0::IOffload;
+using android::hardware::wifi::offload::V1_0::ScanResult;
+using android::hardware::wifi::offload::V1_0::ScanFilter;
+using android::hardware::wifi::offload::V1_0::ScanParam;
+using android::hardware::wifi::offload::V1_0::ScanStats;
+using android::hardware::wifi::offload::V1_0::OffloadStatus;
+using android::hardware::wifi::offload::V1_0::OffloadStatusCode;
+
+using android::wificond::OffloadCallback;
+using android::wificond::OnNativeScanResultsReadyHandler;
+using ::com::android::server::wifi::wificond::NativeScanResult;
+using ::com::android::server::wifi::wificond::NativeScanStats;
+using std::vector;
+using std::weak_ptr;
+
+using namespace std::placeholders;
+
+namespace {
+const uint32_t kSubscriptionDelayMs = 5000;
+}
+
+namespace android {
+namespace wificond {
+
+OffloadCallbackHandlersImpl::OffloadCallbackHandlersImpl(
+    OffloadScanManager* offload_scan_manager)
+    : offload_scan_manager_(offload_scan_manager) {}
+
+OffloadCallbackHandlersImpl::~OffloadCallbackHandlersImpl() {}
+
+void OffloadCallbackHandlersImpl::OnScanResultHandler(
+    const vector<ScanResult>& scanResult) {
+  if (offload_scan_manager_ != nullptr) {
+    offload_scan_manager_->ReportScanResults(scanResult);
+  }
+}
+
+void OffloadCallbackHandlersImpl::OnErrorHandler(const OffloadStatus& status) {
+  if (offload_scan_manager_ != nullptr) {
+    offload_scan_manager_->ReportError(status);
+  }
+}
+
+OffloadScanManager::OffloadScanManager(weak_ptr<OffloadServiceUtils> utils,
+                                       OnNativeScanResultsReadyHandler handler)
+    : wifi_offload_hal_(nullptr),
+      wifi_offload_callback_(nullptr),
+      offload_status_(OffloadScanManager::kError),
+      subscription_enabled_(false),
+      offload_callback_handlers_(new OffloadCallbackHandlersImpl(this)),
+      scan_result_handler_(handler) {
+  auto offload_scan_utils = utils.lock();
+  if (scan_result_handler_ == nullptr) {
+    LOG(ERROR) << "Invalid Offload scan result handler";
+    return;
+  }
+  wifi_offload_hal_ = offload_scan_utils->GetOffloadService();
+  if (wifi_offload_hal_ == nullptr) {
+    LOG(ERROR) << "No Offload Service available";
+    return;
+  }
+
+  death_recipient_ = offload_scan_utils->GetOffloadDeathRecipient(
+      std::bind(&OffloadScanManager::OnObjectDeath, this, _1));
+  uint64_t cookie = reinterpret_cast<uint64_t>(wifi_offload_hal_.get());
+  auto link_to_death_status =
+      wifi_offload_hal_->linkToDeath(death_recipient_, cookie);
+  if (!link_to_death_status.isOk()) {
+    LOG(ERROR) << "Unable to register death handler "
+               << link_to_death_status.description();
+    return;
+  }
+
+  wifi_offload_callback_ =
+      offload_scan_utils->GetOffloadCallback(offload_callback_handlers_.get());
+  if (wifi_offload_callback_ == nullptr) {
+    LOG(ERROR) << "Invalid Offload callback object";
+    return;
+  }
+  wifi_offload_hal_->setEventCallback(wifi_offload_callback_);
+  offload_status_ = OffloadScanManager::kNoError;
+}
+
+bool OffloadScanManager::stopScan(OffloadScanManager::ReasonCode* reason_code) {
+  if (!subscription_enabled_) {
+    LOG(VERBOSE) << "Scans are not subscribed over Offload HAL";
+    *reason_code = OffloadScanManager::kNotSubscribed;
+    return false;
+  }
+  if (wifi_offload_hal_ != nullptr) {
+    wifi_offload_hal_->unsubscribeScanResults();
+    subscription_enabled_ = false;
+  }
+  *reason_code = OffloadScanManager::kNone;
+  return true;
+}
+
+bool OffloadScanManager::GetScanStats(NativeScanStats* native_scan_stats) {
+  const auto& result = HIDL_INVOKE(wifi_offload_hal_, getScanStats);
+  const auto& offload_status_and_scan_stats = result.first;
+  bool transport_status = result.second;
+  if (!transport_status) {
+    return false;
+  }
+  OffloadStatus offload_status = offload_status_and_scan_stats.first;
+  ScanStats scan_stats = offload_status_and_scan_stats.second;
+  if (offload_status.code != OffloadStatusCode::OK) {
+    LOG(WARNING) << offload_status.description;
+    return false;
+  }
+  *native_scan_stats = OffloadScanUtils::convertToNativeScanStats(scan_stats);
+  return true;
+}
+
+bool OffloadScanManager::VerifyAndConvertHIDLStatus(
+    std::pair<OffloadStatus, bool> result,
+    OffloadScanManager::ReasonCode* reason_code) {
+  const auto& offload_status = result.first;
+  bool transport_status = result.second;
+  if (!transport_status) {
+    *reason_code = OffloadScanManager::kTransactionFailed;
+    return false;
+  }
+  if (offload_status.code != OffloadStatusCode::OK) {
+    LOG(WARNING) << offload_status.description;
+    *reason_code = OffloadScanManager::kOperationFailed;
+    return false;
+  }
+  return true;
+}
+
+bool OffloadScanManager::startScan(
+    uint32_t interval_ms, int32_t rssi_threshold,
+    const vector<vector<uint8_t>>& scan_ssids,
+    const vector<vector<uint8_t>>& match_ssids,
+    const vector<uint8_t>& match_security, const vector<uint32_t>& freqs,
+    OffloadScanManager::ReasonCode* reason_code) {
+  if (getOffloadStatus() != OffloadScanManager::kNoError) {
+    *reason_code = OffloadScanManager::kNotAvailable;
+    LOG(WARNING) << "Offload HAL scans are not available";
+    return false;
+  }
+  ScanParam param =
+      OffloadScanUtils::createScanParam(scan_ssids, freqs, interval_ms);
+  ScanFilter filter = OffloadScanUtils::createScanFilter(
+      match_ssids, match_security, rssi_threshold);
+
+  if (!ConfigureScans(param, filter, reason_code)) {
+    return false;
+  }
+
+  if (!subscription_enabled_ && !SubscribeScanResults(reason_code)) {
+    return false;
+  }
+
+  subscription_enabled_ = true;
+  *reason_code = OffloadScanManager::kNone;
+  return true;
+}
+
+bool OffloadScanManager::ConfigureScans(
+    ScanParam param, ScanFilter filter,
+    OffloadScanManager::ReasonCode* reason_code) {
+  const auto& result =
+      HIDL_INVOKE(wifi_offload_hal_, configureScans, param, filter);
+  if (!VerifyAndConvertHIDLStatus(result, reason_code)) {
+    return false;
+  }
+  return true;
+}
+
+bool OffloadScanManager::SubscribeScanResults(
+    OffloadScanManager::ReasonCode* reason_code) {
+  const auto& result = HIDL_INVOKE(wifi_offload_hal_, subscribeScanResults,
+                                   kSubscriptionDelayMs);
+  if (!VerifyAndConvertHIDLStatus(result, reason_code)) {
+    return false;
+  }
+  return true;
+}
+
+OffloadScanManager::StatusCode OffloadScanManager::getOffloadStatus() const {
+  if (wifi_offload_hal_ == nullptr) {
+    return OffloadScanManager::kNoService;
+  }
+  return offload_status_;
+}
+
+bool OffloadScanManager::getScanStats(NativeScanStats* native_scan_stats) {
+  if (getOffloadStatus() != OffloadScanManager::kNoError) {
+    LOG(WARNING) << "Unable to get scan stats due to Wifi Offload HAL error";
+    return false;
+  }
+  return GetScanStats(native_scan_stats);
+}
+
+OffloadScanManager::~OffloadScanManager() {
+  if (wifi_offload_hal_ != nullptr) {
+    wifi_offload_hal_->unlinkToDeath(death_recipient_);
+  }
+}
+
+void OffloadScanManager::ReportScanResults(
+    const vector<ScanResult>& scanResult) {
+  if (scan_result_handler_ != nullptr) {
+    scan_result_handler_(
+        OffloadScanUtils::convertToNativeScanResults(scanResult));
+  } else {
+    LOG(ERROR) << "No scan result handler for Offload ScanManager";
+  }
+}
+
+void OffloadScanManager::ReportError(const OffloadStatus& status) {
+  OffloadStatusCode status_code = status.code;
+  OffloadScanManager::StatusCode status_result = OffloadScanManager::kNoError;
+  switch (status_code) {
+    case OffloadStatusCode::OK:
+      status_result = OffloadScanManager::kNoError;
+      break;
+    case OffloadStatusCode::TIMEOUT:
+      status_result = OffloadScanManager::kTimeOut;
+      break;
+    case OffloadStatusCode::NO_CONNECTION:
+      status_result = OffloadScanManager::kNotConnected;
+      break;
+    case OffloadStatusCode::ERROR:
+      status_result = OffloadScanManager::kError;
+      break;
+    default:
+      LOG(WARNING) << "Invalid Offload Error reported";
+      return;
+  }
+  if (status_result != OffloadScanManager::kNoError) {
+    LOG(WARNING) << "Offload Error reported " << status.description;
+  }
+  offload_status_ = status_result;
+}
+
+void OffloadScanManager::OnObjectDeath(uint64_t cookie) {
+  if (wifi_offload_hal_ == reinterpret_cast<IOffload*>(cookie)) {
+    wifi_offload_hal_.clear();
+    LOG(ERROR) << "Death Notification for Wifi Offload HAL";
+  }
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/scanning/offload/offload_scan_manager.h b/scanning/offload/offload_scan_manager.h
new file mode 100644
index 0000000..e2c2b0c
--- /dev/null
+++ b/scanning/offload/offload_scan_manager.h
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ */
+#ifndef WIFICOND_OFFLOAD_SCAN_MANAGER_H_
+#define WIFICOND_OFFLOAD_SCAN_MANAGER_H_
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include "wificond/scanning/offload/offload_callback.h"
+#include "wificond/scanning/offload/offload_callback_handlers.h"
+#include "wificond/scanning/offload/offload_service_utils.h"
+
+#include <vector>
+
+namespace com {
+namespace android {
+namespace server {
+namespace wifi {
+namespace wificond {
+
+class NativeScanResult;
+class NativeScanStats;
+
+}  // namespace wificond
+}  // namespace wifi
+}  // namespace server
+}  // namespace android
+}  // namespace com
+
+namespace android {
+namespace wificond {
+
+class OffloadScanManager;
+
+typedef std::function<void(
+    const std::vector<::com::android::server::wifi::wificond::NativeScanResult>
+        scanResult)>
+    OnNativeScanResultsReadyHandler;
+
+// Provides callback interface implementation from Offload HAL
+class OffloadCallbackHandlersImpl : public OffloadCallbackHandlers {
+ public:
+  OffloadCallbackHandlersImpl(OffloadScanManager* parent);
+  ~OffloadCallbackHandlersImpl() override;
+
+  void OnScanResultHandler(
+      const std::vector<android::hardware::wifi::offload::V1_0::ScanResult>&
+          scanResult) override;
+  void OnErrorHandler(
+      const android::hardware::wifi::offload::V1_0::OffloadStatus& status)
+      override;
+
+ private:
+  OffloadScanManager* offload_scan_manager_;
+};
+
+// Provides methods to interact with Offload HAL
+class OffloadScanManager {
+ public:
+  enum StatusCode {
+    /* Corresponds to OffloadStatusCode::OK */
+    kNoError,
+    /* Offload HAL service not avaialble */
+    kNoService,
+    /* Corresponds to OffloadStatusCode::NO_CONNECTION */
+    kNotConnected,
+    /* Corresponds to OffloadStatusCode::TIMEOUT */
+    kTimeOut,
+    /* Corresponds to OffloadStatusCode::ERROR */
+    kError
+  };
+
+  enum ReasonCode {
+    /* Default value */
+    kNone,
+    /* Offload HAL scans is not available */
+    kNotAvailable,
+    /* Offload HAL service is not subscribed to */
+    kNotSubscribed,
+    /* Offload HAL requested operation failure */
+    kOperationFailed,
+    /* Binder failed to deliver message to Offload HAL*/
+    kTransactionFailed,
+  };
+
+  explicit OffloadScanManager(std::weak_ptr<OffloadServiceUtils> utils,
+                              OnNativeScanResultsReadyHandler handler);
+  virtual ~OffloadScanManager();
+  /* Request start of offload scans with scan parameters and scan filter
+   * settings. Internally calls Offload HAL service with configureScans()
+   * and subscribeScanResults() APIs. If already subscribed, it updates
+   * the scan configuration only. Reason code is updated in failure case
+   */
+  bool startScan(uint32_t /* interval_ms */, int32_t /* rssi_threshold */,
+                 const std::vector<std::vector<uint8_t>>& /* scan_ssids */,
+                 const std::vector<std::vector<uint8_t>>& /* match_ssids */,
+                 const std::vector<uint8_t>& /* match_security */,
+                 const std::vector<uint32_t>& /* freqs */,
+                 ReasonCode* /* failure reason */);
+  /* Request stop of offload scans, returns true if scans were subscribed
+   * to from the Offload HAL service. Otherwise, returns false. Reason code
+   * is updated in case of failure.
+   */
+  bool stopScan(ReasonCode* /* failure reason */);
+  /* Get statistics for scans performed by Offload HAL */
+  bool getScanStats(
+      ::com::android::server::wifi::wificond::NativeScanStats* /* scanStats */);
+  /* Otain status of the Offload HAL service */
+  StatusCode getOffloadStatus() const;
+
+ private:
+  void ReportScanResults(
+      const std::vector<android::hardware::wifi::offload::V1_0::ScanResult>&
+          scanResult);
+  void ReportError(
+      const android::hardware::wifi::offload::V1_0::OffloadStatus& status);
+  bool VerifyAndConvertHIDLStatus(
+      std::pair<android::hardware::wifi::offload::V1_0::OffloadStatus, bool>
+          result,
+      OffloadScanManager::ReasonCode* reason_code);
+  bool GetScanStats(
+      ::com::android::server::wifi::wificond::NativeScanStats* stats);
+  bool SubscribeScanResults(OffloadScanManager::ReasonCode* reason_code);
+  bool ConfigureScans(android::hardware::wifi::offload::V1_0::ScanParam,
+                      android::hardware::wifi::offload::V1_0::ScanFilter,
+                      OffloadScanManager::ReasonCode* reason_code);
+  /* Handle binder death */
+  void OnObjectDeath(uint64_t /* cookie */);
+
+  android::sp<android::hardware::wifi::offload::V1_0::IOffload>
+      wifi_offload_hal_;
+  android::sp<OffloadCallback> wifi_offload_callback_;
+  android::sp<OffloadDeathRecipient> death_recipient_;
+  StatusCode offload_status_;
+  bool subscription_enabled_;
+
+  const std::unique_ptr<OffloadCallbackHandlersImpl> offload_callback_handlers_;
+  OnNativeScanResultsReadyHandler scan_result_handler_;
+
+  friend class OffloadCallbackHandlersImpl;
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_OFFLOAD_SCAN_MANAGER_H_
diff --git a/scanning/offload/offload_scan_utils.cpp b/scanning/offload/offload_scan_utils.cpp
new file mode 100644
index 0000000..9127632
--- /dev/null
+++ b/scanning/offload/offload_scan_utils.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "wificond/scanning/offload/offload_scan_utils.h"
+
+#include <android-base/logging.h>
+
+#include "wificond/scanning/offload/scan_stats.h"
+#include "wificond/scanning/scan_result.h"
+
+using ::com::android::server::wifi::wificond::NativeScanResult;
+using ::com::android::server::wifi::wificond::NativeScanStats;
+using android::hardware::wifi::offload::V1_0::ScanResult;
+using android::hardware::wifi::offload::V1_0::ScanParam;
+using android::hardware::wifi::offload::V1_0::ScanFilter;
+using android::hardware::wifi::offload::V1_0::ScanStats;
+using android::hardware::wifi::offload::V1_0::NetworkInfo;
+using android::hardware::hidl_vec;
+using std::vector;
+
+namespace android {
+namespace wificond {
+
+vector<NativeScanResult> OffloadScanUtils::convertToNativeScanResults(
+    const vector<ScanResult>& scan_result) {
+  vector<NativeScanResult> native_scan_result;
+  native_scan_result.reserve(scan_result.size());
+  for (size_t i = 0; i < scan_result.size(); i++) {
+    NativeScanResult single_scan_result;
+    single_scan_result.ssid = scan_result[i].networkInfo.ssid;
+    single_scan_result.bssid.assign(scan_result[i].networkInfo.ssid.begin(),
+                                    scan_result[i].networkInfo.ssid.end());
+    single_scan_result.frequency = scan_result[i].frequency;
+    single_scan_result.signal_mbm = scan_result[i].rssi;
+    single_scan_result.tsf = scan_result[i].tsf;
+    single_scan_result.capability = scan_result[i].capability;
+    single_scan_result.associated = false;
+    native_scan_result.emplace_back(single_scan_result);
+  }
+  return native_scan_result;
+}
+
+ScanParam OffloadScanUtils::createScanParam(
+    const vector<vector<uint8_t>>& ssid_list,
+    const vector<uint32_t>& frequency_list, uint32_t scan_interval_ms) {
+  ScanParam scan_param;
+  scan_param.disconnectedModeScanIntervalMs = scan_interval_ms;
+  scan_param.frequencyList = frequency_list;
+  vector<hidl_vec<uint8_t>> ssid_list_tmp;
+  for (const auto& ssid : ssid_list) {
+    ssid_list_tmp.push_back(ssid);
+  }
+  scan_param.ssidList = ssid_list_tmp;
+  return scan_param;
+}
+
+ScanFilter OffloadScanUtils::createScanFilter(
+    const vector<vector<uint8_t>>& ssids, const vector<uint8_t>& flags,
+    int8_t rssi_threshold) {
+  ScanFilter scan_filter;
+  vector<NetworkInfo> nw_info_list;
+  size_t i = 0;
+  scan_filter.rssiThreshold = rssi_threshold;
+  // Note that the number of ssids should match the number of security flags
+  for (const auto& ssid : ssids) {
+    NetworkInfo nw_info;
+    nw_info.ssid = ssid;
+    if (i < flags.size()) {
+      nw_info.flags = flags[i++];
+    } else {
+      continue;
+    }
+    nw_info_list.push_back(nw_info);
+  }
+  scan_filter.preferredNetworkInfoList = nw_info_list;
+  return scan_filter;
+}
+
+NativeScanStats OffloadScanUtils::convertToNativeScanStats(
+    const ScanStats& scanStats) {
+  uint32_t num_channels_scanned = 0;
+  uint32_t scan_duration_ms = 0;
+  vector<uint8_t> histogram_channels;
+
+  for (size_t i = 0; i < scanStats.scanRecord.size(); i++) {
+    scan_duration_ms += scanStats.scanRecord[i].durationMs;
+    num_channels_scanned += scanStats.scanRecord[i].numChannelsScanned;
+  }
+  for (size_t i = 0; i < scanStats.histogramChannelsScanned.size(); i++) {
+    histogram_channels.push_back(scanStats.histogramChannelsScanned[i]);
+  }
+
+  NativeScanStats native_scan_stats(
+      scanStats.numScansRequestedByWifi, scanStats.numScansServicedByWifi,
+      scanStats.subscriptionDurationMs, scan_duration_ms, num_channels_scanned,
+      histogram_channels);
+  return native_scan_stats;
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/scanning/offload/offload_scan_utils.h b/scanning/offload/offload_scan_utils.h
new file mode 100644
index 0000000..ce4afa7
--- /dev/null
+++ b/scanning/offload/offload_scan_utils.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+#ifndef WIFICOND_OFFLOAD_SCAN_UTILS_H_
+#define WIFICOND_OFFLOAD_SCAN_UTILS_H_
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include "wificond/scanning/offload/offload_callback.h"
+
+#include <vector>
+
+namespace com {
+namespace android {
+namespace server {
+namespace wifi {
+namespace wificond {
+
+class NativeScanResult;
+class NativeScanStats;
+
+}  // namespace wificond
+}  // namespace wifi
+}  // namespace server
+}  // namespace android
+}  // namespace com
+
+namespace android {
+namespace wificond {
+
+// Provides utility methods for Offload Scan Manager
+class OffloadScanUtils {
+ public:
+  static std::vector<::com::android::server::wifi::wificond::NativeScanResult>
+      convertToNativeScanResults(
+          const std::vector<android::hardware::wifi::offload::V1_0::ScanResult>&);
+  static android::hardware::wifi::offload::V1_0::ScanParam createScanParam(
+      const std::vector<std::vector<uint8_t>>& ssid_list,
+      const std::vector<uint32_t>& frequency_list, uint32_t scan_interval_ms);
+  /* Creates ScanFilter using ssids, security flags and rssi_threshold
+   * The caller must ensure that the number of ssids match the number of
+   * security flags, also there must be ordering maintained among the two lists.
+   * For eg: (ssid[0], flags[0]) describe the SSID and security settings of one
+   * network
+   */
+  static android::hardware::wifi::offload::V1_0::ScanFilter createScanFilter(
+      const std::vector<std::vector<uint8_t>>& ssids,
+      const std::vector<uint8_t>& flags, int8_t rssi_threshold);
+  static ::com::android::server::wifi::wificond::NativeScanStats
+      convertToNativeScanStats(
+          const android::hardware::wifi::offload::V1_0::ScanStats& /* scanStats */);
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_OFFLOAD_SCAN_UTILS_H_
diff --git a/scanning/offload/offload_service_utils.cpp b/scanning/offload/offload_service_utils.cpp
new file mode 100644
index 0000000..2a14a32
--- /dev/null
+++ b/scanning/offload/offload_service_utils.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "wificond/scanning/offload/offload_service_utils.h"
+#include "wificond/scanning/offload/offload_scan_manager.h"
+
+using ::android::hardware::wifi::offload::V1_0::IOffload;
+
+namespace android {
+namespace wificond {
+
+android::sp<IOffload> OffloadServiceUtils::GetOffloadService() {
+  return IOffload::getService();
+}
+
+android::sp<OffloadCallback> OffloadServiceUtils::GetOffloadCallback(
+    OffloadCallbackHandlers* handlers) {
+  return new OffloadCallback(handlers);
+}
+
+OffloadDeathRecipient* OffloadServiceUtils::GetOffloadDeathRecipient(
+    OffloadDeathRecipientHandler handler) {
+  return new OffloadDeathRecipient(handler);
+}
+
+bool OffloadServiceUtils::IsOffloadScanSupported() const {
+  bool result = false;
+#ifdef WIFI_OFFLOAD_SCANS
+  LOG(VERBOSE) << "Offload HAL supported";
+  result = true;
+#endif
+  return result;
+}
+
+OffloadDeathRecipient::OffloadDeathRecipient(
+    OffloadDeathRecipientHandler handler)
+    : handler_(handler) {}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/scanning/offload/offload_service_utils.h b/scanning/offload/offload_service_utils.h
new file mode 100644
index 0000000..cff5575
--- /dev/null
+++ b/scanning/offload/offload_service_utils.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+#ifndef WIFICOND_OFFLOAD_SERVICE_UTILS_H_
+#define WIFICOND_OFFLOAD_SERVICE_UTILS_H_
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include "wificond/scanning/offload/offload_callback.h"
+#include "wificond/scanning/offload/offload_callback_handlers.h"
+
+namespace android {
+namespace wificond {
+
+typedef std::function<void(uint64_t)> OffloadDeathRecipientHandler;
+
+class OffloadDeathRecipient : public android::hardware::hidl_death_recipient {
+ public:
+  OffloadDeathRecipient(OffloadDeathRecipientHandler handler);
+
+  void serviceDied(
+      uint64_t cookie,
+      const android::wp<android::hidl::base::V1_0::IBase>& who) override {
+    this->handler_(cookie);
+  }
+
+ private:
+  OffloadDeathRecipientHandler handler_;
+};
+
+// Provides methods to get Offload HAL service and create callback
+class OffloadServiceUtils {
+ public:
+  OffloadServiceUtils() = default;
+  virtual ~OffloadServiceUtils() = default;
+  virtual android::sp<android::hardware::wifi::offload::V1_0::IOffload>
+      GetOffloadService();
+  // Check if Offload scan is supported on this device.
+  virtual bool IsOffloadScanSupported() const;
+  virtual android::sp<OffloadCallback> GetOffloadCallback(
+      OffloadCallbackHandlers* handlers);
+  virtual OffloadDeathRecipient* GetOffloadDeathRecipient(
+      OffloadDeathRecipientHandler handler);
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_OFFLOAD_SERVICE_UTILS_H
diff --git a/scanning/offload/scan_stats.cpp b/scanning/offload/scan_stats.cpp
new file mode 100644
index 0000000..4a963cb
--- /dev/null
+++ b/scanning/offload/scan_stats.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "wificond/scanning/offload/scan_stats.h"
+
+#include <android-base/logging.h>
+
+#include "wificond/parcelable_utils.h"
+
+using android::status_t;
+
+namespace com {
+namespace android {
+namespace server {
+namespace wifi {
+namespace wificond {
+
+NativeScanStats::NativeScanStats(uint32_t num_scans_requested_by_wifi,
+                                 uint32_t num_scans_serviced_by_wifi,
+                                 uint32_t subscription_duration_ms,
+                                 uint32_t scan_duration_ms,
+                                 uint32_t num_channels_scanned,
+                                 std::vector<uint8_t> histogram_channels)
+    : num_scans_requested_by_wifi_(num_scans_requested_by_wifi),
+      num_scans_serviced_by_wifi_(num_scans_serviced_by_wifi),
+      subscription_duration_ms_(subscription_duration_ms),
+      scan_duration_ms_(scan_duration_ms),
+      num_channels_scanned_(num_channels_scanned),
+      time_stamp_(0),
+      histogram_channels_(histogram_channels) {}
+
+NativeScanStats::NativeScanStats()
+    : num_scans_requested_by_wifi_(0),
+      num_scans_serviced_by_wifi_(0),
+      subscription_duration_ms_(0),
+      num_channels_scanned_(0),
+      time_stamp_(0) {}
+
+bool NativeScanStats::operator==(const NativeScanStats& rhs) const {
+  if ((rhs.num_scans_requested_by_wifi_ != num_scans_requested_by_wifi_) ||
+      (rhs.num_scans_serviced_by_wifi_ != num_scans_serviced_by_wifi_) ||
+      (rhs.scan_duration_ms_ != scan_duration_ms_) ||
+      (rhs.num_channels_scanned_ != num_channels_scanned_)) {
+    return false;
+  }
+  if (rhs.histogram_channels_.size() != histogram_channels_.size()) {
+    return false;
+  }
+  for (size_t i = 0; i < histogram_channels_.size(); i++) {
+    if (rhs.histogram_channels_[i] != histogram_channels_[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+status_t NativeScanStats::writeToParcel(::android::Parcel* parcel) const {
+  RETURN_IF_FAILED(parcel->writeUint32(num_scans_requested_by_wifi_));
+  RETURN_IF_FAILED(parcel->writeUint32(num_scans_serviced_by_wifi_));
+  RETURN_IF_FAILED(parcel->writeUint32(subscription_duration_ms_));
+  RETURN_IF_FAILED(parcel->writeUint32(scan_duration_ms_));
+  RETURN_IF_FAILED(parcel->writeUint32(num_channels_scanned_));
+  RETURN_IF_FAILED(parcel->writeByteVector(histogram_channels_));
+  return ::android::OK;
+}
+
+status_t NativeScanStats::readFromParcel(const ::android::Parcel* parcel) {
+  RETURN_IF_FAILED(parcel->readUint32(&num_scans_requested_by_wifi_));
+  RETURN_IF_FAILED(parcel->readUint32(&num_scans_serviced_by_wifi_));
+  RETURN_IF_FAILED(parcel->readUint32(&subscription_duration_ms_));
+  RETURN_IF_FAILED(parcel->readUint32(&scan_duration_ms_));
+  RETURN_IF_FAILED(parcel->readUint32(&num_channels_scanned_));
+  RETURN_IF_FAILED(parcel->readByteVector(&histogram_channels_));
+  return ::android::OK;
+}
+
+}  // namespace wificond
+}  // namespace wifi
+}  // namespace server
+}  // namespace android
+}  // namespace com
diff --git a/scanning/offload/scan_stats.h b/scanning/offload/scan_stats.h
new file mode 100644
index 0000000..eb1d16a
--- /dev/null
+++ b/scanning/offload/scan_stats.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFICOND_SCAN_STATS_H
+#define WIFICOND_SCAN_STATS_H
+
+#include <vector>
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+namespace com {
+namespace android {
+namespace server {
+namespace wifi {
+namespace wificond {
+
+class NativeScanStats : public ::android::Parcelable {
+ public:
+  NativeScanStats();
+  NativeScanStats(uint32_t num_scans_requested_by_wifi,
+                  uint32_t num_scans_serviced_by_wifi,
+                  uint32_t subscription_duration_ms, uint32_t scan_duration_ms,
+                  uint32_t num_channels_scanned,
+                  std::vector<uint8_t> histogram_channels);
+
+  bool operator==(const NativeScanStats&) const;
+  ::android::status_t writeToParcel(::android::Parcel* parcel) const override;
+  ::android::status_t readFromParcel(const ::android::Parcel* parcel) override;
+
+  uint32_t num_scans_requested_by_wifi_;
+  uint32_t num_scans_serviced_by_wifi_;
+  uint32_t subscription_duration_ms_;
+  uint32_t scan_duration_ms_;
+  uint32_t num_channels_scanned_;
+  uint32_t time_stamp_;
+  std::vector<uint8_t> histogram_channels_;
+};
+
+}  // namespace wificond
+}  // namespace wifi
+}  // namespace server
+}  // namespace android
+}  // namespace com
+#endif  // WIFICOND_SCAN_STATS_H
diff --git a/scanning/pno_settings.cpp b/scanning/pno_settings.cpp
index c8b5ff6..be051dd 100644
--- a/scanning/pno_settings.cpp
+++ b/scanning/pno_settings.cpp
@@ -28,6 +28,9 @@
 namespace wifi {
 namespace wificond {
 
+const uint32_t PnoSettings::kFastScanIterations = 3;
+const uint32_t PnoSettings::kSlowScanIntervalMultiplier = 3;
+
 status_t PnoSettings::writeToParcel(::android::Parcel* parcel) const {
   RETURN_IF_FAILED(parcel->writeInt32(interval_ms_));
   RETURN_IF_FAILED(parcel->writeInt32(min_2g_rssi_));
diff --git a/scanning/pno_settings.h b/scanning/pno_settings.h
index e236ec8..a606b0f 100644
--- a/scanning/pno_settings.h
+++ b/scanning/pno_settings.h
@@ -32,6 +32,9 @@
 
 class PnoSettings : public ::android::Parcelable {
  public:
+  static const uint32_t kFastScanIterations;
+  static const uint32_t kSlowScanIntervalMultiplier;
+
   PnoSettings()
       : interval_ms_(0),
         min_2g_rssi_(0),
diff --git a/scanning/scan_utils.cpp b/scanning/scan_utils.cpp
index 5199e2b..dfd051c 100644
--- a/scanning/scan_utils.cpp
+++ b/scanning/scan_utils.cpp
@@ -36,6 +36,7 @@
 namespace {
 
 constexpr uint8_t kElemIdSsid = 0;
+constexpr unsigned int kMsecPerSec = 1000;
 
 }  // namespace
 
@@ -151,17 +152,11 @@
       // These scan results are considered as malformed.
       return false;
     }
-    uint64_t tsf;
-    if (!bss.GetAttributeValue(NL80211_BSS_TSF, &tsf)) {
-      LOG(ERROR) << "Failed to get TSF from scan result packet";
+    uint64_t last_seen_since_boot_microseconds;
+    if (!GetBssTimestamp(bss, &last_seen_since_boot_microseconds)) {
+      // Logging is done inside |GetBssTimestamp|.
       return false;
     }
-    uint64_t beacon_tsf;
-    if (bss.GetAttributeValue(NL80211_BSS_BEACON_TSF, &beacon_tsf)) {
-      if (beacon_tsf > tsf) {
-        tsf = beacon_tsf;
-      }
-    }
     int32_t signal;
     if (!bss.GetAttributeValue(NL80211_BSS_SIGNAL_MBM, &signal)) {
       LOG(ERROR) << "Failed to get Signal Strength from scan result packet";
@@ -181,7 +176,37 @@
     }
 
     *scan_result =
-        NativeScanResult(ssid, bssid, ie, freq, signal, tsf, capability, associated);
+        NativeScanResult(ssid, bssid, ie, freq, signal,
+                         last_seen_since_boot_microseconds,
+                         capability, associated);
+  }
+  return true;
+}
+
+bool ScanUtils::GetBssTimestampForTesting(
+    const NL80211NestedAttr& bss,
+    uint64_t* last_seen_since_boot_microseconds){
+  return GetBssTimestamp(bss, last_seen_since_boot_microseconds);
+}
+
+bool ScanUtils::GetBssTimestamp(const NL80211NestedAttr& bss,
+                                uint64_t* last_seen_since_boot_microseconds){
+  uint64_t last_seen_since_boot_nanoseconds;
+  if (bss.GetAttributeValue(NL80211_BSS_LAST_SEEN_BOOTTIME,
+                            &last_seen_since_boot_nanoseconds)) {
+    *last_seen_since_boot_microseconds = last_seen_since_boot_nanoseconds / 1000;
+  } else {
+    // Fall back to use TSF if we can't find NL80211_BSS_LAST_SEEN_BOOTTIME
+    // attribute.
+    if (!bss.GetAttributeValue(NL80211_BSS_TSF, last_seen_since_boot_microseconds)) {
+      LOG(ERROR) << "Failed to get TSF from scan result packet";
+      return false;
+    }
+    uint64_t beacon_tsf_microseconds;
+    if (bss.GetAttributeValue(NL80211_BSS_BEACON_TSF, &beacon_tsf_microseconds)) {
+      *last_seen_since_boot_microseconds = std::max(*last_seen_since_boot_microseconds,
+                                                    beacon_tsf_microseconds);
+    }
   }
   return true;
 }
@@ -220,7 +245,8 @@
 bool ScanUtils::Scan(uint32_t interface_index,
                      bool request_random_mac,
                      const vector<vector<uint8_t>>& ssids,
-                     const vector<uint32_t>& freqs) {
+                     const vector<uint32_t>& freqs,
+                     int* error_code) {
   NL80211Packet trigger_scan(
       netlink_manager_->GetFamilyId(),
       NL80211_CMD_TRIGGER_SCAN,
@@ -261,8 +287,13 @@
   // scan results here, so it is OK to expect a timely response because
   // kernel is supposed to send the ERROR/ACK back before the scan starts.
   vector<unique_ptr<const NL80211Packet>> response;
-  if (!netlink_manager_->SendMessageAndGetAck(trigger_scan)) {
-    LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed";
+  if (!netlink_manager_->SendMessageAndGetAckOrError(trigger_scan,
+                                                     error_code)) {
+    // Logging is done inside |SendMessageAndGetAckOrError|.
+    return false;
+  }
+  if (*error_code != 0) {
+    LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed: " << strerror(*error_code);
     return false;
   }
   return true;
@@ -318,12 +349,13 @@
 
 bool ScanUtils::StartScheduledScan(
     uint32_t interface_index,
-    uint32_t interval_ms,
+    const SchedScanIntervalSetting& interval_setting,
     int32_t rssi_threshold,
     bool request_random_mac,
     const std::vector<std::vector<uint8_t>>& scan_ssids,
     const std::vector<std::vector<uint8_t>>& match_ssids,
-    const std::vector<uint32_t>& freqs) {
+    const std::vector<uint32_t>& freqs,
+    int* error_code) {
   NL80211Packet start_sched_scan(
       netlink_manager_->GetFamilyId(),
       NL80211_CMD_START_SCHED_SCAN,
@@ -354,6 +386,7 @@
         NL80211Attr<int32_t>(NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, rssi_threshold));
     scan_match_attr.AddAttribute(match_group);
   }
+  start_sched_scan.AddAttribute(scan_match_attr);
 
   // Append all attributes to the NL80211_CMD_START_SCHED_SCAN packet.
   start_sched_scan.AddAttribute(
@@ -364,9 +397,31 @@
   if (!freqs.empty()) {
     start_sched_scan.AddAttribute(freqs_attr);
   }
-  start_sched_scan.AddAttribute(
-      NL80211Attr<uint32_t>(NL80211_ATTR_SCHED_SCAN_INTERVAL, interval_ms));
-  start_sched_scan.AddAttribute(scan_match_attr);
+
+  if (!interval_setting.plans.empty()) {
+    NL80211NestedAttr scan_plans(NL80211_ATTR_SCHED_SCAN_PLANS);
+    for (unsigned int i = 0; i < interval_setting.plans.size(); i++) {
+      NL80211NestedAttr scan_plan(i + 1);
+      scan_plan.AddAttribute(
+          NL80211Attr<uint32_t>(NL80211_SCHED_SCAN_PLAN_INTERVAL,
+                                interval_setting.plans[i].interval_ms / kMsecPerSec));
+      scan_plan.AddAttribute(
+          NL80211Attr<uint32_t>(NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+                                interval_setting.plans[i].n_iterations));
+      scan_plans.AddAttribute(scan_plan);
+    }
+    NL80211NestedAttr last_scan_plan(interval_setting.plans.size() + 1);
+    last_scan_plan.AddAttribute(
+        NL80211Attr<uint32_t>(NL80211_SCHED_SCAN_PLAN_INTERVAL,
+                              interval_setting.final_interval_ms / kMsecPerSec));
+    scan_plans.AddAttribute(last_scan_plan);
+    start_sched_scan.AddAttribute(scan_plans);
+  } else {
+    start_sched_scan.AddAttribute(
+        NL80211Attr<uint32_t>(NL80211_ATTR_SCHED_SCAN_INTERVAL,
+                              interval_setting.final_interval_ms));
+  }
+
   if (request_random_mac) {
     start_sched_scan.AddAttribute(
         NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
@@ -374,8 +429,13 @@
   }
 
   vector<unique_ptr<const NL80211Packet>> response;
-  if (!netlink_manager_->SendMessageAndGetAck(start_sched_scan)) {
-    LOG(ERROR) << "NL80211_CMD_START_SCHED_SCAN failed";
+  if (!netlink_manager_->SendMessageAndGetAckOrError(start_sched_scan,
+                                                     error_code)) {
+    // Logging is done inside |SendMessageAndGetAckOrError|.
+    return false;
+  }
+  if (*error_code != 0) {
+    LOG(ERROR) << "NL80211_CMD_START_SCHED_SCAN failed: " << strerror(*error_code);
     return false;
   }
 
diff --git a/scanning/scan_utils.h b/scanning/scan_utils.h
index 84baeec..87df13a 100644
--- a/scanning/scan_utils.h
+++ b/scanning/scan_utils.h
@@ -41,8 +41,20 @@
 namespace android {
 namespace wificond {
 
+class NL80211NestedAttr;
 class NL80211Packet;
 
+struct SchedScanIntervalSetting {
+  struct ScanPlan {
+    uint32_t interval_ms;
+    uint32_t n_iterations;
+  };
+  std::vector<ScanPlan> plans;
+  // After |plans| has been exhausted, scan at every
+  // |final_interval_ms|.
+  uint32_t final_interval_ms{0};
+};
+
 // Provides scanning helper functions.
 class ScanUtils {
  public:
@@ -69,11 +81,13 @@
   // If |ssids| contains an empty string, it will a scan for all ssids.
   // |freqs| is a vector of frequencies we request to scan.
   // If |freqs| is an empty vector, it will scan all supported frequencies.
+  // |error_code| contains the errno kernel replied when this returns false.
   // Returns true on success.
   virtual bool Scan(uint32_t interface_index,
                     bool request_random_mac,
                     const std::vector<std::vector<uint8_t>>& ssids,
-                    const std::vector<uint32_t>& freqs);
+                    const std::vector<uint32_t>& freqs,
+                    int* error_code);
 
   // Send scan request to kernel for interface with index |interface_index|.
   // |inteval_ms| is the expected scan interval in milliseconds.
@@ -91,15 +105,17 @@
   // If |freqs| is an empty vector, it will scan all supported frequencies.
   // Only BSSs match the |match_ssids| and |rssi_threshold| will be returned as
   // scan results.
+  // |error_code| contains the errno kernel replied when this returns false.
   // Returns true on success.
   virtual bool StartScheduledScan(
       uint32_t interface_index,
-      uint32_t interval_ms,
+      const SchedScanIntervalSetting& interval_setting,
       int32_t rssi_threshold,
       bool request_random_mac,
       const std::vector<std::vector<uint8_t>>& scan_ssids,
       const std::vector<std::vector<uint8_t>>& match_ssids,
-      const std::vector<uint32_t>& freqs);
+      const std::vector<uint32_t>& freqs,
+      int* error_code);
 
   // Stop existing scheduled scan on interface with index |interface_index|.
   // Returns true on success.
@@ -110,6 +126,14 @@
   // Returns true on success.
   virtual bool AbortScan(uint32_t interface_index);
 
+  // Visible for testing.
+  // Get a timestamp for the scan result |bss| represents.
+  // This timestamp records the time passed since boot when last time the
+  // AP was seen.
+  virtual bool GetBssTimestampForTesting(
+      const NL80211NestedAttr& bss,
+       uint64_t* last_seen_since_boot_microseconds);
+
   // Sign up to be notified when new scan results are available.
   // |handler| will be called when the kernel signals to wificond that a scan
   // has been completed on the given |interface_index|.  See the declaration of
@@ -137,6 +161,8 @@
   virtual void UnsubscribeSchedScanResultNotification(uint32_t interface_index);
 
  private:
+  bool GetBssTimestamp(const NL80211NestedAttr& bss,
+                       uint64_t* last_seen_since_boot_microseconds);
   bool GetSSIDFromInfoElement(const std::vector<uint8_t>& ie,
                               std::vector<uint8_t>* ssid);
   // Converts a NL80211_CMD_NEW_SCAN_RESULTS packet to a ScanResult object.
diff --git a/scanning/scanner_impl.cpp b/scanning/scanner_impl.cpp
index 2c1b1d0..9bf13b3 100644
--- a/scanning/scanner_impl.cpp
+++ b/scanning/scanner_impl.cpp
@@ -23,17 +23,22 @@
 
 #include "wificond/client_interface_impl.h"
 #include "wificond/scanning/scan_utils.h"
+#include "wificond/scanning/offload/offload_service_utils.h"
+#include "wificond/scanning/offload/offload_scan_manager.h"
 
 using android::binder::Status;
 using android::net::wifi::IPnoScanEvent;
 using android::net::wifi::IScanEvent;
+using android::hardware::wifi::offload::V1_0::IOffload;
 using android::sp;
 using com::android::server::wifi::wificond::NativeScanResult;
 using com::android::server::wifi::wificond::PnoSettings;
 using com::android::server::wifi::wificond::SingleScanSettings;
 
+using std::pair;
 using std::string;
 using std::vector;
+using std::weak_ptr;
 
 using namespace std::placeholders;
 
@@ -46,10 +51,12 @@
                          const WiphyFeatures& wiphy_features,
                          ClientInterfaceImpl* client_interface,
                          NetlinkUtils* netlink_utils,
-                         ScanUtils* scan_utils)
+                         ScanUtils* scan_utils,
+                         weak_ptr<OffloadServiceUtils> offload_service_utils)
     : valid_(true),
       scan_started_(false),
       pno_scan_started_(false),
+      offload_scan_supported_(false),
       wiphy_index_(wiphy_index),
       interface_index_(interface_index),
       scan_capabilities_(scan_capabilities),
@@ -72,6 +79,12 @@
       std::bind(&ScannerImpl::OnSchedScanResultsReady,
                 this,
                 _1, _2));
+  offload_scan_manager_.reset(
+      new OffloadScanManager(
+          offload_service_utils,
+          std::bind(&ScannerImpl::OnOffloadScanResult,
+              this, _1)));
+  offload_scan_supported_ = offload_service_utils.lock()->IsOffloadScanSupported();
 }
 
 ScannerImpl::~ScannerImpl() {
@@ -195,7 +208,10 @@
     freqs.push_back(channel.frequency_);
   }
 
-  if (!scan_utils_->Scan(interface_index_, request_random_mac, ssids, freqs)) {
+  int error_code = 0;
+  if (!scan_utils_->Scan(interface_index_, request_random_mac, ssids, freqs,
+                         &error_code)) {
+    CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond";
     *out_success = false;
     return Status::ok();
   }
@@ -206,9 +222,72 @@
 
 Status ScannerImpl::startPnoScan(const PnoSettings& pno_settings,
                                  bool* out_success) {
+  if (!offload_scan_supported_ || !StartPnoScanOffload(pno_settings)) {
+    *out_success = StartPnoScanDefault(pno_settings);
+  } else {
+    // scanning over offload succeeded
+    *out_success = true;
+  }
+  return Status::ok();
+}
+
+bool ScannerImpl::StartPnoScanOffload(const PnoSettings& pno_settings) {
+  OffloadScanManager::ReasonCode reason_code;
+  vector<vector<uint8_t>> scan_ssids;
+  vector<vector<uint8_t>> match_ssids;
+  vector<uint8_t> match_security;
+  // Empty frequency list: scan all frequencies.
+  vector<uint32_t> freqs;
+
+  ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs, &match_security);
+
+  bool success = offload_scan_manager_->startScan(
+          pno_settings.interval_ms_,
+          // TODO: honor both rssi thresholds.
+          pno_settings.min_5g_rssi_,
+          scan_ssids,
+          match_ssids,
+          match_security,
+          freqs,
+          &reason_code);
+  return success;
+}
+
+void ScannerImpl::ParsePnoSettings(const PnoSettings& pno_settings,
+    vector<vector<uint8_t>>* scan_ssids,
+    vector<vector<uint8_t>>* match_ssids,
+    vector<uint32_t>* freqs,
+    vector<uint8_t>* match_security) {
+  // TODO provide actionable security match parameters
+  const uint8_t kNetworkFlagsDefault = 0;
+  vector<vector<uint8_t>> skipped_scan_ssids;
+  vector<vector<uint8_t>> skipped_match_ssids;
+  for (auto& network : pno_settings.pno_networks_) {
+    // Add hidden network ssid.
+    if (network.is_hidden_) {
+      // TODO remove pruning for Offload Scans
+      if (scan_ssids->size() + 1 > scan_capabilities_.max_num_sched_scan_ssids) {
+        skipped_scan_ssids.emplace_back(network.ssid_);
+        continue;
+      }
+      scan_ssids->push_back(network.ssid_);
+    }
+
+    if (match_ssids->size() + 1 > scan_capabilities_.max_match_sets) {
+      skipped_match_ssids.emplace_back(network.ssid_);
+      continue;
+    }
+    match_ssids->push_back(network.ssid_);
+    match_security->push_back(kNetworkFlagsDefault);
+  }
+
+  LogSsidList(skipped_scan_ssids, "Skip scan ssid for pno scan");
+  LogSsidList(skipped_match_ssids, "Skip match ssid for pno scan");
+}
+
+bool ScannerImpl::StartPnoScanDefault(const PnoSettings& pno_settings) {
   if (!CheckIsValid()) {
-    *out_success = false;
-    return Status::ok();
+      return false;
   }
   if (pno_scan_started_) {
     LOG(WARNING) << "Pno scan already started";
@@ -216,70 +295,63 @@
   // An empty ssid for a wild card scan.
   vector<vector<uint8_t>> scan_ssids = {{}};
   vector<vector<uint8_t>> match_ssids;
+  vector<uint8_t> unused;
   // Empty frequency list: scan all frequencies.
   vector<uint32_t> freqs;
 
-  vector<vector<uint8_t>> skipped_scan_ssids;
-  vector<vector<uint8_t>> skipped_match_ssids;
-  for (auto& network : pno_settings.pno_networks_) {
-    // Add hidden network ssid.
-    if (network.is_hidden_) {
-      if (scan_ssids.size() + 1 > scan_capabilities_.max_num_sched_scan_ssids) {
-        skipped_scan_ssids.emplace_back(network.ssid_);
-        continue;
-      }
-      scan_ssids.push_back(network.ssid_);
-    }
-
-    if (match_ssids.size() + 1 > scan_capabilities_.max_match_sets) {
-      skipped_match_ssids.emplace_back(network.ssid_);
-      continue;
-    }
-    match_ssids.push_back(network.ssid_);
-  }
-
-  LogSsidList(skipped_scan_ssids, "Skip scan ssid for pno scan");
-  LogSsidList(skipped_match_ssids, "Skip match ssid for pno scan");
-
+  ParsePnoSettings(pno_settings, &scan_ssids, &match_ssids, &freqs, &unused);
   // Only request MAC address randomization when station is not associated.
   bool request_random_mac = wiphy_features_.supports_random_mac_sched_scan &&
       !client_interface_->IsAssociated();
 
+  int error_code = 0;
   if (!scan_utils_->StartScheduledScan(interface_index_,
-                                       pno_settings.interval_ms_,
+                                       GenerateIntervalSetting(pno_settings),
                                        // TODO: honor both rssi thresholds.
                                        pno_settings.min_5g_rssi_,
                                        request_random_mac,
                                        scan_ssids,
                                        match_ssids,
-                                       freqs)) {
-    *out_success = false;
+                                       freqs,
+                                       &error_code)) {
     LOG(ERROR) << "Failed to start pno scan";
-    return Status::ok();
+    CHECK(error_code != ENODEV) << "Driver is in a bad state, restarting wificond";
+    return false;
   }
   LOG(INFO) << "Pno scan started";
   pno_scan_started_ = true;
-  *out_success = true;
-  return Status::ok();
+  return true;
 }
 
 Status ScannerImpl::stopPnoScan(bool* out_success) {
+  if (!offload_scan_supported_ || !StopPnoScanOffload()) {
+    *out_success = StopPnoScanDefault();
+  } else {
+    // Pno scans over offload stopped successfully
+    *out_success = true;
+  }
+  return Status::ok();
+}
+
+bool ScannerImpl::StopPnoScanOffload() {
+  OffloadScanManager::ReasonCode reason_code;
+  return(offload_scan_manager_->stopScan(&reason_code));
+}
+
+bool ScannerImpl::StopPnoScanDefault() {
   if (!CheckIsValid()) {
-    *out_success = false;
-    return Status::ok();
+    return false;
   }
 
   if (!pno_scan_started_) {
     LOG(WARNING) << "No pno scan started";
   }
   if (!scan_utils_->StopScheduledScan(interface_index_)) {
-    *out_success = false;
-    return Status::ok();
+    return false;
   }
   LOG(INFO) << "Pno scan stopped";
   pno_scan_started_ = false;
-  *out_success = true;
-  return Status::ok();
+  return true;
 }
 
 Status ScannerImpl::abortScan() {
@@ -376,6 +448,34 @@
   }
 }
 
+SchedScanIntervalSetting ScannerImpl::GenerateIntervalSetting(
+    const ::com::android::server::wifi::wificond::PnoSettings&
+        pno_settings) const {
+  bool support_num_scan_plans = scan_capabilities_.max_num_scan_plans >= 2;
+  bool support_scan_plan_interval =
+      scan_capabilities_.max_scan_plan_interval * 1000 >=
+          pno_settings.interval_ms_ * PnoSettings::kSlowScanIntervalMultiplier;
+  bool support_scan_plan_iterations =
+      scan_capabilities_.max_scan_plan_iterations >=
+                  PnoSettings::kFastScanIterations;
+
+  uint32_t fast_scan_interval =
+      static_cast<uint32_t>(pno_settings.interval_ms_);
+  if (support_num_scan_plans && support_scan_plan_interval &&
+      support_scan_plan_iterations) {
+    return SchedScanIntervalSetting{
+        {{fast_scan_interval, PnoSettings::kFastScanIterations}},
+        fast_scan_interval * PnoSettings::kSlowScanIntervalMultiplier};
+  } else {
+    // Device doesn't support the provided scan plans.
+    // Specify single interval instead.
+    // In this case, the driver/firmware is expected to implement back off
+    // logic internally using |pno_settings.interval_ms_| as "fast scan"
+    // interval.
+    return SchedScanIntervalSetting{{}, fast_scan_interval};
+  }
+}
+
 void ScannerImpl::LogSsidList(vector<vector<uint8_t>>& ssid_list,
                               string prefix) {
   if (ssid_list.empty()) {
@@ -391,5 +491,15 @@
   LOG(WARNING) << prefix << ": " << ssid_list_string;
 }
 
+void ScannerImpl::OnOffloadScanResult(
+    std::vector<NativeScanResult> scanResult) {
+  LOG(INFO) << "Offload Scan results received";
+  if (pno_scan_event_handler_ != nullptr) {
+    pno_scan_event_handler_->OnPnoNetworkFound();
+  } else {
+    LOG(WARNING) << "No scan event handler Offload Scan result";
+  }
+}
+
 }  // namespace wificond
 }  // namespace android
diff --git a/scanning/scanner_impl.h b/scanning/scanner_impl.h
index 61a7b96..2bb9262 100644
--- a/scanning/scanner_impl.h
+++ b/scanning/scanner_impl.h
@@ -24,12 +24,14 @@
 
 #include "android/net/wifi/BnWifiScannerImpl.h"
 #include "wificond/net/netlink_utils.h"
+#include "wificond/scanning/offload/offload_scan_manager.h"
+#include "wificond/scanning/scan_utils.h"
 
 namespace android {
 namespace wificond {
 
 class ClientInterfaceImpl;
-class ScanUtils;
+class OffloadServiceUtils;
 
 class ScannerImpl : public android::net::wifi::BnWifiScannerImpl {
  public:
@@ -39,7 +41,8 @@
               const WiphyFeatures& wiphy_features,
               ClientInterfaceImpl* client_interface,
               NetlinkUtils* netlink_utils,
-              ScanUtils* scan_utils);
+              ScanUtils* scan_utils,
+              std::weak_ptr<OffloadServiceUtils> offload_service_utils);
   ~ScannerImpl();
   // Returns a vector of available frequencies for 2.4GHz channels.
   ::android::binder::Status getAvailable2gChannels(
@@ -74,6 +77,8 @@
 
  private:
   bool CheckIsValid();
+  void OnOffloadScanResult(
+      std::vector<::com::android::server::wifi::wificond::NativeScanResult>);
   void OnScanResultsReady(
       uint32_t interface_index,
       bool aborted,
@@ -82,11 +87,26 @@
   void OnSchedScanResultsReady(uint32_t interface_index, bool scan_stopped);
   void LogSsidList(std::vector<std::vector<uint8_t>>& ssid_list,
                    std::string prefix);
+  bool StartPnoScanDefault(
+      const ::com::android::server::wifi::wificond::PnoSettings& pno_settings);
+  bool StartPnoScanOffload(
+      const ::com::android::server::wifi::wificond::PnoSettings& pno_settings);
+  bool StopPnoScanDefault();
+  bool StopPnoScanOffload();
+  void ParsePnoSettings(
+    const ::com::android::server::wifi::wificond::PnoSettings& pno_settings,
+    std::vector<std::vector<uint8_t>>* scan_ssids,
+    std::vector<std::vector<uint8_t>>* match_ssids,
+    std::vector<uint32_t>* freqs,
+    std::vector<uint8_t>* match_security);
+  SchedScanIntervalSetting GenerateIntervalSetting(
+    const ::com::android::server::wifi::wificond::PnoSettings& pno_settings) const;
 
   // Boolean variables describing current scanner status.
   bool valid_;
   bool scan_started_;
   bool pno_scan_started_;
+  bool offload_scan_supported_;
 
   const uint32_t wiphy_index_;
   const uint32_t interface_index_;
@@ -100,6 +120,7 @@
   ScanUtils* const scan_utils_;
   ::android::sp<::android::net::wifi::IPnoScanEvent> pno_scan_event_handler_;
   ::android::sp<::android::net::wifi::IScanEvent> scan_event_handler_;
+  std::unique_ptr<OffloadScanManager> offload_scan_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(ScannerImpl);
 };
diff --git a/server.cpp b/server.cpp
index 595fd8a..be57e89 100644
--- a/server.cpp
+++ b/server.cpp
@@ -20,6 +20,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <binder/IPCThreadState.h>
 #include <binder/PermissionCache.h>
 
@@ -268,7 +269,10 @@
     // Some kernel/driver uses station type for p2p interface.
     // In that case we can only rely on hard-coded name to exclude
     // p2p interface from station interfaces.
-    if (iface.name != "p2p0") {
+    // Currently NAN interfaces also use station type.
+    // We should blacklist NAN interfaces as well.
+    if (iface.name != "p2p0" &&
+        !android::base::StartsWith(iface.name, "aware_data")) {
       *interface = iface;
       return true;
     }
diff --git a/tests/mock_client_interface_impl.cpp b/tests/mock_client_interface_impl.cpp
new file mode 100644
index 0000000..775eb1c
--- /dev/null
+++ b/tests/mock_client_interface_impl.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "wificond/tests/mock_client_interface_impl.h"
+
+#include <vector>
+
+#include <wifi_system/interface_tool.h>
+#include <wifi_system/supplicant_manager.h>
+
+#include "wificond/net/netlink_utils.h"
+#include "wificond/scanning/scan_utils.h"
+
+namespace android {
+namespace wificond {
+
+const char kTestInterfaceName[] = "testwifi0";
+const uint8_t kTestInterfaceMacAddress[] = {0x10, 0x20, 0xfe, 0xae, 0x2d, 0xc2};
+const uint32_t kTestInterfaceIndex = 42;
+const uint32_t kTestWiphyIndex = 2;
+
+MockClientInterfaceImpl::MockClientInterfaceImpl(
+      android::wifi_system::InterfaceTool* interface_tool,
+      android::wifi_system::SupplicantManager* supplicant_manager,
+      NetlinkUtils* netlink_utils,
+      ScanUtils* scan_utils)
+    : ClientInterfaceImpl(
+        kTestWiphyIndex,
+        kTestInterfaceName,
+        kTestInterfaceIndex,
+        std::vector<uint8_t>(
+            kTestInterfaceMacAddress,
+            kTestInterfaceMacAddress + arraysize(kTestInterfaceMacAddress)),
+        interface_tool,
+        supplicant_manager,
+        netlink_utils,
+        scan_utils) {}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/mock_client_interface_impl.h b/tests/mock_client_interface_impl.h
new file mode 100644
index 0000000..05cf686
--- /dev/null
+++ b/tests/mock_client_interface_impl.h
@@ -0,0 +1,44 @@
+/*
+ * 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 WIFICOND_TEST_MOCK_CLIENT_INTERFACE_IMPL_H_
+#define WIFICOND_TEST_MOCK_CLIENT_INTERFACE_IMPL_H_
+
+#include <gmock/gmock.h>
+
+#include "wificond/client_interface_impl.h"
+
+namespace android {
+
+namespace wificond {
+
+class MockClientInterfaceImpl : public ClientInterfaceImpl {
+ public:
+  MockClientInterfaceImpl(
+      android::wifi_system::InterfaceTool*,
+      android::wifi_system::SupplicantManager*,
+      NetlinkUtils*,
+      ScanUtils*);
+  ~MockClientInterfaceImpl() override = default;
+
+  MOCK_CONST_METHOD0(IsAssociated, bool());
+
+};  // class MockClientInterfaceImpl
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_TEST_MOCK_CLIENT_INTERFACE_IMPL_H_
diff --git a/tests/mock_offload.cpp b/tests/mock_offload.cpp
new file mode 100644
index 0000000..02ad00d
--- /dev/null
+++ b/tests/mock_offload.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "wificond/tests/mock_offload.h"
+
+namespace android {
+namespace wificond {
+
+MockOffload::MockOffload() {}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/mock_offload.h b/tests/mock_offload.h
new file mode 100644
index 0000000..399639c
--- /dev/null
+++ b/tests/mock_offload.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFICOND_TEST_MOCK_OFFLOAD_H
+#define WIFICOND_TEST_MOCK_OFFLOAD_H
+
+#include <gmock/gmock.h>
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+
+namespace android {
+namespace wificond {
+
+typedef std::function<void(
+    const android::hardware::wifi::offload::V1_0::OffloadStatus& status,
+    const android::hardware::wifi::offload::V1_0::ScanStats& scanStats)>
+        OnScanStatsCallback;
+typedef std::function<void(
+    const android::hardware::wifi::offload::V1_0::OffloadStatus& status)>
+        StatusCallback;
+
+class MockOffload : public android::hardware::wifi::offload::V1_0::IOffload {
+ public:
+  MockOffload();
+  ~MockOffload() override = default;
+
+  MOCK_METHOD3(
+      configureScans,
+      android::hardware::Return<void>(
+          const android::hardware::wifi::offload::V1_0::ScanParam& param,
+          const android::hardware::wifi::offload::V1_0::ScanFilter& filter,
+          StatusCallback _hidl_cb));
+  MOCK_METHOD1(getScanStats,
+               android::hardware::Return<void>(OnScanStatsCallback cb));
+  MOCK_METHOD2(subscribeScanResults,
+               android::hardware::Return<void>(uint32_t delayMs,
+                                               StatusCallback _hidl_cb));
+  MOCK_METHOD0(unsubscribeScanResults, android::hardware::Return<void>());
+  MOCK_METHOD1(
+      setEventCallback,
+      android::hardware::Return<void>(
+          const android::sp<
+              android::hardware::wifi::offload::V1_0::IOffloadCallback>& cb));
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_TEST_MOCK_OFFLOAD_H
diff --git a/tests/mock_offload_callback_handlers.cpp b/tests/mock_offload_callback_handlers.cpp
new file mode 100644
index 0000000..e4e5d48
--- /dev/null
+++ b/tests/mock_offload_callback_handlers.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "wificond/tests/mock_offload_callback_handlers.h"
+
+namespace android {
+namespace wificond {
+
+MockOffloadCallbackHandlers::MockOffloadCallbackHandlers() {}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/mock_offload_callback_handlers.h b/tests/mock_offload_callback_handlers.h
new file mode 100644
index 0000000..e9b6285
--- /dev/null
+++ b/tests/mock_offload_callback_handlers.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFICOND_TESTS_MOCK_OFFLOAD_CALLBACK_HANDLERS_H_
+#define WIFICOND_TESTS_MOCK_OFFLOAD_CALLBACK_HANDLERS_H_
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include "wificond/scanning/offload/offload_callback_handlers.h"
+
+namespace android {
+namespace wificond {
+
+class MockOffloadCallbackHandlers : public OffloadCallbackHandlers {
+ public:
+  MockOffloadCallbackHandlers();
+  ~MockOffloadCallbackHandlers() override = default;
+
+  MOCK_METHOD1(
+      OnScanResultHandler,
+      void(const std::vector<
+           android::hardware::wifi::offload::V1_0::ScanResult>& scanResult));
+  MOCK_METHOD1(OnErrorHandler,
+               void(const android::hardware::wifi::offload::V1_0::OffloadStatus&
+                    status));
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_TESTS_MOCK_OFFLOAD_CALLBACK_HANDLERS_H_
diff --git a/tests/mock_offload_service_utils.cpp b/tests/mock_offload_service_utils.cpp
new file mode 100644
index 0000000..c58be31
--- /dev/null
+++ b/tests/mock_offload_service_utils.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "wificond/tests/mock_offload_service_utils.h"
+
+namespace android {
+namespace wificond {
+
+MockOffloadServiceUtils::MockOffloadServiceUtils() {}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/mock_offload_service_utils.h b/tests/mock_offload_service_utils.h
new file mode 100644
index 0000000..fd9fd34
--- /dev/null
+++ b/tests/mock_offload_service_utils.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFICOND_TEST_MOCK_OFFLOAD_SERVICE_UTILS_H_
+#define WIFICOND_TEST_MOCK_OFFLOAD_SERVICE_UTILS_H_
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include "wificond/scanning/offload/offload_callback.h"
+#include "wificond/scanning/offload/offload_callback_handlers.h"
+#include "wificond/scanning/offload/offload_service_utils.h"
+
+namespace android {
+namespace wificond {
+
+class MockOffloadServiceUtils : public OffloadServiceUtils {
+ public:
+  MockOffloadServiceUtils();
+  ~MockOffloadServiceUtils() override = default;
+  MOCK_CONST_METHOD0(IsOffloadScanSupported, bool());
+  MOCK_METHOD0(GetOffloadService,
+               sp<android::hardware::wifi::offload::V1_0::IOffload>());
+  MOCK_METHOD1(GetOffloadCallback,
+               sp<OffloadCallback>(OffloadCallbackHandlers* handlers));
+  MOCK_METHOD1(GetOffloadDeathRecipient,
+               android::wificond::OffloadDeathRecipient*(
+                   OffloadDeathRecipientHandler handler));
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_TEST_MOCK_OFFLOAD_SERVICE_UTILS_H_
diff --git a/tests/mock_scan_utils.h b/tests/mock_scan_utils.h
index 4f9e461..3e5cc7f 100644
--- a/tests/mock_scan_utils.h
+++ b/tests/mock_scan_utils.h
@@ -29,22 +29,34 @@
   MockScanUtils(NetlinkManager* netlink_manager);
   ~MockScanUtils() override = default;
 
-  MOCK_METHOD2(GetScanResult, bool(
-      uint32_t interface_index,
-      std::vector<::com::android::server::wifi::wificond::NativeScanResult>* out_scan_results));
-
-  MOCK_METHOD4(Scan, bool(
-      uint32_t interface_index,
-      bool random_mac,
-      const std::vector<std::vector<uint8_t>>& ssids,
-      const std::vector<uint32_t>& freqs));
+  MOCK_METHOD1(UnsubscribeScanResultNotification,
+               void(uint32_t interface_index));
+  MOCK_METHOD1(AbortScan, bool(uint32_t interface_index));
+  MOCK_METHOD1(StopScheduledScan, bool(uint32_t interface_index));
 
   MOCK_METHOD2(SubscribeScanResultNotification,void(
       uint32_t interface_index,
       OnScanResultsReadyHandler handler));
+  MOCK_METHOD2(GetScanResult, bool(
+      uint32_t interface_index,
+      std::vector<::com::android::server::wifi::wificond::NativeScanResult>* out_scan_results));
 
-  MOCK_METHOD1(UnsubscribeScanResultNotification,
-               void(uint32_t interface_index));
+  MOCK_METHOD5(Scan, bool(
+      uint32_t interface_index,
+      bool random_mac,
+      const std::vector<std::vector<uint8_t>>& ssids,
+      const std::vector<uint32_t>& freqs,
+      int* error_code));
+
+  MOCK_METHOD8(StartScheduledScan, bool(
+      uint32_t interface_index,
+      const SchedScanIntervalSetting& interval_setting,
+      int32_t rssi_threshold,
+      bool request_random_mac,
+      const std::vector<std::vector<uint8_t>>& scan_ssids,
+      const std::vector<std::vector<uint8_t>>& match_ssids,
+      const std::vector<uint32_t>& freqs,
+      int* error_code));
 
 };  // class MockScanUtils
 
diff --git a/tests/netlink_utils_unittest.cpp b/tests/netlink_utils_unittest.cpp
index b1a7939..646d4a9 100644
--- a/tests/netlink_utils_unittest.cpp
+++ b/tests/netlink_utils_unittest.cpp
@@ -42,6 +42,9 @@
 constexpr uint8_t kFakeMaxNumScanSSIDs = 10;
 constexpr uint8_t kFakeMaxNumSchedScanSSIDs = 16;
 constexpr uint8_t kFakeMaxMatchSets = 18;
+constexpr uint8_t kFakeMaxNumScanPlans = 8;
+constexpr uint8_t kFakeMaxScanPlanIntervals = 80;
+constexpr uint8_t kFakeMaxScanPlanIterations = 10;
 constexpr uint16_t kFakeFamilyId = 14;
 constexpr uint32_t kFakeFrequency1 = 2412;
 constexpr uint32_t kFakeFrequency2 = 2437;
@@ -80,6 +83,116 @@
   return CreateControlMessageError(0);
 }
 
+void AppendScanCapabilitiesAttributes(NL80211Packet* packet,
+                                      bool supports_scan_plan) {
+  packet->AddAttribute(NL80211Attr<uint8_t>(NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+                                            kFakeMaxNumScanSSIDs));
+  packet->AddAttribute(NL80211Attr<uint8_t>(
+      NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+      kFakeMaxNumSchedScanSSIDs));
+  packet->AddAttribute(NL80211Attr<uint8_t>(NL80211_ATTR_MAX_MATCH_SETS,
+                                            kFakeMaxMatchSets));
+  if (supports_scan_plan) {
+    packet->AddAttribute(NL80211Attr<uint32_t>(
+        NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+        kFakeMaxNumScanPlans));
+    packet->AddAttribute(NL80211Attr<uint32_t>(
+        NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+        kFakeMaxScanPlanIntervals));
+    packet->AddAttribute(NL80211Attr<uint32_t>(
+        NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+        kFakeMaxScanPlanIterations));
+  }
+}
+
+void AppendBandInfoAttributes(NL80211Packet* packet) {
+  NL80211NestedAttr freq_2g_1(1);
+  NL80211NestedAttr freq_2g_2(2);
+  NL80211NestedAttr freq_2g_3(3);
+  NL80211NestedAttr freq_5g_1(4);
+  NL80211NestedAttr freq_5g_2(5);
+  NL80211NestedAttr freq_dfs_1(6);
+  freq_2g_1.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
+                                               kFakeFrequency1));
+  freq_2g_2.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
+                                               kFakeFrequency2));
+  freq_2g_3.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
+                                               kFakeFrequency3));
+  freq_5g_1.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
+                                               kFakeFrequency4));
+  freq_5g_2.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
+                                               kFakeFrequency5));
+  // DFS frequency.
+  freq_dfs_1.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
+                                                kFakeFrequency6));
+  freq_dfs_1.AddAttribute(NL80211Attr<uint32_t>(
+      NL80211_FREQUENCY_ATTR_DFS_STATE,
+      NL80211_DFS_USABLE));
+
+  NL80211NestedAttr band_2g_freqs(NL80211_BAND_ATTR_FREQS);
+  NL80211NestedAttr band_5g_freqs(NL80211_BAND_ATTR_FREQS);
+  band_2g_freqs.AddAttribute(freq_2g_1);
+  band_2g_freqs.AddAttribute(freq_2g_2);
+  band_2g_freqs.AddAttribute(freq_2g_3);
+  band_5g_freqs.AddAttribute(freq_5g_1);
+  band_5g_freqs.AddAttribute(freq_5g_2);
+  band_5g_freqs.AddAttribute(freq_dfs_1);
+
+  NL80211NestedAttr band_2g_attr(1);
+  NL80211NestedAttr band_5g_attr(2);
+  band_2g_attr.AddAttribute(band_2g_freqs);
+  band_5g_attr.AddAttribute(band_5g_freqs);
+
+  NL80211NestedAttr band_attr(NL80211_ATTR_WIPHY_BANDS);
+  band_attr.AddAttribute(band_2g_attr);
+  band_attr.AddAttribute(band_5g_attr);
+
+  packet->AddAttribute(band_attr);
+}
+
+void AppendWiphyFeaturesAttributes(NL80211Packet* packet) {
+  packet->AddAttribute(NL80211Attr<uint32_t>(
+      NL80211_ATTR_FEATURE_FLAGS,
+      NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR));
+}
+
+void VerifyScanCapabilities(const ScanCapabilities& scan_capabilities,
+                            bool supports_scan_plan) {
+  EXPECT_EQ(scan_capabilities.max_num_scan_ssids,
+            kFakeMaxNumScanSSIDs);
+  EXPECT_EQ(scan_capabilities.max_num_sched_scan_ssids,
+            kFakeMaxNumSchedScanSSIDs);
+  EXPECT_EQ(scan_capabilities.max_match_sets,
+            kFakeMaxMatchSets);
+  if (supports_scan_plan) {
+    EXPECT_EQ(scan_capabilities.max_num_scan_plans,
+              kFakeMaxNumScanPlans);
+    EXPECT_EQ(scan_capabilities.max_scan_plan_interval,
+              kFakeMaxScanPlanIntervals);
+    EXPECT_EQ(scan_capabilities.max_scan_plan_iterations,
+              kFakeMaxScanPlanIterations);
+  } else {
+    EXPECT_EQ(scan_capabilities.max_num_scan_plans, (unsigned int) 0);
+    EXPECT_EQ(scan_capabilities.max_scan_plan_interval, (unsigned int) 0);
+    EXPECT_EQ(scan_capabilities.max_scan_plan_iterations, (unsigned int) 0);
+  }
+}
+
+void VerifyBandInfo(const BandInfo& band_info) {
+  vector<uint32_t> band_2g_expected = {kFakeFrequency1,
+      kFakeFrequency2, kFakeFrequency3};
+  vector<uint32_t> band_5g_expected = {kFakeFrequency4, kFakeFrequency5};
+  vector<uint32_t> band_dfs_expected = {kFakeFrequency6};
+  EXPECT_EQ(band_info.band_2g, band_2g_expected);
+  EXPECT_EQ(band_info.band_5g, band_5g_expected);
+  EXPECT_EQ(band_info.band_dfs, band_dfs_expected);
+}
+
+void VerifyWiphyFeatures(const WiphyFeatures& wiphy_features) {
+  EXPECT_TRUE(wiphy_features.supports_random_mac_oneshot_scan);
+  EXPECT_FALSE(wiphy_features.supports_random_mac_sched_scan);
+}
+
 }  // namespace
 
 class NetlinkUtilsTest : public ::testing::Test {
@@ -314,64 +427,9 @@
   new_wiphy.AddAttribute(NL80211Attr<uint32_t>(NL80211_ATTR_WIPHY,
                                                kFakeWiphyIndex));
 
-  // Insert band information to mock netlink response.
-
-  NL80211NestedAttr freq_2g_1(1);
-  NL80211NestedAttr freq_2g_2(2);
-  NL80211NestedAttr freq_2g_3(3);
-  NL80211NestedAttr freq_5g_1(4);
-  NL80211NestedAttr freq_5g_2(5);
-  NL80211NestedAttr freq_dfs_1(6);
-  freq_2g_1.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
-                                               kFakeFrequency1));
-  freq_2g_2.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
-                                               kFakeFrequency2));
-  freq_2g_3.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
-                                               kFakeFrequency3));
-  freq_5g_1.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
-                                               kFakeFrequency4));
-  freq_5g_2.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
-                                               kFakeFrequency5));
-  // DFS frequency.
-  freq_dfs_1.AddAttribute(NL80211Attr<uint32_t>(NL80211_FREQUENCY_ATTR_FREQ,
-                                                kFakeFrequency6));
-  freq_dfs_1.AddAttribute(NL80211Attr<uint32_t>(
-      NL80211_FREQUENCY_ATTR_DFS_STATE,
-      NL80211_DFS_USABLE));
-
-  NL80211NestedAttr band_2g_freqs(NL80211_BAND_ATTR_FREQS);
-  NL80211NestedAttr band_5g_freqs(NL80211_BAND_ATTR_FREQS);
-  band_2g_freqs.AddAttribute(freq_2g_1);
-  band_2g_freqs.AddAttribute(freq_2g_2);
-  band_2g_freqs.AddAttribute(freq_2g_3);
-  band_5g_freqs.AddAttribute(freq_5g_1);
-  band_5g_freqs.AddAttribute(freq_5g_2);
-  band_5g_freqs.AddAttribute(freq_dfs_1);
-
-  NL80211NestedAttr band_2g_attr(1);
-  NL80211NestedAttr band_5g_attr(2);
-  band_2g_attr.AddAttribute(band_2g_freqs);
-  band_5g_attr.AddAttribute(band_5g_freqs);
-
-  NL80211NestedAttr band_attr(NL80211_ATTR_WIPHY_BANDS);
-  band_attr.AddAttribute(band_2g_attr);
-  band_attr.AddAttribute(band_5g_attr);
-
-  new_wiphy.AddAttribute(band_attr);
-
-  // Insert scan capabilities to mock netlink response.
-  new_wiphy.AddAttribute(NL80211Attr<uint8_t>(NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
-                                              kFakeMaxNumScanSSIDs));
-  new_wiphy.AddAttribute(NL80211Attr<uint8_t>(
-      NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
-      kFakeMaxNumSchedScanSSIDs));
-  new_wiphy.AddAttribute(NL80211Attr<uint8_t>(NL80211_ATTR_MAX_MATCH_SETS,
-                                              kFakeMaxMatchSets));
-
-  // Insert wiphy features to mock netlink response.
-  new_wiphy.AddAttribute(NL80211Attr<uint32_t>(
-      NL80211_ATTR_FEATURE_FLAGS,
-      NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR));
+  AppendBandInfoAttributes(&new_wiphy);
+  AppendScanCapabilitiesAttributes(&new_wiphy, true);
+  AppendWiphyFeaturesAttributes(&new_wiphy);
 
   vector<NL80211Packet> response = {new_wiphy};
 
@@ -385,29 +443,42 @@
                                            &band_info,
                                            &scan_capabilities,
                                            &wiphy_features));
-
-  // Verify band information.
-  vector<uint32_t> band_2g_expected = {kFakeFrequency1,
-      kFakeFrequency2, kFakeFrequency3};
-  vector<uint32_t> band_5g_expected = {kFakeFrequency4, kFakeFrequency5};
-  vector<uint32_t> band_dfs_expected = {kFakeFrequency6};
-  EXPECT_EQ(band_info.band_2g, band_2g_expected);
-  EXPECT_EQ(band_info.band_5g, band_5g_expected);
-  EXPECT_EQ(band_info.band_dfs, band_dfs_expected);
-
-  // Verify scan capabilities.
-  EXPECT_EQ(scan_capabilities.max_num_scan_ssids,
-            kFakeMaxNumScanSSIDs);
-  EXPECT_EQ(scan_capabilities.max_num_sched_scan_ssids,
-            kFakeMaxNumSchedScanSSIDs);
-  EXPECT_EQ(scan_capabilities.max_match_sets,
-            kFakeMaxMatchSets);
-
-  // Verify wiphy features.
-  EXPECT_TRUE(wiphy_features.supports_random_mac_oneshot_scan);
-  EXPECT_FALSE(wiphy_features.supports_random_mac_sched_scan);
+  VerifyBandInfo(band_info);
+  VerifyScanCapabilities(scan_capabilities, true);
+  VerifyWiphyFeatures(wiphy_features);
 }
 
+TEST_F(NetlinkUtilsTest, CanGetWiphyInfoScanPlanNotSupported) {
+  NL80211Packet new_wiphy(
+      netlink_manager_->GetFamilyId(),
+      NL80211_CMD_NEW_WIPHY,
+      netlink_manager_->GetSequenceNumber(),
+      getpid());
+  new_wiphy.AddAttribute(NL80211Attr<uint32_t>(NL80211_ATTR_WIPHY,
+                                               kFakeWiphyIndex));
+
+  AppendBandInfoAttributes(&new_wiphy);
+  AppendScanCapabilitiesAttributes(&new_wiphy, false);
+  AppendWiphyFeaturesAttributes(&new_wiphy);
+
+  vector<NL80211Packet> response = {new_wiphy};
+
+  EXPECT_CALL(*netlink_manager_, SendMessageAndGetResponses(_, _)).
+      WillOnce(DoAll(MakeupResponse(response), Return(true)));
+
+  BandInfo band_info;
+  ScanCapabilities scan_capabilities;
+  WiphyFeatures wiphy_features;
+  EXPECT_TRUE(netlink_utils_->GetWiphyInfo(kFakeWiphyIndex,
+                                           &band_info,
+                                           &scan_capabilities,
+                                           &wiphy_features));
+  VerifyBandInfo(band_info);
+  VerifyScanCapabilities(scan_capabilities, false);
+  VerifyWiphyFeatures(wiphy_features);
+}
+
+
 TEST_F(NetlinkUtilsTest, CanHandleGetWiphyInfoError) {
   // Mock an error response from kernel.
   vector<NL80211Packet> response = {CreateControlMessageError(kFakeErrorCode)};
diff --git a/tests/offload_callback_test.cpp b/tests/offload_callback_test.cpp
new file mode 100644
index 0000000..f55d281
--- /dev/null
+++ b/tests/offload_callback_test.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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 <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "wificond/scanning/offload/offload_callback.h"
+#include "wificond/scanning/scan_result.h"
+#include "wificond/tests/mock_offload_callback_handlers.h"
+#include "wificond/tests/offload_test_utils.h"
+
+using android::hardware::wifi::offload::V1_0::ScanResult;
+using android::hardware::wifi::offload::V1_0::OffloadStatus;
+using android::hardware::wifi::offload::V1_0::OffloadStatusCode;
+using android::hardware::hidl_vec;
+using testing::NiceMock;
+
+namespace android {
+namespace wificond {
+
+class OffloadCallbackTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    dummy_scan_results_ = OffloadTestUtils::createOffloadScanResults();
+  }
+
+  void TearDown() override { dummy_scan_results_.clear(); }
+
+  std::vector<ScanResult> dummy_scan_results_;
+  std::unique_ptr<OffloadCallback> offload_callback_;
+  std::unique_ptr<NiceMock<MockOffloadCallbackHandlers>> handlers_;
+};
+
+/**
+ * Testing OffloadCallback to invoke the registered callback handler
+ * with the scan results when they are available
+ */
+TEST_F(OffloadCallbackTest, checkScanResultSize) {
+  std::vector<ScanResult> scan_result;
+  handlers_.reset(new NiceMock<MockOffloadCallbackHandlers>());
+  ON_CALL(*handlers_, OnScanResultHandler(testing::_))
+      .WillByDefault(testing::Invoke(
+          [&scan_result](std::vector<ScanResult> scanResult) -> void {
+            scan_result = scanResult;
+          }));
+  offload_callback_.reset(new OffloadCallback(handlers_.get()));
+  hidl_vec<ScanResult> offloadScanResult(dummy_scan_results_);
+  offload_callback_->onScanResult(offloadScanResult);
+  EXPECT_EQ(dummy_scan_results_.size(), scan_result.size());
+}
+
+/**
+ * Testing OffloadCallback to invoke the registered error handler
+ */
+TEST_F(OffloadCallbackTest, checkErrorStatus) {
+  OffloadStatus status_;
+  handlers_.reset(new NiceMock<MockOffloadCallbackHandlers>());
+  ON_CALL(*handlers_, OnErrorHandler(testing::_))
+      .WillByDefault(testing::Invoke(
+          [&status_](OffloadStatus status) -> void { status_ = status; }));
+  offload_callback_.reset(new OffloadCallback(handlers_.get()));
+  OffloadStatus status =
+      OffloadTestUtils::createOffloadStatus(OffloadStatusCode::ERROR);
+  offload_callback_->onError(status);
+  EXPECT_EQ(status_.code, OffloadStatusCode::ERROR);
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/offload_hal_test_constants.cpp b/tests/offload_hal_test_constants.cpp
new file mode 100644
index 0000000..79705fe
--- /dev/null
+++ b/tests/offload_hal_test_constants.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "offload_hal_test_constants.h"
+
+namespace android {
+namespace wificond {
+namespace offload_hal_test_constants {
+
+const uint8_t kSsid1[] = {'G', 'o', 'o', 'g', 'l', 'e'};
+const size_t kSsid1_size = sizeof(kSsid1);
+const uint8_t kSsid2[] = {'X', 'f', 'i', 'n', 'i', 't', 'y'};
+const size_t kSsid2_size = sizeof(kSsid2);
+const uint8_t kBssid[6] = {0x12, 0xef, 0xa1, 0x2c, 0x97, 0x8b};
+const int16_t kRssi = -60;
+const int16_t kRssiThreshold = -76;
+const uint32_t kFrequency1 = 2412;
+const uint32_t kFrequency2 = 2437;
+const uint8_t kBssidSize = 6;
+const uint64_t kTsf = 0;
+const uint16_t kCapability = 0;
+const uint8_t kNetworkFlags = 0;
+const uint32_t kDisconnectedModeScanIntervalMs = 5000;
+const uint64_t kSubscriptionDurationMs = 10000;
+const uint64_t kScanDurationMs[2] = {2000, 500};
+const uint32_t kNumChannelsScanned[2] = {14, 6};
+const uint8_t kDefaultNumTimesAChannelsIsScanned = 1;
+const uint8_t kChannelNotScanned = 0;
+const uint32_t kDefaultNumScansRequestedByWifi = 2;
+const uint32_t kDefaultNumScansServicedByWifi = 2;
+const uint64_t kScanDurationTotalMs = 2000;
+const uint32_t kNumChannelsTotalScanned = 20;
+const uint32_t kNumChannelsInHistogram = 256;
+const uint64_t kDeathCode = 0xBEEF;
+
+}  // namespace offload_hal_test_constants
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/offload_hal_test_constants.h b/tests/offload_hal_test_constants.h
new file mode 100644
index 0000000..efc589f
--- /dev/null
+++ b/tests/offload_hal_test_constants.h
@@ -0,0 +1,56 @@
+/*
+ * 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 WIFICOND_OFFLOAD_HAL_TEST_CONSTANTS_H_
+#define WIFICOND_OFFLOAD_HAL_TEST_CONSTANTS_H_
+
+#include <cstdint>
+
+namespace android {
+namespace wificond {
+namespace offload_hal_test_constants {
+
+extern const uint8_t kSsid1[];
+extern const size_t kSsid1_size;
+extern const uint8_t kSsid2[];
+extern const size_t kSsid2_size;
+extern const uint8_t kBssid[6];
+extern const int16_t kRssi;
+extern const int16_t kRssiThreshold;
+extern const uint32_t kFrequency1;
+extern const uint32_t kFrequency2;
+extern const uint8_t kBssidSize;
+extern const uint64_t kTsf;
+extern const uint16_t kCapability;
+extern const uint8_t kNetworkFlags;
+extern const uint32_t kDisconnectedModeScanIntervalMs;
+extern const uint64_t kSubscriptionDurationMs;
+extern const uint64_t kScanDurationMs[2];
+extern const uint32_t kNumChannelsScanned[2];
+extern const uint8_t kDefaultNumTimesAChannelsIsScanned;
+extern const uint8_t kChannelNotScanned;
+extern const uint32_t kDefaultNumScansRequestedByWifi;
+extern const uint32_t kDefaultNumScansServicedByWifi;
+extern const uint64_t kScanDurationTotalMs;
+extern const uint32_t kNumChannelsTotalScanned;
+extern const uint32_t kNumChannelsInHistogram;
+extern const uint64_t kDeathCode;
+
+}  // namespace offload_hal_test_constants
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_OFFLOAD_HAL_TEST_CONSTANTS_H_
diff --git a/tests/offload_scan_manager_test.cpp b/tests/offload_scan_manager_test.cpp
new file mode 100644
index 0000000..2dbf953
--- /dev/null
+++ b/tests/offload_scan_manager_test.cpp
@@ -0,0 +1,480 @@
+/*
+ * 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 <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include <gtest/gtest.h>
+
+#include "wificond/tests/mock_offload.h"
+#include "wificond/tests/mock_offload_service_utils.h"
+#include "wificond/tests/offload_hal_test_constants.h"
+#include "wificond/tests/offload_test_utils.h"
+
+#include "wificond/scanning/offload/offload_callback.h"
+#include "wificond/scanning/offload/offload_callback_handlers.h"
+#include "wificond/scanning/offload/offload_scan_manager.h"
+#include "wificond/scanning/scan_result.h"
+
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::wifi::offload::V1_0::IOffload;
+using android::hardware::wifi::offload::V1_0::ScanResult;
+using android::hardware::wifi::offload::V1_0::OffloadStatus;
+using android::hardware::wifi::offload::V1_0::OffloadStatusCode;
+using android::hardware::wifi::offload::V1_0::ScanParam;
+using android::hardware::wifi::offload::V1_0::ScanFilter;
+using android::hardware::wifi::offload::V1_0::ScanStats;
+using android::sp;
+using com::android::server::wifi::wificond::NativeScanResult;
+using com::android::server::wifi::wificond::NativeScanStats;
+using testing::NiceMock;
+using testing::_;
+using testing::Invoke;
+using std::shared_ptr;
+using std::unique_ptr;
+using std::vector;
+using std::bind;
+
+using namespace std::placeholders;
+using namespace android::wificond::offload_hal_test_constants;
+
+namespace android {
+namespace wificond {
+
+sp<OffloadCallback> CaptureCallback(OffloadCallbackHandlers* handler,
+                                    sp<OffloadCallback>* offload_callback) {
+  *offload_callback = sp<OffloadCallback>(new OffloadCallback(handler));
+  return *offload_callback;
+}
+
+OffloadDeathRecipient* CaptureDeathRecipient(
+    OffloadDeathRecipientHandler handler,
+    sp<OffloadDeathRecipient>* death_recipient_) {
+  OffloadDeathRecipient* death_recipient = new OffloadDeathRecipient(handler);
+  *death_recipient_ = sp<OffloadDeathRecipient>(death_recipient);
+  return death_recipient;
+}
+
+Return<void> HidlStatusReturn(android::wificond::StatusCallback cb,
+                              OffloadStatus* status) {
+  cb(*status);
+  return Void();
+}
+
+Return<void> ScanStatusReturn(android::wificond::OnScanStatsCallback cb,
+                              OffloadStatus* status) {
+  ScanStats stats;
+  cb(*status, stats);
+  return Void();
+}
+
+class OffloadScanManagerTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    ON_CALL(*mock_offload_service_utils_, GetOffloadCallback(_))
+        .WillByDefault(Invoke(bind(CaptureCallback, _1, &offload_callback_)));
+    ON_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_))
+        .WillByDefault(
+            Invoke(bind(CaptureDeathRecipient, _1, &death_recipient_)));
+    status = OffloadTestUtils::createOffloadStatus(OffloadStatusCode::OK);
+    ON_CALL(*mock_offload_, configureScans(_, _, _))
+        .WillByDefault(Invoke(bind(HidlStatusReturn, _3, &status)));
+    ON_CALL(*mock_offload_, subscribeScanResults(_, _))
+        .WillByDefault(Invoke(bind(HidlStatusReturn, _2, &status)));
+    ON_CALL(*mock_offload_, getScanStats(_))
+        .WillByDefault(Invoke(bind(ScanStatusReturn, _1, &status)));
+  }
+
+  void TearDown() override {
+    offload_callback_.clear();
+    death_recipient_.clear();
+  }
+
+  sp<NiceMock<MockOffload>> mock_offload_{new NiceMock<MockOffload>()};
+  sp<OffloadCallback> offload_callback_;
+  sp<OffloadDeathRecipient> death_recipient_;
+  shared_ptr<NiceMock<MockOffloadServiceUtils>> mock_offload_service_utils_{
+      new NiceMock<MockOffloadServiceUtils>()};
+  unique_ptr<OffloadScanManager> offload_scan_manager_;
+  OffloadStatus status;
+  vector<vector<uint8_t>> scan_ssids{kSsid1, kSsid2};
+  vector<vector<uint8_t>> match_ssids{kSsid1, kSsid2};
+  vector<uint8_t> security_flags{kNetworkFlags, kNetworkFlags};
+  vector<uint32_t> frequencies{kFrequency1, kFrequency2};
+  uint64_t cookie_ = reinterpret_cast<uint64_t>(mock_offload_.get());
+};
+
+/**
+ * Testing OffloadScanManager for binder death with registered cookie
+ */
+TEST_F(OffloadScanManagerTest, BinderDeathRegisteredCookieAndService) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  death_recipient_->serviceDied(cookie_, mock_offload_);
+  EXPECT_EQ(OffloadScanManager::kNoService,
+            offload_scan_manager_->getOffloadStatus());
+}
+
+/**
+ * Testing OffloadScanManager for binder death with invalid cookie
+ */
+TEST_F(OffloadScanManagerTest, BinderDeathUnregisteredCookie) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  death_recipient_->serviceDied(kDeathCode, mock_offload_);
+  EXPECT_FALSE(OffloadScanManager::kNoService ==
+               offload_scan_manager_->getOffloadStatus());
+}
+
+/**
+ * Testing OffloadScanManager with no handle on Offloal HAL service
+ * and no registered handler for Offload Scan results
+ */
+TEST_F(OffloadScanManagerTest, ServiceNotAvailableTest) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(nullptr));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_EQ(OffloadScanManager::kNoService,
+            offload_scan_manager_->getOffloadStatus());
+}
+
+/**
+ * Testing OffloadScanManager when service is available and valid handler
+ * registered for Offload Scan results
+ */
+TEST_F(OffloadScanManagerTest, ServiceAvailableTest) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_EQ(OffloadScanManager::kNoError,
+            offload_scan_manager_->getOffloadStatus());
+}
+
+/**
+ * Testing OffloadScanManager when service is available and valid handler
+ * is registered, test to ensure that registered handler is invoked when
+ * scan results are available
+ */
+TEST_F(OffloadScanManagerTest, CallbackInvokedTest) {
+  bool callback_invoked = false;
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [&callback_invoked](vector<NativeScanResult> scanResult) -> void {
+        callback_invoked = true;
+      }));
+  vector<ScanResult> dummy_scan_results_ =
+      OffloadTestUtils::createOffloadScanResults();
+  offload_callback_->onScanResult(dummy_scan_results_);
+  EXPECT_EQ(true, callback_invoked);
+}
+
+/**
+ * Testing OffloadScanManager when service is available and valid handler
+ * is registered, ensure that error callback is invoked
+ */
+TEST_F(OffloadScanManagerTest, ErrorCallbackInvokedTest) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  OffloadStatus status =
+      OffloadTestUtils::createOffloadStatus(OffloadStatusCode::ERROR);
+  offload_callback_->onError(status);
+  EXPECT_EQ(offload_scan_manager_->getOffloadStatus(),
+            OffloadScanManager::kError);
+}
+
+/**
+ * Testing OffloadScanManager for subscribing to the scan results from
+ * Offload HAL when service is running without errors
+ */
+TEST_F(OffloadScanManagerTest, StartScanTestWhenServiceIsOk) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_CALL(*mock_offload_, subscribeScanResults(_, _));
+  EXPECT_CALL(*mock_offload_, configureScans(_, _, _));
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, true);
+}
+
+/**
+ * Testing OffloadScanManager for subscribing to the scan results from
+ * Offload HAL when service is not available
+ */
+TEST_F(OffloadScanManagerTest, StartScanTestWhenServiceIsNotAvailable) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(nullptr));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, false);
+  EXPECT_EQ(reason_code, OffloadScanManager::kNotAvailable);
+}
+
+/**
+ * Testing OffloadScanManager for subscribing to the scan results from
+ * Offload HAL when service is not working correctly
+ */
+TEST_F(OffloadScanManagerTest, StartScanTestWhenServiceIsNotConnected) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  OffloadStatus status =
+      OffloadTestUtils::createOffloadStatus(OffloadStatusCode::NO_CONNECTION);
+  offload_callback_->onError(status);
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, false);
+  EXPECT_EQ(reason_code, OffloadScanManager::kNotAvailable);
+}
+
+/**
+ * Testing OffloadScanManager for subscribing to the scan results from
+ * Offload HAL twice when service is okay
+ */
+TEST_F(OffloadScanManagerTest, StartScanTwiceTestWhenServiceIsOk) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_CALL(*mock_offload_, subscribeScanResults(_, _)).Times(1);
+  EXPECT_CALL(*mock_offload_, configureScans(_, _, _)).Times(2);
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, true);
+  result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, true);
+}
+
+/**
+ * Testing OffloadScanManager for unsubscribing to the scan results from
+ * Offload HAL when service is ok
+ */
+TEST_F(OffloadScanManagerTest, StopScanTestWhenServiceIsOk) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_CALL(*mock_offload_, subscribeScanResults(_, _));
+  EXPECT_CALL(*mock_offload_, configureScans(_, _, _));
+  EXPECT_CALL(*mock_offload_, unsubscribeScanResults());
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, true);
+  result = offload_scan_manager_->stopScan(&reason_code);
+  EXPECT_EQ(result, true);
+}
+
+/**
+ * Testing OffloadScanManager for unsubscribing to the scan results from
+ * Offload HAL without first subscribing
+ */
+TEST_F(OffloadScanManagerTest, StopScanTestWithoutStartWhenServiceIsOk) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->stopScan(&reason_code);
+  EXPECT_EQ(result, false);
+  EXPECT_EQ(reason_code, OffloadScanManager::kNotSubscribed);
+}
+
+/**
+ * Testing OffloadScanManager for unsubscribing to the scan results from
+ * Offload HAL without first subscribing when service is not working correctly
+ */
+TEST_F(OffloadScanManagerTest, StopScanTestWhenServiceIsNotConnectedAnymore) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_CALL(*mock_offload_, subscribeScanResults(_, _));
+  EXPECT_CALL(*mock_offload_, configureScans(_, _, _));
+  EXPECT_CALL(*mock_offload_, unsubscribeScanResults());
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, true);
+  OffloadStatus status =
+      OffloadTestUtils::createOffloadStatus(OffloadStatusCode::NO_CONNECTION);
+  offload_callback_->onError(status);
+  result = offload_scan_manager_->stopScan(&reason_code);
+  EXPECT_EQ(result, true);
+}
+
+/**
+ * Testing OffloadScanManager for getting scan statistics when the
+ * Offload HAL service is running without errors
+ */
+TEST_F(OffloadScanManagerTest, getScanStatsTestWhenServiceIsOk) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_CALL(*mock_offload_, getScanStats(_));
+  NativeScanStats stats;
+  bool result = offload_scan_manager_->getScanStats(&stats);
+  EXPECT_EQ(result, true);
+}
+
+/**
+ * Testing OffloadScanManager for getting scan statistics when the
+ * Offload HAL service is not connected
+ */
+TEST_F(OffloadScanManagerTest, getScanStatsTestWhenServiceIsNotOk) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  OffloadStatus status =
+      OffloadTestUtils::createOffloadStatus(OffloadStatusCode::NO_CONNECTION);
+  offload_callback_->onError(status);
+  EXPECT_CALL(*mock_offload_, getScanStats(_)).Times(0);
+  NativeScanStats stats;
+  bool result = offload_scan_manager_->getScanStats(&stats);
+  EXPECT_EQ(result, false);
+}
+
+/**
+ * Testing OffloadScanManager for subscribing to the scan results from
+ * Offload HAL when service is running without errors, operation failure
+ */
+TEST_F(OffloadScanManagerTest, StartScanFailedTest) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  EXPECT_CALL(*mock_offload_, subscribeScanResults(_, _)).Times(0);
+  EXPECT_CALL(*mock_offload_, configureScans(_, _, _)).Times(1);
+  status = OffloadTestUtils::createOffloadStatus(OffloadStatusCode::ERROR);
+  OffloadScanManager::ReasonCode reason_code = OffloadScanManager::kNone;
+  bool result = offload_scan_manager_->startScan(
+      kDisconnectedModeScanIntervalMs, kRssiThreshold, scan_ssids, match_ssids,
+      security_flags, frequencies, &reason_code);
+  EXPECT_EQ(result, false);
+  EXPECT_EQ(reason_code, OffloadScanManager::kOperationFailed);
+}
+
+/**
+ * Testing OffloadScanManager for getting scan statistics when the
+ * Offload HAL service is running without errors, getting scan stats failure
+ */
+TEST_F(OffloadScanManagerTest, getScanStatsFailedTest) {
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadService());
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadCallback(_));
+  EXPECT_CALL(*mock_offload_service_utils_, GetOffloadDeathRecipient(_));
+  ON_CALL(*mock_offload_service_utils_, GetOffloadService())
+      .WillByDefault(testing::Return(mock_offload_));
+  offload_scan_manager_.reset(new OffloadScanManager(
+      mock_offload_service_utils_,
+      [](vector<NativeScanResult> scanResult) -> void {}));
+  status = OffloadTestUtils::createOffloadStatus(OffloadStatusCode::TIMEOUT);
+  EXPECT_CALL(*mock_offload_, getScanStats(_));
+  NativeScanStats stats;
+  bool result = offload_scan_manager_->getScanStats(&stats);
+  EXPECT_EQ(result, false);
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/offload_scan_utils_test.cpp b/tests/offload_scan_utils_test.cpp
new file mode 100644
index 0000000..d75143a
--- /dev/null
+++ b/tests/offload_scan_utils_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 <vector>
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include "wificond/scanning/offload/offload_scan_utils.h"
+#include "wificond/scanning/scan_result.h"
+#include "wificond/tests/offload_hal_test_constants.h"
+#include "wificond/tests/offload_test_utils.h"
+
+using android::hardware::wifi::offload::V1_0::ScanResult;
+using android::hardware::wifi::offload::V1_0::ScanParam;
+using android::hardware::wifi::offload::V1_0::ScanFilter;
+using android::hardware::wifi::offload::V1_0::NetworkInfo;
+using android::hardware::wifi::offload::V1_0::ScanRecord;
+using android::hardware::wifi::offload::V1_0::ScanStats;
+using ::com::android::server::wifi::wificond::NativeScanResult;
+using ::com::android::server::wifi::wificond::NativeScanStats;
+using std::vector;
+
+using namespace android::wificond::offload_hal_test_constants;
+
+namespace android {
+namespace wificond {
+
+class OffloadScanUtilsTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    dummy_scan_results_ = OffloadTestUtils::createOffloadScanResults();
+  }
+
+  void TearDown() override { dummy_scan_results_.clear(); }
+
+  vector<ScanResult> dummy_scan_results_;
+};
+
+TEST_F(OffloadScanUtilsTest, verifyConversion) {
+  vector<NativeScanResult> native_scan_results =
+      OffloadScanUtils::convertToNativeScanResults(dummy_scan_results_);
+  EXPECT_EQ(native_scan_results.size(), dummy_scan_results_.size());
+  for (size_t i = 0; i < native_scan_results.size(); i++) {
+    EXPECT_EQ(native_scan_results[i].frequency,
+              dummy_scan_results_[i].frequency);
+    EXPECT_EQ(native_scan_results[i].tsf, dummy_scan_results_[i].tsf);
+    EXPECT_EQ(native_scan_results[i].signal_mbm, dummy_scan_results_[i].rssi);
+    EXPECT_EQ(native_scan_results[i].ssid.size(),
+              dummy_scan_results_[i].networkInfo.ssid.size());
+    EXPECT_EQ(native_scan_results[i].bssid.size(),
+              dummy_scan_results_[i].bssid.elementCount());
+    EXPECT_EQ(native_scan_results[i].capability,
+              dummy_scan_results_[i].capability);
+  }
+}
+
+TEST_F(OffloadScanUtilsTest, verifyScanParam) {
+  vector<vector<uint8_t>> scan_ssids{kSsid1, kSsid2};
+  vector<uint32_t> frequencies{kFrequency1, kFrequency2};
+  ScanParam scanParam = OffloadScanUtils::createScanParam(
+      scan_ssids, frequencies, kDisconnectedModeScanIntervalMs);
+  EXPECT_EQ(scanParam.disconnectedModeScanIntervalMs,
+            kDisconnectedModeScanIntervalMs);
+  for (size_t i = 0; i < frequencies.size(); i++) {
+    EXPECT_EQ(scanParam.frequencyList[i], frequencies[i]);
+  }
+  for (size_t j = 0; j < scan_ssids.size(); j++) {
+    vector<uint8_t> ssid_result = scanParam.ssidList[j];
+    vector<uint8_t> ssid_input = scan_ssids[j];
+    for (size_t k = 0; k < ssid_result.size(); k++) {
+      EXPECT_EQ(ssid_result[k], ssid_input[k]);
+    }
+  }
+}
+
+TEST_F(OffloadScanUtilsTest, verifyScanFilter) {
+  vector<vector<uint8_t>> match_ssids{kSsid1, kSsid2};
+  vector<uint8_t> security_flags{kNetworkFlags, kNetworkFlags};
+  ScanFilter scanFilter = OffloadScanUtils::createScanFilter(
+      match_ssids, security_flags, kRssiThreshold);
+  EXPECT_EQ(kRssiThreshold, scanFilter.rssiThreshold);
+  EXPECT_FALSE(scanFilter.preferredNetworkInfoList.size() == 0);
+  for (size_t i = 0; i < security_flags.size(); ++i) {
+    NetworkInfo nwInfo = scanFilter.preferredNetworkInfoList[i];
+    vector<uint8_t> ssid = nwInfo.ssid;
+    vector<uint8_t> match_ssid = match_ssids[i];
+    EXPECT_EQ(nwInfo.flags, security_flags[i]);
+    for (size_t j = 0; j < ssid.size(); j++) {
+      EXPECT_EQ(ssid[j], match_ssid[j]);
+    }
+  }
+}
+
+TEST_F(OffloadScanUtilsTest, verifyScanStats) {
+  NativeScanStats stats_expected;
+  ScanStats offload_scan_stats =
+      OffloadTestUtils::createScanStats(&stats_expected);
+  NativeScanStats stats_returned =
+      OffloadScanUtils::convertToNativeScanStats(offload_scan_stats);
+  EXPECT_TRUE(stats_expected == stats_returned);
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/offload_test_utils.cpp b/tests/offload_test_utils.cpp
new file mode 100644
index 0000000..2b2acd8
--- /dev/null
+++ b/tests/offload_test_utils.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 <vector>
+
+#include "wificond/tests/offload_hal_test_constants.h"
+#include "wificond/tests/offload_test_utils.h"
+
+using android::hardware::wifi::offload::V1_0::ScanResult;
+using android::hardware::wifi::offload::V1_0::ScanStats;
+using android::hardware::wifi::offload::V1_0::ScanRecord;
+using android::hardware::wifi::offload::V1_0::OffloadStatus;
+using android::hardware::wifi::offload::V1_0::OffloadStatusCode;
+
+using ::com::android::server::wifi::wificond::NativeScanResult;
+using ::com::android::server::wifi::wificond::NativeScanStats;
+
+using namespace android::wificond::offload_hal_test_constants;
+
+namespace android {
+namespace wificond {
+
+std::vector<ScanResult> OffloadTestUtils::createOffloadScanResults() {
+  std::vector<ScanResult> scanResults;
+  ScanResult scanResult;
+  std::vector<uint8_t> ssid(kSsid1, kSsid1 + kSsid1_size);
+  scanResult.tsf = kTsf;
+  scanResult.rssi = kRssi;
+  scanResult.frequency = kFrequency1;
+  scanResult.capability = kCapability;
+  memcpy(&scanResult.bssid[0], &kBssid[0], kBssidSize);
+  scanResult.networkInfo.ssid = ssid;
+  scanResult.networkInfo.flags = kNetworkFlags;
+  scanResults.push_back(scanResult);
+  return scanResults;
+}
+
+ScanStats OffloadTestUtils::createScanStats(NativeScanStats* nativeScanStats) {
+  std::vector<ScanRecord> scan_records;
+  std::vector<uint8_t> histogram_channels;
+  uint32_t scan_duration_ms = 0;
+  uint32_t num_channels_scanned = 0;
+  ScanStats scan_stats;
+  int numEntriesInScanRecord =
+      sizeof(kNumChannelsScanned) / sizeof(kNumChannelsScanned[0]);
+  for (int i = 0; i < numEntriesInScanRecord; i++) {
+    ScanRecord scan_record;
+    scan_record.durationMs = kScanDurationMs[i];
+    scan_duration_ms += kScanDurationMs[i];
+    scan_record.numChannelsScanned = kNumChannelsScanned[i];
+    num_channels_scanned += kNumChannelsScanned[i];
+    scan_record.numEntriesAggregated = 1;
+    scan_records.push_back(scan_record);
+  }
+  scan_stats.scanRecord = scan_records;
+  scan_stats.numScansRequestedByWifi = kDefaultNumScansRequestedByWifi;
+  scan_stats.numScansServicedByWifi = kDefaultNumScansServicedByWifi;
+  scan_stats.subscriptionDurationMs = kSubscriptionDurationMs;
+  uint32_t skip_tmp = 256 / num_channels_scanned;
+  for (size_t i = 0; i < 256; i++) {
+    if (i % skip_tmp == 0) {
+      scan_stats.histogramChannelsScanned[i] =
+          kDefaultNumTimesAChannelsIsScanned;
+      histogram_channels.push_back(kDefaultNumTimesAChannelsIsScanned);
+    } else {
+      scan_stats.histogramChannelsScanned[i] = kChannelNotScanned;
+      histogram_channels.push_back(kChannelNotScanned);
+    }
+  }
+  NativeScanStats native_scan_stats(kDefaultNumScansRequestedByWifi,
+                                    kDefaultNumScansServicedByWifi,
+                                    kSubscriptionDurationMs, scan_duration_ms,
+                                    num_channels_scanned, histogram_channels);
+  *nativeScanStats = native_scan_stats;
+  return scan_stats;
+}
+
+OffloadStatus OffloadTestUtils::createOffloadStatus(OffloadStatusCode code) {
+  return createOffloadStatus(code, "");
+}
+
+OffloadStatus OffloadTestUtils::createOffloadStatus(OffloadStatusCode code,
+                                                    const std::string& desc) {
+  return {code, desc};
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/offload_test_utils.h b/tests/offload_test_utils.h
new file mode 100644
index 0000000..f2370e2
--- /dev/null
+++ b/tests/offload_test_utils.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFICOND_OFFLOAD_TEST_UTILS_H_
+#define WIFICOND_OFFLOAD_TEST_UTILS_H_
+
+#include <android/hardware/wifi/offload/1.0/IOffload.h>
+#include <vector>
+
+#include "wificond/scanning/offload/scan_stats.h"
+#include "wificond/scanning/scan_result.h"
+
+namespace android {
+namespace wificond {
+
+class OffloadTestUtils {
+ public:
+  static std::vector<android::hardware::wifi::offload::V1_0::ScanResult>
+      createOffloadScanResults();
+  static android::hardware::wifi::offload::V1_0::ScanStats createScanStats(
+      ::com::android::server::wifi::wificond::
+          NativeScanStats* /* nativeScanStats */);
+  static android::hardware::wifi::offload::V1_0::OffloadStatus
+      createOffloadStatus(
+          android::hardware::wifi::offload::V1_0::OffloadStatusCode code);
+  static android::hardware::wifi::offload::V1_0::OffloadStatus
+      createOffloadStatus(
+          android::hardware::wifi::offload::V1_0::OffloadStatusCode code,
+          const std::string& desc);
+};
+
+}  // namespace wificond
+}  // namespace android
+
+#endif  // WIFICOND_OFFLOAD_TEST_UTILS_H
diff --git a/tests/scan_stats_unittest.cpp b/tests/scan_stats_unittest.cpp
new file mode 100644
index 0000000..6eb72d6
--- /dev/null
+++ b/tests/scan_stats_unittest.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 <vector>
+
+#include <gtest/gtest.h>
+
+#include "wificond/scanning/offload/scan_stats.h"
+#include "wificond/tests/offload_hal_test_constants.h"
+
+using ::com::android::server::wifi::wificond::NativeScanStats;
+using namespace android::wificond::offload_hal_test_constants;
+
+namespace android {
+namespace wificond {
+
+class ScanStatsTest : public ::testing::Test {};
+
+TEST_F(ScanStatsTest, ParcelableTest) {
+  std::vector<uint8_t> histogram_channels;
+  for (size_t i = 0; i < kNumChannelsInHistogram; i++) {
+    histogram_channels.push_back(kNumChannelsInHistogram - 1 - i);
+  }
+  NativeScanStats scan_stats_in(kDefaultNumScansRequestedByWifi,
+                                kDefaultNumScansServicedByWifi,
+                                kScanDurationTotalMs, kSubscriptionDurationMs,
+                                kNumChannelsTotalScanned, histogram_channels);
+  Parcel parcel;
+  EXPECT_EQ(::android::OK, scan_stats_in.writeToParcel(&parcel));
+  NativeScanStats scan_stats_out;
+  parcel.setDataPosition(0);
+  EXPECT_EQ(::android::OK, scan_stats_out.readFromParcel(&parcel));
+  EXPECT_TRUE(scan_stats_in == scan_stats_out);
+}
+
+}  // namespace wificond
+}  // namespace android
diff --git a/tests/scan_utils_unittest.cpp b/tests/scan_utils_unittest.cpp
index 3dbfe21..28ea283 100644
--- a/tests/scan_utils_unittest.cpp
+++ b/tests/scan_utils_unittest.cpp
@@ -32,8 +32,10 @@
 using std::placeholders::_2;
 using std::unique_ptr;
 using std::vector;
+using testing::AllOf;
 using testing::Invoke;
 using testing::NiceMock;
+using testing::Not;
 using testing::Return;
 using testing::_;
 
@@ -106,6 +108,11 @@
   return arg.GetCommand() == command;
 }
 
+MATCHER_P(DoesNL80211PacketHaveAttribute, attr,
+          "Check if the netlink packet has atttribute |attr|") {
+  return arg.HasAttribute(attr);
+}
+
 TEST_F(ScanUtilsTest, CanGetScanResult) {
   vector<NativeScanResult> scan_results;
   EXPECT_CALL(
@@ -130,7 +137,9 @@
               WillOnce(Invoke(bind(
                   AppendMessageAndReturn, response, true, _1, _2)));
 
-  EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, kFakeUseRandomMAC, {}, {}));
+  int errno_ignored;
+  EXPECT_TRUE(scan_utils_.Scan(kFakeInterfaceIndex, kFakeUseRandomMAC, {}, {},
+                               &errno_ignored));
   // TODO(b/34231420): Add validation of requested scan ssids, threshold,
   // and frequencies.
 }
@@ -143,7 +152,10 @@
           DoesNL80211PacketMatchCommand(NL80211_CMD_TRIGGER_SCAN), _)).
               WillOnce(Invoke(bind(
                   AppendMessageAndReturn, response, true, _1, _2)));
-  EXPECT_FALSE(scan_utils_.Scan(kFakeInterfaceIndex, kFakeUseRandomMAC, {}, {}));
+  int error_code;
+  EXPECT_FALSE(scan_utils_.Scan(kFakeInterfaceIndex, kFakeUseRandomMAC, {}, {},
+                                &error_code));
+  EXPECT_EQ(kFakeErrorCode, error_code);
 }
 
 TEST_F(ScanUtilsTest, CanSendSchedScanRequest) {
@@ -154,10 +166,11 @@
            DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), _)).
               WillOnce(Invoke(bind(
                   AppendMessageAndReturn, response, true, _1, _2)));
+  int errno_ignored;
   EXPECT_TRUE(scan_utils_.StartScheduledScan(
       kFakeInterfaceIndex,
-      kFakeScheduledScanIntervalMs,
-      kFakeRssiThreshold, kFakeUseRandomMAC, {}, {}, {}));
+      SchedScanIntervalSetting(),
+      kFakeRssiThreshold, kFakeUseRandomMAC, {}, {}, {}, &errno_ignored));
   // TODO(b/34231420): Add validation of requested scan ssids, threshold,
   // and frequencies.
 }
@@ -170,10 +183,112 @@
            DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN), _)).
               WillOnce(Invoke(bind(
                   AppendMessageAndReturn, response, true, _1, _2)));
+  int error_code;
   EXPECT_FALSE(scan_utils_.StartScheduledScan(
       kFakeInterfaceIndex,
-      kFakeScheduledScanIntervalMs,
-      kFakeRssiThreshold, kFakeUseRandomMAC, {}, {}, {}));
+      SchedScanIntervalSetting(),
+      kFakeRssiThreshold, kFakeUseRandomMAC, {}, {}, {}, &error_code));
+  EXPECT_EQ(kFakeErrorCode, error_code);
+}
+
+TEST_F(ScanUtilsTest, CanSpecifyScanPlansForSchedScanRequest) {
+  EXPECT_CALL(
+      netlink_manager_,
+       SendMessageAndGetResponses(
+           AllOf(
+               DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN),
+               DoesNL80211PacketHaveAttribute(NL80211_ATTR_SCHED_SCAN_PLANS),
+               Not(DoesNL80211PacketHaveAttribute(
+                   NL80211_ATTR_SCHED_SCAN_INTERVAL))),
+           _));
+  int errno_ignored;
+  SchedScanIntervalSetting interval_setting{
+      {{kFakeScheduledScanIntervalMs, 10 /* repeated times */}},
+      kFakeScheduledScanIntervalMs * 3 /* interval for infinite scans */};
+
+  scan_utils_.StartScheduledScan(
+      kFakeInterfaceIndex,
+      interval_setting,
+      kFakeRssiThreshold, kFakeUseRandomMAC, {}, {}, {}, &errno_ignored);
+}
+
+TEST_F(ScanUtilsTest, CanSpecifySingleIntervalForSchedScanRequest) {
+  EXPECT_CALL(
+      netlink_manager_,
+       SendMessageAndGetResponses(
+           AllOf(
+               DoesNL80211PacketMatchCommand(NL80211_CMD_START_SCHED_SCAN),
+               DoesNL80211PacketHaveAttribute(NL80211_ATTR_SCHED_SCAN_INTERVAL),
+               Not(DoesNL80211PacketHaveAttribute(
+                   NL80211_ATTR_SCHED_SCAN_PLANS))),
+           _));
+  int errno_ignored;
+  SchedScanIntervalSetting interval_setting{{}, kFakeScheduledScanIntervalMs};
+
+  scan_utils_.StartScheduledScan(
+      kFakeInterfaceIndex,
+      interval_setting,
+      kFakeRssiThreshold, kFakeUseRandomMAC, {}, {}, {}, &errno_ignored);
+}
+
+TEST_F(ScanUtilsTest, CanPrioritizeLastSeenSinceBootNetlinkAttribute) {
+  constexpr uint64_t kLastSeenTimestampNanoSeconds = 123456;
+  constexpr uint64_t kBssTsfTimestampMicroSeconds = 654321;
+  NL80211NestedAttr bss(NL80211_ATTR_BSS);
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_LAST_SEEN_BOOTTIME,
+                            kLastSeenTimestampNanoSeconds));
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds));
+  uint64_t timestamp_microseconds;
+  EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting(
+      bss, &timestamp_microseconds));
+  EXPECT_EQ(kLastSeenTimestampNanoSeconds/1000, timestamp_microseconds);
+}
+
+TEST_F(ScanUtilsTest, CanHandleMissingLastSeenSinceBootNetlinkAttribute) {
+  constexpr uint64_t kBssTsfTimestampMicroSeconds = 654321;
+  NL80211NestedAttr bss(NL80211_ATTR_BSS);
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds));
+  uint64_t timestamp_microseconds;
+  EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting(
+      bss, &timestamp_microseconds));
+  EXPECT_EQ(kBssTsfTimestampMicroSeconds, timestamp_microseconds);
+}
+
+// Probe TSF is newer.
+TEST_F(ScanUtilsTest, CanPickMostRecentTimestampBetweenBetweenProbeAndBeacon1) {
+  constexpr uint64_t kBssBeaconTsfTimestampMicroSeconds = 654321;
+  constexpr uint64_t kBssTsfTimestampMicroSeconds =
+      kBssBeaconTsfTimestampMicroSeconds + 2000;
+  NL80211NestedAttr bss(NL80211_ATTR_BSS);
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_BEACON_TSF,
+                            kBssBeaconTsfTimestampMicroSeconds));
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds));
+  uint64_t timestamp_microseconds;
+  EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting(
+      bss, &timestamp_microseconds));
+  EXPECT_EQ(kBssTsfTimestampMicroSeconds, timestamp_microseconds);
+}
+
+// Beacon TSF is newer.
+TEST_F(ScanUtilsTest, CanPickMostRecentTimestampBetweenBetweenProbeAndBeacon2) {
+  constexpr uint64_t kBssTsfTimestampMicroSeconds = 654321;
+  constexpr uint64_t kBssBeaconTsfTimestampMicroSeconds =
+      kBssTsfTimestampMicroSeconds + 2000;
+  NL80211NestedAttr bss(NL80211_ATTR_BSS);
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_BEACON_TSF,
+                            kBssBeaconTsfTimestampMicroSeconds));
+  bss.AddAttribute(
+      NL80211Attr<uint64_t>(NL80211_BSS_TSF, kBssTsfTimestampMicroSeconds));
+  uint64_t timestamp_microseconds;
+  EXPECT_TRUE(scan_utils_.GetBssTimestampForTesting(
+      bss, &timestamp_microseconds));
+  EXPECT_EQ(kBssBeaconTsfTimestampMicroSeconds, timestamp_microseconds);
 }
 
 }  // namespace wificond
diff --git a/tests/scanner_unittest.cpp b/tests/scanner_unittest.cpp
new file mode 100644
index 0000000..bbaeffd
--- /dev/null
+++ b/tests/scanner_unittest.cpp
@@ -0,0 +1,253 @@
+/*
+ * 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 <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <wifi_system_test/mock_interface_tool.h>
+#include <wifi_system_test/mock_supplicant_manager.h>
+
+#include "wificond/scanning/scanner_impl.h"
+#include "wificond/tests/mock_client_interface_impl.h"
+#include "wificond/tests/mock_netlink_manager.h"
+#include "wificond/tests/mock_netlink_utils.h"
+#include "wificond/tests/mock_offload_service_utils.h"
+#include "wificond/tests/mock_scan_utils.h"
+
+using ::android::binder::Status;
+using ::android::wifi_system::MockInterfaceTool;
+using ::android::wifi_system::MockSupplicantManager;
+using ::com::android::server::wifi::wificond::SingleScanSettings;
+using ::com::android::server::wifi::wificond::PnoSettings;
+using ::com::android::server::wifi::wificond::NativeScanResult;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::_;
+using std::shared_ptr;
+using std::unique_ptr;
+using std::vector;
+
+using namespace std::placeholders;
+
+namespace android {
+namespace wificond {
+
+namespace {
+
+constexpr uint32_t kFakeInterfaceIndex = 12;
+constexpr uint32_t kFakeWiphyIndex = 5;
+constexpr uint32_t kFakeScanIntervalMs = 10000;
+
+// This is a helper function to mock the behavior of ScanUtils::Scan()
+// when we expect a error code.
+// |interface_index_ignored|, |request_random_mac_ignored|, |ssids_ignored|,
+// |freqs_ignored|, |error_code| are mapped to existing parameters of ScanUtils::Scan().
+// |mock_error_code| is a additional parameter used for specifying expected error code.
+bool ReturnErrorCodeForScanRequest(
+    int mock_error_code,
+    uint32_t interface_index_ignored,
+    bool request_random_mac_ignored,
+    const std::vector<std::vector<uint8_t>>& ssids_ignored,
+    const std::vector<uint32_t>& freqs_ignored,
+    int* error_code) {
+  *error_code = mock_error_code;
+  // Returing false because this helper function is used for failure case.
+  return false;
+}
+
+bool CaptureSchedScanIntervalSetting(
+    uint32_t /* interface_index */,
+    const SchedScanIntervalSetting&  interval_setting,
+    int32_t /* rssi_threshold */,
+    bool /* request_random_mac */,
+    const  std::vector<std::vector<uint8_t>>& /* scan_ssids */,
+    const std::vector<std::vector<uint8_t>>& /* match_ssids */,
+    const  std::vector<uint32_t>& /* freqs */,
+    int* /* error_code */,
+    SchedScanIntervalSetting* out_interval_setting) {
+  *out_interval_setting = interval_setting;
+  return true;
+}
+
+}  // namespace
+
+class ScannerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+   ON_CALL(*offload_service_utils_, IsOffloadScanSupported()).WillByDefault(
+      Return(false));
+   netlink_scanner_.reset(new ScannerImpl(
+       kFakeWiphyIndex, kFakeInterfaceIndex,
+       scan_capabilities_, wiphy_features_,
+       &client_interface_impl_,
+       &netlink_utils_, &scan_utils_, offload_service_utils_));
+  }
+
+  unique_ptr<ScannerImpl> netlink_scanner_;
+  NiceMock<MockNetlinkManager> netlink_manager_;
+  NiceMock<MockNetlinkUtils> netlink_utils_{&netlink_manager_};
+  NiceMock<MockScanUtils> scan_utils_{&netlink_manager_};
+  NiceMock<MockInterfaceTool> if_tool_;
+  NiceMock<MockSupplicantManager> supplicant_manager_;
+  NiceMock<MockClientInterfaceImpl> client_interface_impl_{
+      &if_tool_, &supplicant_manager_, &netlink_utils_, &scan_utils_};
+  shared_ptr<NiceMock<MockOffloadServiceUtils>> offload_service_utils_{
+      new NiceMock<MockOffloadServiceUtils>()};
+  ScanCapabilities scan_capabilities_;
+  WiphyFeatures wiphy_features_;
+};
+
+TEST_F(ScannerTest, TestSingleScan) {
+  EXPECT_CALL(scan_utils_, Scan(_, _, _, _, _)).WillOnce(Return(true));
+  bool success = false;
+  EXPECT_TRUE(netlink_scanner_->scan(SingleScanSettings(), &success).isOk());
+  EXPECT_TRUE(success);
+}
+
+TEST_F(ScannerTest, TestSingleScanFailure) {
+  EXPECT_CALL(
+      scan_utils_,
+      Scan(_, _, _, _, _)).
+          WillOnce(Invoke(bind(
+              ReturnErrorCodeForScanRequest, EBUSY, _1, _2, _3, _4, _5)));
+
+  bool success = false;
+  EXPECT_TRUE(netlink_scanner_->scan(SingleScanSettings(), &success).isOk());
+  EXPECT_FALSE(success);
+}
+
+TEST_F(ScannerTest, TestProcessAbortsOnScanReturningNoDeviceError) {
+  ON_CALL(
+      scan_utils_,
+      Scan(_, _, _, _, _)).
+          WillByDefault(Invoke(bind(
+              ReturnErrorCodeForScanRequest, ENODEV, _1, _2, _3, _4, _5)));
+
+  bool success_ignored;
+  EXPECT_DEATH(
+      netlink_scanner_->scan(SingleScanSettings(), &success_ignored),
+      "Driver is in a bad state*");
+}
+
+TEST_F(ScannerTest, TestAbortScan) {
+  bool single_scan_success = false;
+  EXPECT_CALL(scan_utils_, Scan(_, _, _, _, _)).WillOnce(Return(true));
+  EXPECT_TRUE(netlink_scanner_->scan(SingleScanSettings(),
+                            &single_scan_success).isOk());
+  EXPECT_TRUE(single_scan_success);
+
+  EXPECT_CALL(scan_utils_, AbortScan(_));
+  EXPECT_TRUE(netlink_scanner_->abortScan().isOk());
+}
+
+TEST_F(ScannerTest, TestAbortScanNotIssuedIfNoOngoingScan) {
+  EXPECT_CALL(scan_utils_, AbortScan(_)).Times(0);
+  EXPECT_TRUE(netlink_scanner_->abortScan().isOk());
+}
+
+TEST_F(ScannerTest, TestGetScanResults) {
+  vector<NativeScanResult> scan_results;
+  EXPECT_CALL(scan_utils_, GetScanResult(_, _)).WillOnce(Return(true));
+  EXPECT_TRUE(netlink_scanner_->getScanResults(&scan_results).isOk());
+}
+
+TEST_F(ScannerTest, TestStartPnoScanViaNetlink) {
+  bool success = false;
+  EXPECT_CALL(scan_utils_, StartScheduledScan(_, _, _, _, _, _, _, _)).
+              WillOnce(Return(true));
+  EXPECT_TRUE(netlink_scanner_->startPnoScan(PnoSettings(), &success).isOk());
+  EXPECT_TRUE(success);
+}
+
+TEST_F(ScannerTest, TestStopPnoScanViaNetlink) {
+  bool success = false;
+  // StopScheduledScan() will be called no matter if there is an ongoing
+  // scheduled scan or not. This is for making the system more robust.
+  EXPECT_CALL(scan_utils_, StopScheduledScan(_)).WillOnce(Return(true));
+  EXPECT_TRUE(netlink_scanner_->stopPnoScan(&success).isOk());
+  EXPECT_TRUE(success);
+}
+
+TEST_F(ScannerTest, TestGenerateScanPlansIfDeviceSupports) {
+  ScanCapabilities scan_capabilities_scan_plan_supported(
+      0 /* max_num_scan_ssids */,
+      0 /* max_num_sched_scan_ssids */,
+      0 /* max_match_sets */,
+      // Parameters above are not related to this test.
+      2 /* 1 plan for finite repeated scan and 1 plan for ininfite scan loop */,
+      kFakeScanIntervalMs * PnoSettings::kSlowScanIntervalMultiplier / 1000,
+      PnoSettings::kFastScanIterations);
+  ScannerImpl scanner(
+      kFakeWiphyIndex, kFakeInterfaceIndex,
+      scan_capabilities_scan_plan_supported, wiphy_features_,
+      &client_interface_impl_,
+      &netlink_utils_, &scan_utils_, offload_service_utils_);
+
+  PnoSettings pno_settings;
+  pno_settings.interval_ms_ = kFakeScanIntervalMs;
+
+  SchedScanIntervalSetting interval_setting;
+  EXPECT_CALL(
+      scan_utils_,
+      StartScheduledScan(_, _, _, _, _, _, _, _)).
+              WillOnce(Invoke(bind(
+                  CaptureSchedScanIntervalSetting,
+                  _1, _2, _3, _4, _5, _6, _7, _8, &interval_setting)));
+
+  bool success_ignored = 0;
+  EXPECT_TRUE(scanner.startPnoScan(pno_settings, &success_ignored).isOk());
+  /* 1 plan for finite repeated scan */
+  EXPECT_EQ(1U, interval_setting.plans.size());
+  EXPECT_EQ(kFakeScanIntervalMs * PnoSettings::kSlowScanIntervalMultiplier,
+            interval_setting.final_interval_ms);
+}
+
+TEST_F(ScannerTest, TestGenerateSingleIntervalIfDeviceDoesNotSupportScanPlan) {
+  ScanCapabilities scan_capabilities_no_scan_plan_support(
+      0 /* max_num_scan_ssids */,
+      0 /* max_num_sched_scan_ssids */,
+      0 /* max_match_sets */,
+      // Parameters above are not related to this test.
+      0 /* max_num_scan_plans */,
+      0 /* max_scan_plan_interval */,
+      0 /* max_scan_plan_iterations */);
+  ScannerImpl scanner(
+      kFakeWiphyIndex, kFakeInterfaceIndex,
+      scan_capabilities_no_scan_plan_support, wiphy_features_,
+      &client_interface_impl_,
+      &netlink_utils_, &scan_utils_, offload_service_utils_);
+  PnoSettings pno_settings;
+  pno_settings.interval_ms_ = kFakeScanIntervalMs;
+
+  SchedScanIntervalSetting interval_setting;
+  EXPECT_CALL(
+      scan_utils_,
+      StartScheduledScan(_, _, _, _, _, _, _, _)).
+              WillOnce(Invoke(bind(
+                  CaptureSchedScanIntervalSetting,
+                  _1, _2, _3, _4, _5, _6, _7, _8, &interval_setting)));
+
+  bool success_ignored = 0;
+  EXPECT_TRUE(scanner.startPnoScan(pno_settings, &success_ignored).isOk());
+
+  EXPECT_EQ(0U, interval_setting.plans.size());
+  EXPECT_EQ(kFakeScanIntervalMs, interval_setting.final_interval_ms);
+}
+
+}  // namespace wificond
+}  // namespace android