shill: cellular: Move Scanning property from capabilities to cellular.

This is a part of an effort to move all exposed DBus properties of shill from
CellularCapability* to Cellular.

BUG=chromium:322747
TEST=(1) Run network_3GScanningProperty
     (2) Check that the ui for network scanning updates correctly on link,
         and lumpy
             (1) During initialization of the modem
             (2) After the user disables/enables the modem.
             (3) When the user initiates a scan from the ui.

Change-Id: I9bfeae70cf54f74a047047a2e091768821ff48b2
Reviewed-on: https://chromium-review.googlesource.com/182250
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Prathmesh Prabhu <pprabhu@chromium.org>
Commit-Queue: Prathmesh Prabhu <pprabhu@chromium.org>
diff --git a/callbacks.h b/callbacks.h
index b1dc9ae..1b16660 100644
--- a/callbacks.h
+++ b/callbacks.h
@@ -9,6 +9,7 @@
 
 #include <base/callback.h>
 
+#include "shill/accessor_interface.h"
 #include "shill/dbus_properties.h"
 
 namespace shill {
@@ -33,6 +34,8 @@
 typedef base::Callback<void(const std::string &, const Error &)> StringCallback;
 typedef base::Callback<void(
     uint32, uint32, const DBusPropertiesMap &)> ActivationStateSignalCallback;
+typedef base::Callback<void(const Stringmaps &,
+                            const Error &)> ResultStringmapsCallback;
 
 }  // namespace shill
 #endif  // SHILL_CALLBACKS_
diff --git a/cellular.cc b/cellular.cc
index 456eac8..b0320a5 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -52,6 +52,7 @@
 
 // static
 const char Cellular::kAllowRoaming[] = "AllowRoaming";
+const int64 Cellular::kDefaultScanningTimeoutMilliseconds = 60000;
 
 Cellular::Operator::Operator() {
   SetName("");
@@ -117,6 +118,7 @@
       dbus_service_(service),
       dbus_path_(path),
       scanning_supported_(false),
+      scanning_(false),
       provider_requires_roaming_(false),
       scan_interval_(0),
       sim_present_(false),
@@ -124,18 +126,17 @@
       proxy_factory_(proxy_factory),
       ppp_device_factory_(PPPDeviceFactory::GetInstance()),
       allow_roaming_(false),
+      proposed_scan_in_progress_(false),
       explicit_disconnect_(false),
-      is_ppp_authenticating_(false) {
+      is_ppp_authenticating_(false),
+      scanning_timeout_milliseconds_(kDefaultScanningTimeoutMilliseconds) {
   RegisterProperties();
-  // For now, only a single capability is supported.
   InitCapability(type);
-
   SLOG(Cellular, 2) << "Cellular device " << this->link_name()
                     << " initialized.";
 }
 
-Cellular::~Cellular() {
-}
+Cellular::~Cellular() {}
 
 bool Cellular::Load(StoreInterface *storage) {
   const string id = GetStorageIdentifier();
@@ -498,9 +499,39 @@
 
 void Cellular::Scan(ScanType /*scan_type*/, Error *error,
                     const string &/*reason*/) {
+  SLOG(Cellular, 2) << __func__;
+  CHECK(error);
+  if (proposed_scan_in_progress_) {
+    Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
+    return;
+  }
+
   // |scan_type| is ignored because Cellular only does a full scan.
-  // TODO(ers): for now report immediate success or failure.
-  capability_->Scan(error, ResultCallback());
+  ResultStringmapsCallback cb = Bind(&Cellular::OnScanReply,
+                                     weak_ptr_factory_.GetWeakPtr());
+  capability_->Scan(error, cb);
+  // An immediate failure in |cabapility_->Scan(...)| is indicated through the
+  // |error| argument.
+  if (error->IsFailure())
+    return;
+
+  proposed_scan_in_progress_ = true;
+  UpdateScanning();
+}
+
+void Cellular::OnScanReply(const Stringmaps &found_networks,
+                           const Error &error) {
+  proposed_scan_in_progress_ = false;
+  UpdateScanning();
+
+  // TODO(jglasgow): fix error handling.
+  // At present, there is no way of notifying user of this asynchronous error.
+  if (error.IsFailure()) {
+    clear_found_networks();
+    return;
+  }
+
+  set_found_networks(found_networks);
 }
 
 void Cellular::HandleNewRegistrationState() {
@@ -562,6 +593,9 @@
   service_ = new CellularService(modem_info_, this);
   capability_->OnServiceCreated();
   manager()->RegisterService(service_);
+  // We might have missed a property update because the service wasn't created
+  // ealier.
+  UpdateScanning();
 }
 
 void Cellular::DestroyService() {
@@ -808,6 +842,10 @@
              old_state == kModemStateConnecting)
       OnConnected();
   }
+
+  // Update the kScanningProperty property after we've handled the current state
+  // update completely.
+  UpdateScanning();
 }
 
 bool Cellular::IsActivating() const {
@@ -1032,6 +1070,31 @@
   OnPPPDisconnected();
 }
 
+void Cellular::UpdateScanning() {
+  if (proposed_scan_in_progress_) {
+    set_scanning(true);
+    return;
+  }
+
+  if (modem_state_ == kModemStateEnabling) {
+    set_scanning(true);
+    return;
+  }
+
+  if (service_ && service_->activation_state() != kActivationStateActivated) {
+    set_scanning(false);
+    return;
+  }
+
+  if (modem_state_ == kModemStateEnabled ||
+      modem_state_ == kModemStateSearching) {
+    set_scanning(true);
+    return;
+  }
+
+  set_scanning(false);
+}
+
 void Cellular::RegisterProperties() {
   PropertyStore *store = this->mutable_store();
 
@@ -1058,6 +1121,7 @@
   store->RegisterConstString(kMinProperty, &min_);
   store->RegisterConstString(kManufacturerProperty, &manufacturer_);
   store->RegisterConstString(kModelIDProperty, &model_id_);
+  store->RegisterConstBool(kScanningProperty, &scanning_);
 
   store->RegisterConstString(kSelectedNetworkProperty, &selected_network_);
   store->RegisterConstStringmaps(kFoundNetworksProperty, &found_networks_);
@@ -1187,6 +1251,33 @@
   adaptor()->EmitStringChanged(kModelIDProperty, model_id_);
 }
 
+void Cellular::set_scanning(bool scanning) {
+  if (scanning_ == scanning)
+    return;
+
+  scanning_ = scanning;
+  adaptor()->EmitBoolChanged(kScanningProperty, scanning_);
+
+  // kScanningProperty is a sticky-false property.
+  // Every time it is set to |true|, it will remain |true| up to a maximum of
+  // |kScanningTimeout| time, after which it will be reset to |false|.
+  if (!scanning_ && !scanning_timeout_callback_.IsCancelled()) {
+     SLOG(Cellular, 2) << "Scanning set to false. "
+                       << "Cancelling outstanding timeout.";
+     scanning_timeout_callback_.Cancel();
+  } else {
+    CHECK(scanning_timeout_callback_.IsCancelled());
+    SLOG(Cellular, 2) << "Scanning set to true. "
+                      << "Starting timeout to reset to false.";
+    scanning_timeout_callback_.Reset(Bind(&Cellular::set_scanning,
+                                          weak_ptr_factory_.GetWeakPtr(),
+                                          false));
+    dispatcher()->PostDelayedTask(
+        scanning_timeout_callback_.callback(),
+        scanning_timeout_milliseconds_);
+  }
+}
+
 void Cellular::set_selected_network(const std::string &selected_network) {
   if (selected_network_ == selected_network)
     return;
@@ -1202,6 +1293,14 @@
   adaptor()->EmitStringmapsChanged(kFoundNetworksProperty, found_networks_);
 }
 
+void Cellular::clear_found_networks() {
+  if (found_networks_.empty())
+    return;
+
+  found_networks_.clear();
+  adaptor()->EmitStringmapsChanged(kFoundNetworksProperty, found_networks_);
+}
+
 void Cellular::set_provider_requires_roaming(bool provider_requires_roaming) {
   if (provider_requires_roaming_ == provider_requires_roaming)
     return;
diff --git a/cellular.h b/cellular.h
index dc98afd..b7ebb3b 100644
--- a/cellular.h
+++ b/cellular.h
@@ -218,6 +218,7 @@
   void OnDisconnectFailed();
   std::string GetTechnologyFamily(Error *error);
   void OnModemStateChanged(ModemState new_state);
+  void OnScanReply(const Stringmaps &found_networks, const Error &error);
 
   // accessor to read the allow roaming property
   bool allow_roaming_property() const { return allow_roaming_; }
@@ -253,6 +254,7 @@
   const std::string &min() const { return min_; }
   const std::string &manufacturer() const { return manufacturer_; }
   const std::string &model_id() const { return model_id_; }
+  bool scanning() const { return scanning_; }
 
   const std::string &selected_network() const { return selected_network_; }
   const Stringmaps &found_networks() const { return found_networks_; }
@@ -274,8 +276,10 @@
   void set_min(const std::string &min);
   void set_manufacturer(const std::string &manufacturer);
   void set_model_id(const std::string &model_id);
+  void set_scanning(bool scanning);
 
   void set_selected_network(const std::string &selected_network);
+  void clear_found_networks();
   void set_found_networks(const Stringmaps &found_networks);
   void set_provider_requires_roaming(bool provider_requires_roaming);
   void set_sim_present(bool sim_present);
@@ -364,6 +368,9 @@
   FRIEND_TEST(CellularTest, PPPConnectionFailedAfterAuth);
   FRIEND_TEST(CellularTest, PPPConnectionFailedBeforeAuth);
   FRIEND_TEST(CellularTest, PPPConnectionFailedDuringAuth);
+  FRIEND_TEST(CellularTest, ScanAsynchronousFailure);
+  FRIEND_TEST(CellularTest, ScanImmediateFailure);
+  FRIEND_TEST(CellularTest, ScanSuccess);
   FRIEND_TEST(CellularTest, SetAllowRoaming);
   FRIEND_TEST(CellularTest, StartModemCallback);
   FRIEND_TEST(CellularTest, StartModemCallbackFail);
@@ -378,11 +385,17 @@
   FRIEND_TEST(CellularTest, StartPPP);
   FRIEND_TEST(CellularTest, StartPPPAfterEthernetUp);
   FRIEND_TEST(CellularTest, StartPPPAlreadyStarted);
+  FRIEND_TEST(CellularTest, UpdateScanning);
   FRIEND_TEST(Modem1Test, CreateDeviceMM1);
 
   // Names of properties in storage
   static const char kAllowRoaming[];
 
+  // the |kScanningProperty| exposed by Cellular device is sticky false. Every
+  // time it is set to true, it must be reset to false after a time equal to
+  // this constant.
+  static const int64 kDefaultScanningTimeoutMilliseconds;
+
   void SetState(State state);
 
   // Invoked when the modem is connected to the cellular network to transition
@@ -439,6 +452,8 @@
   void OnPPPConnected(const std::map<std::string, std::string> &params);
   void OnPPPDisconnected();
 
+  void UpdateScanning();
+
   base::WeakPtrFactory<Cellular> weak_ptr_factory_;
 
   State state_;
@@ -465,6 +480,7 @@
   std::string meid_;
   std::string min_;
   std::string model_id_;
+  bool scanning_;
 
   // GSM only properties.
   // They are always exposed but are non empty only for GSM technology modems.
@@ -485,6 +501,9 @@
   // User preference to allow or disallow roaming
   bool allow_roaming_;
 
+  // Track whether a user initiated scan is in prgoress (initiated via ::Scan)
+  bool proposed_scan_in_progress_;
+
   // Flag indicating that a disconnect has been explicitly requested.
   bool explicit_disconnect_;
 
@@ -492,6 +511,13 @@
   PPPDeviceRefPtr ppp_device_;
   bool is_ppp_authenticating_;
 
+  // Sometimes modems may be stuck in the SEARCHING state during the lack of
+  // presence of a network. During this indefinite duration of time, keeping
+  // the Device.Scanning property as |true| causes a bad user experience.
+  // This callback sets it to |false| after a timeout period has passed.
+  base::CancelableClosure scanning_timeout_callback_;
+  int64 scanning_timeout_milliseconds_;
+
   DISALLOW_COPY_AND_ASSIGN(Cellular);
 };
 
diff --git a/cellular_capability.cc b/cellular_capability.cc
index 2e5631b..5a136ab 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -34,8 +34,7 @@
                                        ModemInfo *modem_info)
     : cellular_(cellular),
       proxy_factory_(proxy_factory),
-      modem_info_(modem_info){
-}
+      modem_info_(modem_info) {}
 
 CellularCapability::~CellularCapability() {}
 
@@ -89,7 +88,7 @@
 }
 
 void CellularCapability::Scan(Error *error,
-                              const ResultCallback &callback) {
+                              const ResultStringmapsCallback &callback) {
   OnUnsupportedOperation(__func__, error);
 }
 
diff --git a/cellular_capability.h b/cellular_capability.h
index 0a6d99a..ee995b7 100644
--- a/cellular_capability.h
+++ b/cellular_capability.h
@@ -171,7 +171,7 @@
   // logic into Cellular or CellularCapability.
   //
   // TODO(jglasgow): Implement real error handling.
-  virtual void Scan(Error *error, const ResultCallback &callback);
+  virtual void Scan(Error *error, const ResultStringmapsCallback &callback);
 
   // Returns an empty string if the network technology is unknown.
   virtual std::string GetNetworkTechnologyString() const = 0;
diff --git a/cellular_capability_classic.cc b/cellular_capability_classic.cc
index 3801a4e..fc98c5b 100644
--- a/cellular_capability_classic.cc
+++ b/cellular_capability_classic.cc
@@ -296,7 +296,7 @@
 }
 
 void CellularCapabilityClassic::Scan(Error *error,
-                                     const ResultCallback &callback) {
+                                     const ResultStringmapsCallback &callback) {
   OnUnsupportedOperation(__func__, error);
 }
 
diff --git a/cellular_capability_classic.h b/cellular_capability_classic.h
index a40d8d6..b9589a4 100644
--- a/cellular_capability_classic.h
+++ b/cellular_capability_classic.h
@@ -89,7 +89,7 @@
   virtual void SetCarrier(const std::string &carrier,
                           Error *error, const ResultCallback &callback);
 
-  virtual void Scan(Error *error, const ResultCallback &callback);
+  virtual void Scan(Error *error, const ResultStringmapsCallback &callback);
 
   virtual void OnDBusPropertiesChanged(
       const std::string &interface,
diff --git a/cellular_capability_classic_unittest.cc b/cellular_capability_classic_unittest.cc
index 35f76a1..93c3b0d 100644
--- a/cellular_capability_classic_unittest.cc
+++ b/cellular_capability_classic_unittest.cc
@@ -309,7 +309,8 @@
 TEST_F(CellularCapabilityTest, UnsupportedOperation) {
   Error error;
   EXPECT_CALL(*this, TestCallback(IsSuccess())).Times(0);
-  capability_->CellularCapability::Scan(&error,
+  capability_->CellularCapability::Reset(
+      &error,
       Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
   EXPECT_TRUE(error.IsFailure());
   EXPECT_EQ(Error::kNotSupported, error.type());
diff --git a/cellular_capability_gsm.cc b/cellular_capability_gsm.cc
index a41d4b9..cb0e58c 100644
--- a/cellular_capability_gsm.cc
+++ b/cellular_capability_gsm.cc
@@ -59,11 +59,8 @@
       access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
       home_provider_info_(NULL),
       get_imsi_retries_(0),
-      get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds),
-      scanning_(false) {
+      get_imsi_retry_delay_milliseconds_(kGetIMSIRetryDelayMilliseconds) {
   SLOG(Cellular, 2) << "Cellular capability constructed: GSM";
-  PropertyStore *store = cellular->mutable_store();
-  store->RegisterConstBool(kScanningProperty, &scanning_);
   HelpRegisterConstDerivedKeyValueStore(
       kSIMLockStatusProperty, &CellularCapabilityGSM::SimLockStatusToProperty);
   this->cellular()->set_scanning_supported(true);
@@ -663,45 +660,21 @@
   card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
 }
 
-void CellularCapabilityGSM::Scan(Error *error, const ResultCallback &callback) {
-  SLOG(Cellular, 2) << __func__;
-  CHECK(error);
-  if (scanning_) {
-    Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
-    return;
-  }
+void CellularCapabilityGSM::Scan(Error *error,
+                                 const ResultStringmapsCallback &callback) {
   ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
                                 weak_ptr_factory_.GetWeakPtr(), callback);
   network_proxy_->Scan(error, cb, kTimeoutScan);
-  if (!error->IsFailure()) {
-    scanning_ = true;
-    cellular()->adaptor()->EmitBoolChanged(kScanningProperty, scanning_);
-  }
 }
 
-void CellularCapabilityGSM::OnScanReply(const ResultCallback &callback,
-                                        const GSMScanResults &results,
-                                        const Error &error) {
-  SLOG(Cellular, 2) << __func__;
-
-  // Error handling is weak.  The current expectation is that on any
-  // error, found_networks should be cleared and a property change
-  // notification sent out.
-  //
-  // TODO(jglasgow): fix error handling
+void CellularCapabilityGSM::OnScanReply(
+    const ResultStringmapsCallback &callback,
+    const GSMScanResults &results,
+    const Error &error) {
   Stringmaps found_networks;
-  scanning_ = false;
-  cellular()->adaptor()->EmitBoolChanged(kScanningProperty, scanning_);
-  if (!error.IsFailure()) {
-    for (GSMScanResults::const_iterator it = results.begin();
-         it != results.end(); ++it) {
-      found_networks.push_back(ParseScanResult(*it));
-    }
-  }
-  cellular()->set_found_networks(found_networks);
-
-  if (!callback.is_null())
-    callback.Run(error);
+  for (const auto &result : results)
+    found_networks.push_back(ParseScanResult(result));
+  callback.Run(found_networks, error);
 }
 
 Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult &result) {
diff --git a/cellular_capability_gsm.h b/cellular_capability_gsm.h
index b0c0750..3bb79e0 100644
--- a/cellular_capability_gsm.h
+++ b/cellular_capability_gsm.h
@@ -70,7 +70,7 @@
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
                          Error *error, const ResultCallback &callback);
-  virtual void Scan(Error *error, const ResultCallback &callback);
+  virtual void Scan(Error *error, const ResultStringmapsCallback &callback);
   virtual std::string GetNetworkTechnologyString() const;
   virtual std::string GetRoamingStateString() const;
   virtual std::string GetTypeString() const {
@@ -112,7 +112,6 @@
   FRIEND_TEST(CellularCapabilityGSMTest, ParseScanResult);
   FRIEND_TEST(CellularCapabilityGSMTest, ParseScanResultProviderLookup);
   FRIEND_TEST(CellularCapabilityGSMTest, RegisterOnNetwork);
-  FRIEND_TEST(CellularCapabilityGSMTest, Scan);
   FRIEND_TEST(CellularCapabilityGSMTest, SetAccessTechnology);
   FRIEND_TEST(CellularCapabilityGSMTest, UpdateStatus);
   FRIEND_TEST(CellularCapabilityGSMTest, SetHomeProvider);
@@ -122,6 +121,9 @@
   FRIEND_TEST(CellularCapabilityGSMTest, SetupApnTryList);
   FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityTest, TryApns);
+  FRIEND_TEST(CellularTest, ScanAsynchronousFailure);
+  FRIEND_TEST(CellularTest, ScanImmediateFailure);
+  FRIEND_TEST(CellularTest, ScanSuccess);
   FRIEND_TEST(CellularTest, StartGSMRegister);
   FRIEND_TEST(ModemTest, CreateDeviceFromProperties);
 
@@ -210,7 +212,7 @@
   void OnGetMSISDNReply(const ResultCallback &callback,
                         const std::string &msisdn,
                         const Error &error);
-  void OnScanReply(const ResultCallback &callback,
+  void OnScanReply(const ResultStringmapsCallback &callback,
                    const GSMScanResults &results,
                    const Error &error);
   void OnConnectReply(const ResultCallback &callback, const Error &error);
@@ -235,7 +237,6 @@
 
   // Properties.
   std::deque<Stringmap> apn_try_list_;
-  bool scanning_;
   SimLockStatus sim_lock_status_;
 
   static unsigned int friendly_service_name_id_;
diff --git a/cellular_capability_gsm_unittest.cc b/cellular_capability_gsm_unittest.cc
index e3103e5..de30207 100644
--- a/cellular_capability_gsm_unittest.cc
+++ b/cellular_capability_gsm_unittest.cc
@@ -161,14 +161,6 @@
                        const ResultCallback &callback, int timeout) {
     callback.Run(Error());
   }
-  void InvokeScanReply() {
-    GSMScanResults results;
-    results.push_back(GSMScanResult());
-    results[0][CellularCapabilityGSM::kNetworkPropertyID] = kScanID0;
-    results.push_back(GSMScanResult());
-    results[1][CellularCapabilityGSM::kNetworkPropertyID] = kScanID1;
-    scan_callback_.Run(results, Error());
-  }
   void InvokeGetModemStatus(Error *error,
                             const DBusPropertyMapCallback &callback,
                             int timeout) {
@@ -198,8 +190,6 @@
   static const char kIMEI[];
   static const char kIMSI[];
   static const char kMSISDN[];
-  static const char kScanID0[];
-  static const char kScanID1[];
   static const int kStrength;
 
   class TestProxyFactory : public ProxyFactory {
@@ -313,7 +303,6 @@
   CellularCapabilityGSM *capability_;  // Owned by |cellular_|.
   DeviceMockAdaptor *device_adaptor_;  // Owned by |cellular_|.
   CellularRefPtr cellular_;
-  ScanResultsCallback scan_callback_;  // saved for testing scan operations
 };
 
 const char CellularCapabilityGSMTest::kAddress[] = "1122334455";
@@ -326,8 +315,6 @@
 const char CellularCapabilityGSMTest::kIMEI[] = "987654321098765";
 const char CellularCapabilityGSMTest::kIMSI[] = "310150123456789";
 const char CellularCapabilityGSMTest::kMSISDN[] = "12345678901";
-const char CellularCapabilityGSMTest::kScanID0[] = "123";
-const char CellularCapabilityGSMTest::kScanID1[] = "456";
 const int CellularCapabilityGSMTest::kStrength = 80;
 
 TEST_F(CellularCapabilityGSMTest, PropertyStore) {
@@ -541,44 +528,6 @@
   EXPECT_TRUE(error.IsSuccess());
 }
 
-namespace {
-
-MATCHER(SizeIs2, "") {
-  return arg.size() == 2;
-}
-
-}  // namespace
-
-TEST_F(CellularCapabilityGSMTest, Scan) {
-  Error error;
-  Stringmaps found_networks;
-
-  EXPECT_CALL(*network_proxy_, Scan(_, _, CellularCapability::kTimeoutScan))
-      .WillOnce(SaveArg<1>(&scan_callback_));
-  EXPECT_CALL(*this, TestCallback(IsSuccess()));
-  found_networks = cellular_->found_networks();
-  found_networks.resize(3);
-  cellular_->set_found_networks(found_networks);
-  EXPECT_CALL(*device_adaptor_,
-              EmitStringmapsChanged(kFoundNetworksProperty, SizeIs2()));
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, true));
-  EXPECT_FALSE(capability_->scanning_);
-
-  SetNetworkProxy();
-  capability_->Scan(&error, Bind(&CellularCapabilityGSMTest::TestCallback,
-                                 Unretained(this)));
-  EXPECT_TRUE(error.IsSuccess());
-  EXPECT_TRUE(capability_->scanning_);
-
-  // Simulate the completion of the scan...
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, false));
-  InvokeScanReply();
-  EXPECT_FALSE(capability_->scanning_);
-  found_networks = cellular_->found_networks();
-  EXPECT_EQ(2, cellular_->found_networks().size());
-  EXPECT_EQ(kScanID0, found_networks[0][kNetworkIdProperty]);
-  EXPECT_EQ(kScanID1, found_networks[1][kNetworkIdProperty]);
-}
 
 TEST_F(CellularCapabilityGSMTest, ParseScanResult) {
   static const char kID[] = "123";
diff --git a/cellular_capability_universal.cc b/cellular_capability_universal.cc
index eb73879..6020ede 100644
--- a/cellular_capability_universal.cc
+++ b/cellular_capability_universal.cc
@@ -49,9 +49,6 @@
 const int64
 CellularCapabilityUniversal::kActivationRegistrationTimeoutMilliseconds =
     20000;
-const int64
-CellularCapabilityUniversal::kDefaultScanningOrSearchingTimeoutMilliseconds =
-    60000;
 const int64 CellularCapabilityUniversal::kEnterPinTimeoutMilliseconds = 20000;
 const int64
 CellularCapabilityUniversal::kRegistrationDroppedUpdateTimeoutMilliseconds =
@@ -143,12 +140,8 @@
       access_technologies_(MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
       home_provider_info_(NULL),
       resetting_(false),
-      scanning_(false),
-      scanning_or_searching_(false),
       subscription_state_(kSubscriptionStateUnknown),
       reset_done_(false),
-      scanning_or_searching_timeout_milliseconds_(
-          kDefaultScanningOrSearchingTimeoutMilliseconds),
       activation_registration_timeout_milliseconds_(
           kActivationRegistrationTimeoutMilliseconds),
       registration_dropped_update_timeout_milliseconds_(
@@ -157,7 +150,6 @@
   PropertyStore *store = cellular->mutable_store();
 
   store->RegisterConstString(kIccidProperty, &sim_identifier_);
-  store->RegisterConstBool(kScanningProperty, &scanning_or_searching_);
   HelpRegisterConstDerivedKeyValueStore(
       kSIMLockStatusProperty,
       &CellularCapabilityUniversal::SimLockStatusToProperty);
@@ -691,7 +683,6 @@
 void CellularCapabilityUniversal::OnServiceCreated() {
   UpdateStorageIdentifier();
   UpdateServiceActivationState();
-  UpdateScanningProperty();
   UpdateServingOperator();
   UpdateOLP();
 
@@ -914,52 +905,6 @@
   UpdateServiceName();
 }
 
-void CellularCapabilityUniversal::UpdateScanningProperty() {
-  // Set the Scanning property to true if there is a ongoing network scan
-  // (i.e. |scanning_| is true) or the modem is enabled but not yet registered
-  // to a network.
-  //
-  // TODO(benchan): As the Device DBus interface currently does not have a
-  // State property to indicate whether the device is being enabled, set the
-  // Scanning property to true when the modem is being enabled such that
-  // the network UI can start showing the initializing/scanning animation as
-  // soon as the modem is being enabled.
-  Cellular::ModemState modem_state = cellular()->modem_state();
-  bool is_activated_service_waiting_for_registration =
-      ((modem_state == Cellular::kModemStateEnabled ||
-        modem_state == Cellular::kModemStateSearching) &&
-       !IsServiceActivationRequired());
-  bool new_scanning_or_searching =
-      modem_state == Cellular::kModemStateEnabling ||
-      is_activated_service_waiting_for_registration ||
-      scanning_;
-  if (new_scanning_or_searching != scanning_or_searching_) {
-    scanning_or_searching_ = new_scanning_or_searching;
-    cellular()->adaptor()->EmitBoolChanged(kScanningProperty,
-                                           new_scanning_or_searching);
-
-    if (!scanning_or_searching_) {
-      SLOG(Cellular, 2) << "Initial network scan ended. Canceling timeout.";
-      scanning_or_searching_timeout_callback_.Cancel();
-    } else if (scanning_or_searching_timeout_callback_.IsCancelled()) {
-      SLOG(Cellular, 2) << "Initial network scan started. Starting timeout.";
-      scanning_or_searching_timeout_callback_.Reset(
-          Bind(&CellularCapabilityUniversal::OnScanningOrSearchingTimeout,
-               weak_ptr_factory_.GetWeakPtr()));
-      cellular()->dispatcher()->PostDelayedTask(
-          scanning_or_searching_timeout_callback_.callback(),
-          scanning_or_searching_timeout_milliseconds_);
-    }
-  }
-}
-
-void CellularCapabilityUniversal::OnScanningOrSearchingTimeout() {
-  SLOG(Cellular, 2) << "Initial network scan timed out. Changing "
-                    << "kScanningProperty to |false|.";
-  scanning_or_searching_ = false;
-  cellular()->adaptor()->EmitBoolChanged(kScanningProperty, false);
-}
-
 void CellularCapabilityUniversal::UpdateOLP() {
   SLOG(Cellular, 2) << __func__;
 
@@ -1347,51 +1292,22 @@
     callback.Run(error);
 }
 
-void CellularCapabilityUniversal::Scan(Error *error,
-                                       const ResultCallback &callback) {
-  SLOG(Cellular, 2) << __func__;
-  CHECK(error);
-  if (scanning_) {
-    Error::PopulateAndLog(error, Error::kInProgress, "Already scanning");
-    return;
-  }
+void CellularCapabilityUniversal::Scan(
+    Error *error,
+    const ResultStringmapsCallback &callback) {
   DBusPropertyMapsCallback cb = Bind(&CellularCapabilityUniversal::OnScanReply,
                                      weak_ptr_factory_.GetWeakPtr(), callback);
   modem_3gpp_proxy_->Scan(error, cb, kTimeoutScan);
-  if (!error->IsFailure()) {
-    scanning_ = true;
-    UpdateScanningProperty();
-  }
 }
 
-void CellularCapabilityUniversal::OnScanReply(const ResultCallback &callback,
-                                              const ScanResults &results,
-                                              const Error &error) {
-  SLOG(Cellular, 2) << __func__;
-
-  // Error handling is weak.  The current expectation is that on any
-  // error, found_networks should be cleared and a property change
-  // notification sent out.
-  //
-  // TODO(jglasgow): fix error handling
+void CellularCapabilityUniversal::OnScanReply(
+    const ResultStringmapsCallback &callback,
+    const ScanResults &results,
+    const Error &error) {
   Stringmaps found_networks;
-  scanning_ = false;
-  UpdateScanningProperty();
-  if (!error.IsFailure()) {
-    for (ScanResults::const_iterator it = results.begin();
-         it != results.end(); ++it) {
-      found_networks.push_back(ParseScanResult(*it));
-    }
-  }
-  cellular()->set_found_networks(found_networks);
-
-  // TODO(gmorain): This check for is_null() is a work-around because
-  // Cellular::Scan() passes a null callback.  Instead: 1. Have Cellular::Scan()
-  // pass in a callback. 2. Have Cellular "own" the found_networks property
-  // 3. Have Cellular EmitStingMapsChanged() 4. Share the code between GSM and
-  // Universal.
-  if (!callback.is_null())
-    callback.Run(error);
+  for (const auto &result : results)
+    found_networks.push_back(ParseScanResult(result));
+  callback.Run(found_networks, error);
 }
 
 Stringmap CellularCapabilityUniversal::ParseScanResult(
@@ -1754,7 +1670,6 @@
   SLOG(Cellular, 3) << __func__ << ": " << Cellular::GetModemStateString(state);
 
   cellular()->OnModemStateChanged(state);
-  UpdateScanningProperty();
   // TODO(armansito): Move the deferred enable logic to Cellular
   // (See crbug.com/279499).
   if (!deferred_enable_modem_callback_.is_null() &&
diff --git a/cellular_capability_universal.h b/cellular_capability_universal.h
index c859f39..712a0bb 100644
--- a/cellular_capability_universal.h
+++ b/cellular_capability_universal.h
@@ -97,7 +97,7 @@
                          Error *error, const ResultCallback &callback);
   virtual void Reset(Error *error, const ResultCallback &callback);
 
-  virtual void Scan(Error *error, const ResultCallback &callback);
+  virtual void Scan(Error *error, const ResultStringmapsCallback &callback);
   virtual std::string GetNetworkTechnologyString() const;
   virtual std::string GetRoamingStateString() const;
   virtual void GetSignalQuality();
@@ -167,7 +167,6 @@
   static const char kGenericServiceNamePrefix[];
 
   static const int64 kActivationRegistrationTimeoutMilliseconds;
-  static const int64 kDefaultScanningOrSearchingTimeoutMilliseconds;
   static const int64 kEnterPinTimeoutMilliseconds;
   static const int64 kRegistrationDroppedUpdateTimeoutMilliseconds;
   static const int kSetPowerStateTimeoutMilliseconds;
@@ -245,10 +244,7 @@
   FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateOperatorInfo);
   FRIEND_TEST(CellularCapabilityUniversalMainTest,
               UpdateOperatorInfoViaOperatorId);
-  FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateScanningProperty);
   FRIEND_TEST(CellularCapabilityUniversalTimerTest, CompleteActivation);
-  FRIEND_TEST(CellularCapabilityUniversalTimerTest,
-              UpdateScanningPropertyTimeout);
   FRIEND_TEST(CellularTest, EnableTrafficMonitor);
   FRIEND_TEST(CellularTest,
               HandleNewRegistrationStateForServiceRequiringActivation);
@@ -297,8 +293,6 @@
   // Updates the name property that is exposed by the service to Chrome.
   void UpdateServiceName();
 
-  void UpdateScanningProperty();
-
   // Methods used in acquiring information related to the carrier;
 
   // Updates the Universal operator name and country based on a newly
@@ -393,17 +387,13 @@
   void OnRegisterReply(const ResultCallback &callback,
                        const Error &error);
   void OnResetReply(const ResultCallback &callback, const Error &error);
-  void OnScanReply(const ResultCallback &callback,
+  void OnScanReply(const ResultStringmapsCallback &callback,
                    const ScanResults &results,
                    const Error &error);
   void OnConnectReply(const ResultCallback &callback,
                       const DBus::Path &bearer,
                       const Error &error);
 
-  // Timeout callback for the network scan initiated when Cellular connectivity
-  // gets enabled.
-  void OnScanningOrSearchingTimeout();
-
   // Timeout callback that is called if the modem takes too long to register to
   // a network after online payment is complete. Resets the modem.
   void OnActivationWaitForRegisterTimeout();
@@ -455,8 +445,6 @@
   // Properties.
   std::deque<Stringmap> apn_try_list_;
   bool resetting_;
-  bool scanning_;
-  bool scanning_or_searching_;
   SimLockStatus sim_lock_status_;
   SubscriptionState subscription_state_;
   std::string sim_path_;
@@ -468,13 +456,6 @@
   // enabling is deferred using this callback.
   base::Closure deferred_enable_modem_callback_;
 
-  // Sometimes modems may be stuck in the SEARCHING state during the lack of
-  // presence of a network. During this indefinite duration of time, keeping
-  // the Device.Scanning property as |true| causes a bad user experience.
-  // This callback sets it to |false| after a timeout period has passed.
-  base::CancelableClosure scanning_or_searching_timeout_callback_;
-  int64 scanning_or_searching_timeout_milliseconds_;
-
   base::CancelableClosure activation_wait_for_registration_callback_;
   int64 activation_registration_timeout_milliseconds_;
 
diff --git a/cellular_capability_universal_cdma.cc b/cellular_capability_universal_cdma.cc
index c6cae61..059e98b 100644
--- a/cellular_capability_universal_cdma.cc
+++ b/cellular_capability_universal_cdma.cc
@@ -508,9 +508,11 @@
   // TODO(armansito): Remove once 3GPP is implemented in its own class.
 }
 
-void CellularCapabilityUniversalCDMA::Scan(Error *error,
-                                           const ResultCallback &callback) {
+void CellularCapabilityUniversalCDMA::Scan(
+    Error *error,
+    const ResultStringmapsCallback &callback) {
   // TODO(armansito): Remove once 3GPP is implemented in its own class.
+  OnUnsupportedOperation(__func__, error);
 }
 
 void CellularCapabilityUniversalCDMA::OnSimPathChanged(
diff --git a/cellular_capability_universal_cdma.h b/cellular_capability_universal_cdma.h
index 80ab933..9b1070b 100644
--- a/cellular_capability_universal_cdma.h
+++ b/cellular_capability_universal_cdma.h
@@ -61,7 +61,7 @@
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
                          Error *error, const ResultCallback &callback);
-  virtual void Scan(Error *error, const ResultCallback &callback);
+  virtual void Scan(Error *error, const ResultStringmapsCallback &callback);
   virtual void OnSimPathChanged(const std::string &sim_path);
 
  protected:
diff --git a/cellular_capability_universal_unittest.cc b/cellular_capability_universal_unittest.cc
index b94de45..e63c14e 100644
--- a/cellular_capability_universal_unittest.cc
+++ b/cellular_capability_universal_unittest.cc
@@ -186,20 +186,6 @@
                            int timeout) {
     callback.Run(Error());
   }
-  void InvokeScan(Error *error, const DBusPropertyMapsCallback &callback,
-                  int timeout) {
-    callback.Run(CellularCapabilityUniversal::ScanResults(), Error());
-  }
-  void ScanError(Error *error, const DBusPropertyMapsCallback &callback,
-                 int timeout) {
-    error->Populate(Error::kOperationFailed);
-  }
-
-  bool InvokeScanningOrSearchingTimeout() {
-    capability_->OnScanningOrSearchingTimeout();
-    return true;
-  }
-
   void Set3gppProxy() {
     capability_->modem_3gpp_proxy_.reset(modem_3gpp_proxy_.release());
   }
@@ -332,7 +318,6 @@
   DeviceMockAdaptor *device_adaptor_;  // Owned by |cellular_|.
   CellularRefPtr cellular_;
   MockCellularService *service_;  // owned by cellular_
-  DBusPropertyMapsCallback scan_callback_;  // saved for testing scan operations
   DBusPathCallback connect_callback_;  // saved for testing connect operations
 };
 
@@ -1256,103 +1241,6 @@
   EXPECT_FALSE(capability_->resetting_);
 }
 
-// Validates that OnScanReply does not crash with a null callback.
-TEST_F(CellularCapabilityUniversalMainTest, ScanWithNullCallback) {
-  Error error;
-  EXPECT_CALL(*modem_3gpp_proxy_, Scan(_, _, CellularCapability::kTimeoutScan))
-      .WillOnce(Invoke(this, &CellularCapabilityUniversalTest::InvokeScan));
-  EXPECT_CALL(*device_adaptor_,
-              EmitStringmapsChanged(kFoundNetworksProperty, SizeIs(0)));
-  Set3gppProxy();
-  capability_->Scan(&error, ResultCallback());
-  EXPECT_TRUE(error.IsSuccess());
-}
-
-// Validates that the scanning property is updated
-TEST_F(CellularCapabilityUniversalMainTest, Scan) {
-  Error error;
-
-  EXPECT_CALL(*modem_3gpp_proxy_, Scan(_, _, CellularCapability::kTimeoutScan))
-      .WillRepeatedly(SaveArg<1>(&scan_callback_));
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, true));
-  Set3gppProxy();
-  capability_->Scan(&error, ResultCallback());
-  EXPECT_TRUE(capability_->scanning_);
-  Mock::VerifyAndClearExpectations(device_adaptor_);
-
-  // Simulate the completion of the scan with 2 networks in the results.
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, false));
-  EXPECT_CALL(*device_adaptor_,
-              EmitStringmapsChanged(kFoundNetworksProperty, SizeIs(2)));
-  vector<DBusPropertiesMap> results;
-  const char kScanID0[] = "testID0";
-  const char kScanID1[] = "testID1";
-  results.push_back(DBusPropertiesMap());
-  results[0][CellularCapabilityUniversal::kOperatorLongProperty].
-      writer().append_string(kScanID0);
-  results.push_back(DBusPropertiesMap());
-  results[1][CellularCapabilityUniversal::kOperatorLongProperty].
-      writer().append_string(kScanID1);
-  scan_callback_.Run(results, error);
-  EXPECT_FALSE(capability_->scanning_);
-  Mock::VerifyAndClearExpectations(device_adaptor_);
-
-  // Simulate the completion of the scan with no networks in the results.
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, true));
-  capability_->Scan(&error, ResultCallback());
-  EXPECT_TRUE(capability_->scanning_);
-  Mock::VerifyAndClearExpectations(device_adaptor_);
-
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, false));
-  EXPECT_CALL(*device_adaptor_,
-              EmitStringmapsChanged(kFoundNetworksProperty, SizeIs(0)));
-  scan_callback_.Run(vector<DBusPropertiesMap>(), Error());
-  EXPECT_FALSE(capability_->scanning_);
-}
-
-// Validates expected property updates when scan fails
-TEST_F(CellularCapabilityUniversalMainTest, ScanFailure) {
-  Error error;
-
-  // Test immediate error
-  {
-    InSequence seq;
-    EXPECT_CALL(*modem_3gpp_proxy_,
-                Scan(_, _, CellularCapability::kTimeoutScan))
-        .WillOnce(Invoke(this, &CellularCapabilityUniversalTest::ScanError));
-    EXPECT_CALL(*modem_3gpp_proxy_,
-                Scan(_, _, CellularCapability::kTimeoutScan))
-        .WillOnce(SaveArg<1>(&scan_callback_));
-  }
-  Set3gppProxy();
-  capability_->Scan(&error, ResultCallback());
-  EXPECT_FALSE(capability_->scanning_);
-  EXPECT_TRUE(error.IsFailure());
-
-  // Initiate a scan
-  error.Populate(Error::kSuccess);
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, true));
-  capability_->Scan(&error, ResultCallback());
-  EXPECT_TRUE(capability_->scanning_);
-  EXPECT_TRUE(error.IsSuccess());
-
-  // Validate that error is returned if Scan is called while already scanning.
-  capability_->Scan(&error, ResultCallback());
-  EXPECT_TRUE(capability_->scanning_);
-  EXPECT_TRUE(error.IsFailure());
-  Mock::VerifyAndClearExpectations(device_adaptor_);
-
-  // Validate that signals are emitted even if an error is reported.
-  Stringmaps found_networks = { Stringmap() };
-  cellular_->set_found_networks(found_networks);
-  EXPECT_CALL(*device_adaptor_, EmitBoolChanged(kScanningProperty, false));
-  EXPECT_CALL(*device_adaptor_,
-              EmitStringmapsChanged(kFoundNetworksProperty, SizeIs(0)));
-  vector<DBusPropertiesMap> results;
-  scan_callback_.Run(results, Error(Error::kOperationFailed));
-  EXPECT_FALSE(capability_->scanning_);
-}
-
 TEST_F(CellularCapabilityUniversalMainTest, UpdateActiveBearerPath) {
   // Common resources.
   const size_t kPathCount = 3;
@@ -1627,122 +1515,6 @@
   EXPECT_TRUE(cellular_->provider_requires_roaming());
 }
 
-TEST_F(CellularCapabilityUniversalMainTest, UpdateScanningProperty) {
-  // Save pointers to proxies before they are lost by the call to InitProxies
-  // mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
-  //EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
-  capability_->InitProxies();
-
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-
-  capability_->scanning_ = true;
-  capability_->UpdateScanningProperty();
-  EXPECT_TRUE(capability_->scanning_or_searching_);
-
-  capability_->scanning_ = false;
-  capability_->cellular()->modem_state_ = Cellular::kModemStateInitializing;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateLocked;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateDisabled;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateEnabling;
-  capability_->UpdateScanningProperty();
-  EXPECT_TRUE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateEnabled;
-  capability_->UpdateScanningProperty();
-  EXPECT_TRUE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateSearching;
-  capability_->UpdateScanningProperty();
-  EXPECT_TRUE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateRegistered;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateConnecting;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateConnected;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  capability_->cellular()->modem_state_ = Cellular::kModemStateDisconnecting;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-
-  // Modem with an unactivated service in the 'enabled' or 'searching' state
-  capability_->cellular()->modem_state_ = Cellular::kModemStateEnabled;
-  cellular_->set_mdn("0000000000");
-  CellularService::OLP olp;
-  EXPECT_CALL(*modem_info_.mock_cellular_operator_info(), GetOLPByMCCMNC(_))
-      .WillRepeatedly(Return(&olp));
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-
-  capability_->cellular()->modem_state_ = Cellular::kModemStateSearching;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-}
-
-TEST_F(CellularCapabilityUniversalTimerTest, UpdateScanningPropertyTimeout) {
-  capability_->InitProxies();
-
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  EXPECT_TRUE(
-      capability_->scanning_or_searching_timeout_callback_.IsCancelled());
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  EXPECT_TRUE(
-      capability_->scanning_or_searching_timeout_callback_.IsCancelled());
-
-  EXPECT_CALL(mock_dispatcher_,
-              PostDelayedTask(
-                  _,
-                  CellularCapabilityUniversal::
-                      kDefaultScanningOrSearchingTimeoutMilliseconds));
-
-  capability_->scanning_ = true;
-  capability_->UpdateScanningProperty();
-  EXPECT_FALSE(
-      capability_->scanning_or_searching_timeout_callback_.IsCancelled());
-  EXPECT_TRUE(capability_->scanning_or_searching_);
-  Mock::VerifyAndClearExpectations(&mock_dispatcher_);
-
-  EXPECT_CALL(mock_dispatcher_,
-              PostDelayedTask(
-                  _,
-                  CellularCapabilityUniversal::
-                      kDefaultScanningOrSearchingTimeoutMilliseconds))
-      .Times(0);
-
-  capability_->scanning_ = false;
-  capability_->UpdateScanningProperty();
-  EXPECT_TRUE(
-      capability_->scanning_or_searching_timeout_callback_.IsCancelled());
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-  Mock::VerifyAndClearExpectations(&mock_dispatcher_);
-
-  EXPECT_CALL(mock_dispatcher_,
-              PostDelayedTask(
-                  _,
-                  CellularCapabilityUniversal::
-                      kDefaultScanningOrSearchingTimeoutMilliseconds))
-      .WillOnce(InvokeWithoutArgs(
-          this,
-          &CellularCapabilityUniversalTest::InvokeScanningOrSearchingTimeout));
-
-  capability_->scanning_ = true;
-  capability_->UpdateScanningProperty();
-  // The callback has been scheduled
-  EXPECT_FALSE(
-      capability_->scanning_or_searching_timeout_callback_.IsCancelled());
-  // Our mock invocation worked
-  EXPECT_FALSE(capability_->scanning_or_searching_);
-}
-
 TEST_F(CellularCapabilityUniversalMainTest, UpdateStorageIdentifier) {
   CellularOperatorInfo::CellularOperator provider;
 
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index 36758cf..cf91afa 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -311,6 +311,12 @@
     if (!callback.is_null())
       callback.Run(Error());
   }
+  void InvokeScanFailed(Error *error, Unused, Unused) {
+    error->Populate(Error::kOperationFailed);
+  }
+  void InvokeScanInitiated(Error *error, Unused, Unused) {
+    error->Populate(Error::kOperationInitiated);
+  }
   void InvokeSetPowerState(const uint32_t &power_state,
                            Error *error,
                            const ResultCallback &callback,
@@ -424,6 +430,8 @@
   static const char kIMSI[];
   static const char kMSISDN[];
   static const char kTestMobileProviderDBPath[];
+  static const Stringmaps kTestNetworksGSM;
+  static const Stringmaps kTestNetworksCellular;
   static const int kStrength;
 
   class TestProxyFactory : public ProxyFactory {
@@ -571,6 +579,16 @@
 const char CellularTest::kMSISDN[] = "12345678901";
 const char CellularTest::kTestMobileProviderDBPath[] =
     "provider_db_unittest.bfd";
+const Stringmaps CellularTest::kTestNetworksGSM =
+    {{{CellularCapabilityGSM::kNetworkPropertyStatus, "1"},
+      {CellularCapabilityGSM::kNetworkPropertyID, "0000"},
+      {CellularCapabilityGSM::kNetworkPropertyLongName, "some_long_name"},
+      {CellularCapabilityGSM::kNetworkPropertyShortName, "short"}}};
+const Stringmaps CellularTest::kTestNetworksCellular =
+    {{{kStatusProperty, "available"},
+      {kNetworkIdProperty, "0000"},
+      {kLongNameProperty, "some_long_name"},
+      {kShortNameProperty, "short"}}};
 const int CellularTest::kStrength = 90;
 
 TEST_F(CellularTest, GetStateString) {
@@ -1592,4 +1610,73 @@
   EXPECT_TRUE(error.IsSuccess());
 }
 
+TEST_F(CellularTest, ScanImmediateFailure) {
+  Error error;
+
+  device_->set_found_networks(kTestNetworksCellular);
+  EXPECT_CALL(*gsm_network_proxy_, Scan(&error, _, _))
+      .WillOnce(Invoke(this, &CellularTest::InvokeScanFailed));
+  EXPECT_FALSE(device_->scanning_);
+  // |InitProxies| must be called before calling any functions on the
+  // Capability*, to set up the modem proxies.
+  // Warning: The test loses all references to the proxies when |InitProxies| is
+  // called.
+  GetCapabilityGSM()->InitProxies();
+  device_->Scan(Device::kFullScan, &error, "");
+  EXPECT_TRUE(error.IsFailure());
+  EXPECT_FALSE(device_->scanning_);
+  EXPECT_EQ(kTestNetworksCellular, device_->found_networks());
+}
+
+TEST_F(CellularTest, ScanAsynchronousFailure) {
+  Error error;
+  ScanResultsCallback results_callback;
+
+  device_->set_found_networks(kTestNetworksCellular);
+  EXPECT_CALL(*gsm_network_proxy_, Scan(&error, _, _))
+      .WillOnce(DoAll(Invoke(this, &CellularTest::InvokeScanInitiated),
+                      SaveArg<1>(&results_callback)));
+  EXPECT_FALSE(device_->scanning_);
+  // |InitProxies| must be called before calling any functions on the
+  // Capability*, to set up the modem proxies.
+  // Warning: The test loses all references to the proxies when |InitProxies| is
+  // called.
+  GetCapabilityGSM()->InitProxies();
+  device_->Scan(Device::kFullScan, &error, "");
+  EXPECT_TRUE(error.IsOngoing());
+  EXPECT_TRUE(device_->scanning_);
+
+  // Asynchronously fail the scan.
+  error.Populate(Error::kOperationFailed);
+  results_callback.Run(kTestNetworksGSM, error);
+  EXPECT_FALSE(device_->scanning_);
+  EXPECT_TRUE(device_->found_networks().empty());
+}
+
+TEST_F(CellularTest, ScanSuccess) {
+  Error error;
+  ScanResultsCallback results_callback;
+
+  device_->clear_found_networks();
+  EXPECT_CALL(*gsm_network_proxy_, Scan(&error, _, _))
+      .WillOnce(DoAll(Invoke(this, &CellularTest::InvokeScanInitiated),
+                      SaveArg<1>(&results_callback)));
+  EXPECT_FALSE(device_->scanning_);
+  // |InitProxies| must be called before calling any functions on the
+  // Capability*, to set up the modem proxies.
+  // Warning: The test loses all references to the proxies when |InitProxies| is
+  // called.
+  GetCapabilityGSM()->InitProxies();
+  device_->Scan(Device::kFullScan, &error, "");
+  EXPECT_TRUE(error.IsOngoing());
+  EXPECT_TRUE(device_->scanning_);
+
+  // Successfully complete the scan.
+  const GSMScanResults gsm_results{};
+  error.Populate(Error::kSuccess);
+  results_callback.Run(kTestNetworksGSM, error);
+  EXPECT_FALSE(device_->scanning_);
+  EXPECT_EQ(kTestNetworksCellular, device_->found_networks());
+}
+
 }  // namespace shill