shill: Make cellular Connect work for GSM networks.
Add the APN to the properties map passed to Connect.
Set up the list of APNs to try, and step through it when
a connect attempt fails with the InvalidApn error. The
order in which APNs are tried is
- APN which most recently resulted in a successful
connection on the current network
- a user-specified APN, specified by setting the Cellular.APN
property on the service
- the list of APNs found for the home network provider in the
mobile broadband provider database
- if all those fail, a null APN
BUG=chromium-os:23259
TEST=manual testing, some of which involved modifying the
mobile provider DB to create invalid APN entries. Created
two new unit tests as well, and ran all unit tests.
Change-Id: I38c869228fe1aaf7421de8a826c54e7b62c209b2
Reviewed-on: https://gerrit.chromium.org/gerrit/19122
Tested-by: Eric Shienbrood <ers@chromium.org>
Reviewed-by: Jason Glasgow <jglasgow@chromium.org>
Commit-Ready: Eric Shienbrood <ers@chromium.org>
diff --git a/cellular_capability_gsm.cc b/cellular_capability_gsm.cc
index 6a7e88e..51f80f3 100644
--- a/cellular_capability_gsm.cc
+++ b/cellular_capability_gsm.cc
@@ -184,11 +184,81 @@
}
}
+// Create the list of APNs to try, in the following order:
+// - last APN that resulted in a successful connection attempt on the
+// current network (if any)
+// - the APN, if any, that was set by the user
+// - the list of APNs found in the mobile broadband provider DB for the
+// home provider associated with the current SIM
+// - as a last resort, attempt to connect with no APN
+void CellularCapabilityGSM::SetupApnTryList() {
+ apn_try_list_.clear();
+
+ DCHECK(cellular()->service().get());
+ const Stringmap *apn_info = cellular()->service()->GetLastGoodApn();
+ if (apn_info)
+ apn_try_list_.push_back(*apn_info);
+
+ apn_info = cellular()->service()->GetUserSpecifiedApn();
+ if (apn_info)
+ apn_try_list_.push_back(*apn_info);
+
+ apn_try_list_.insert(apn_try_list_.end(), apn_list_.begin(), apn_list_.end());
+}
+
void CellularCapabilityGSM::SetupConnectProperties(
DBusPropertiesMap *properties) {
+ SetupApnTryList();
+ FillConnectPropertyMap(properties);
+}
+
+void CellularCapabilityGSM::FillConnectPropertyMap(
+ DBusPropertiesMap *properties) {
(*properties)[kConnectPropertyPhoneNumber].writer().append_string(
kPhoneNumber);
- // TODO(petkov): Setup apn and "home_only".
+
+ if (!allow_roaming_)
+ (*properties)[kConnectPropertyHomeOnly].writer().append_bool(true);
+
+ if (!apn_try_list_.empty()) {
+ // Leave the APN at the front of the list, so that it can be recorded
+ // if the connect attempt succeeds.
+ Stringmap apn_info = apn_try_list_.front();
+ VLOG(2) << __func__ << ": Using APN " << apn_info[flimflam::kApnProperty];
+ (*properties)[kConnectPropertyApn].writer().append_string(
+ apn_info[flimflam::kApnProperty].c_str());
+ if (ContainsKey(apn_info, flimflam::kApnUsernameProperty))
+ (*properties)[kConnectPropertyApnUsername].writer().append_string(
+ apn_info[flimflam::kApnUsernameProperty].c_str());
+ if (ContainsKey(apn_info, flimflam::kApnPasswordProperty))
+ (*properties)[kConnectPropertyApnPassword].writer().append_string(
+ apn_info[flimflam::kApnPasswordProperty].c_str());
+ }
+}
+
+void CellularCapabilityGSM::OnConnectReply(const ResultCallback &callback,
+ const Error &error) {
+ if (error.IsFailure()) {
+ cellular()->service()->ClearLastGoodApn();
+ // The APN that was just tried (and failed) is still at the
+ // front of the list, about to be removed. If the list is empty
+ // after that, try one last time without an APN. This may succeed
+ // with some modems in some cases.
+ if (error.type() == Error::kInvalidApn && !apn_try_list_.empty()) {
+ apn_try_list_.pop_front();
+ VLOG(2) << "Connect failed with invalid APN, " << apn_try_list_.size()
+ << " remaining APNs to try";
+ DBusPropertiesMap props;
+ FillConnectPropertyMap(&props);
+ Error error;
+ Connect(props, &error, callback);
+ return;
+ }
+ } else if (!apn_try_list_.empty()) {
+ cellular()->service()->SetLastGoodApn(apn_try_list_.front());
+ apn_try_list_.clear();
+ }
+ CellularCapability::OnConnectReply(callback, error);
}
// always called from an async context