shill: cellular: Fix crash if service disappears before connect finishes

BUG=chromium:238360
TEST=Unit tests

Change-Id: Ie52b570d75cf8d17b8152e8ac99fcb2231690e30
Reviewed-on: https://gerrit.chromium.org/gerrit/56420
Reviewed-by: Arman Uguray <armansito@chromium.org>
Commit-Queue: Thieu Le <thieule@chromium.org>
Tested-by: Thieu Le <thieule@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 5d1c5c5..ac5724a 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -537,7 +537,10 @@
                           weak_ptr_factory_.GetWeakPtr());
   manager()->AddTerminationAction(FriendlyName(), start_cb);
   SetState(kStateConnected);
-  if (!capability_->AllowRoaming() &&
+  if (!service_) {
+    LOG(INFO) << "Disconnecting due to no cellular service.";
+    Disconnect(NULL);
+  } else if (!capability_->AllowRoaming() &&
       service_->roaming_state() == flimflam::kRoamingStateRoaming) {
     LOG(INFO) << "Disconnecting due to roaming.";
     Disconnect(NULL);
diff --git a/cellular.h b/cellular.h
index d9e94c7..878a85c 100644
--- a/cellular.h
+++ b/cellular.h
@@ -266,6 +266,7 @@
   FRIEND_TEST_ALL_PREFIXES(CellularTest, ConnectAddsTerminationAction);
   FRIEND_TEST(CellularTest, ConnectFailure);
   FRIEND_TEST(CellularTest, ConnectFailureNoService);
+  FRIEND_TEST(CellularTest, ConnectSuccessNoService);
   FRIEND_TEST(CellularTest, CustomSetterNoopChange);
   FRIEND_TEST(CellularTest, DisableModem);
   FRIEND_TEST(CellularTest, Disconnect);
diff --git a/cellular_capability_classic.h b/cellular_capability_classic.h
index 334f7aa..b9ec6f6 100644
--- a/cellular_capability_classic.h
+++ b/cellular_capability_classic.h
@@ -171,6 +171,7 @@
   FRIEND_TEST_ALL_PREFIXES(CellularTest, ConnectAddsTerminationAction);
   FRIEND_TEST(CellularTest, ConnectFailure);
   FRIEND_TEST(CellularTest, ConnectFailureNoService);
+  FRIEND_TEST(CellularTest, ConnectSuccessNoService);
   FRIEND_TEST(CellularTest, Disconnect);
   FRIEND_TEST(CellularTest, DisconnectFailure);
   FRIEND_TEST(CellularTest, DisconnectWithCallback);
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index 435a8fe..7289036 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -263,6 +263,12 @@
     device_->service_ = NULL;
     callback.Run(Error(Error::kNotOnHomeNetwork));
   }
+  void InvokeConnectSuccessNoService(DBusPropertiesMap props, Error *error,
+                                     const ResultCallback &callback,
+                                     int timeout) {
+    device_->service_ = NULL;
+    callback.Run(Error());
+  }
   void InvokeDisconnect(Error *error, const ResultCallback &callback,
                         int timeout) {
     if (!callback.is_null())
@@ -696,6 +702,22 @@
   device_->Connect(&error);
 }
 
+TEST_F(CellularTest, ConnectSuccessNoService) {
+  // Make sure we don't crash if the connect succeeds but the service was
+  // destroyed before the connect request completes.
+  SetCellularType(Cellular::kTypeCDMA);
+  device_->state_ = Cellular::kStateRegistered;
+  SetService();
+  EXPECT_CALL(
+      *simple_proxy_,
+      Connect(_, _, _, CellularCapability::kTimeoutConnect))
+      .WillOnce(Invoke(this, &CellularTest::InvokeConnectSuccessNoService));
+  EXPECT_CALL(*modem_info_.mock_manager(), UpdateService(_));
+  GetCapabilityClassic()->simple_proxy_.reset(simple_proxy_.release());
+  Error error;
+  device_->Connect(&error);
+}
+
 TEST_F(CellularTest, LinkEventWontDestroyService) {
   // If the network interface goes down, Cellular::LinkEvent should
   // drop the connection but the service object should persist.