| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/cellular/modem.h" |
| |
| #include <limits> |
| #include <tuple> |
| |
| #include <base/bind.h> |
| #include <base/strings/stringprintf.h> |
| |
| #include <ModemManager/ModemManager.h> |
| |
| #include "shill/cellular/cellular.h" |
| #include "shill/control_interface.h" |
| #include "shill/dbus/dbus_properties_proxy.h" |
| #include "shill/device_info.h" |
| #include "shill/logging.h" |
| #include "shill/manager.h" |
| #include "shill/net/rtnl_handler.h" |
| |
| using base::Bind; |
| using base::Unretained; |
| using std::string; |
| using std::vector; |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kModem; |
| static string ObjectID(const Modem* m) { |
| return m->path().value().c_str(); |
| } |
| } // namespace Logging |
| |
| // statics |
| constexpr char Modem::kFakeDevNameFormat[]; |
| const char Modem::kFakeDevAddress[] = "000000000000"; |
| const int Modem::kFakeDevInterfaceIndex = -1; |
| size_t Modem::fake_dev_serial_ = 0; |
| |
| Modem::Modem(const string& service, |
| const RpcIdentifier& path, |
| ModemInfo* modem_info) |
| : service_(service), |
| path_(path), |
| modem_info_(modem_info), |
| type_(Cellular::kTypeInvalid), |
| has_pending_device_info_(false), |
| rtnl_handler_(RTNLHandler::GetInstance()) { |
| SLOG(this, 1) << "Modem() Path: " << path.value(); |
| } |
| |
| Modem::~Modem() { |
| SLOG(this, 1) << "~Modem() Path: " << path_.value(); |
| if (!device_) { |
| return; |
| } |
| |
| device_->DestroyService(); |
| device_->StopLocationPolling(); |
| device_->DestroyCapability(); |
| |
| // Under certain conditions, Cellular::StopModem may not be called before |
| // the Cellular device is destroyed. This happens if the dbus modem exported |
| // by the modem-manager daemon disappears soon after the modem is disabled, |
| // not giving shill enough time to complete the disable operation. |
| // |
| // In that case, the termination action associated with this cellular object |
| // may not have been removed. |
| modem_info_->manager()->RemoveTerminationAction(device_->link_name()); |
| |
| // Explicitly removes this object from being an observer to |
| // |home_provider_info_| and |serving_operator_info_| to avoid them from |
| // calling into this object while this object is being destructed. |
| device_->home_provider_info()->RemoveObserver(device_.get()); |
| device_->serving_operator_info()->RemoveObserver(device_.get()); |
| |
| // Note: The Cellular Device |device_| is owned by DeviceInfo. It will not |
| // be destroyed here, instead it will be kept around until/unless an RTNL |
| // link delete message is received. If/when a new Modem instance is |
| // constructed (e.g. after modemmanager restarts), the call to |
| // DeviceInfo::GetCellularDevice will return the existing device for the |
| // interface. |
| } |
| |
| void Modem::CreateDeviceMM1(const InterfaceToProperties& properties) { |
| SLOG(this, 1) << __func__; |
| dbus_properties_proxy_ = |
| modem_info_->control_interface()->CreateDBusPropertiesProxy(path(), |
| service()); |
| dbus_properties_proxy_->SetModemManagerPropertiesChangedCallback( |
| Bind(&Modem::OnModemManagerPropertiesChanged, Unretained(this))); |
| dbus_properties_proxy_->SetPropertiesChangedCallback( |
| Bind(&Modem::OnPropertiesChanged, Unretained(this))); |
| |
| uint32_t capabilities = std::numeric_limits<uint32_t>::max(); |
| InterfaceToProperties::const_iterator it = |
| properties.find(MM_DBUS_INTERFACE_MODEM); |
| if (it == properties.end()) { |
| LOG(ERROR) << "Cellular device with no modem properties"; |
| return; |
| } |
| const KeyValueStore& modem_props = it->second; |
| if (modem_props.Contains<uint32_t>(MM_MODEM_PROPERTY_CURRENTCAPABILITIES)) { |
| capabilities = |
| modem_props.Get<uint32_t>(MM_MODEM_PROPERTY_CURRENTCAPABILITIES); |
| } |
| |
| if (capabilities & (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE)) { |
| type_ = Cellular::kType3gpp; |
| } else if (capabilities & MM_MODEM_CAPABILITY_CDMA_EVDO) { |
| type_ = Cellular::kTypeCdma; |
| } else { |
| LOG(ERROR) << "Unsupported capabilities: " << capabilities; |
| return; |
| } |
| |
| // We cannot check the IP method to make sure it's not PPP. The IP |
| // method will be checked later when the bearer object is fetched. |
| CreateDeviceFromModemProperties(properties); |
| } |
| |
| void Modem::OnDeviceInfoAvailable(const string& link_name) { |
| SLOG(this, 1) << __func__ << ": " << link_name |
| << " pending: " << has_pending_device_info_; |
| if (has_pending_device_info_ && link_name_ == link_name) { |
| // has_pending_device_info_ is only set if we've already been through |
| // CreateDeviceFromModemProperties() and saved our initial |
| // properties already |
| has_pending_device_info_ = false; |
| CreateDeviceFromModemProperties(initial_properties_); |
| } |
| } |
| |
| string Modem::GetModemInterface() const { |
| return string(MM_DBUS_INTERFACE_MODEM); |
| } |
| |
| bool Modem::GetLinkName(const KeyValueStore& modem_props, string* name) const { |
| if (!modem_props.ContainsVariant(MM_MODEM_PROPERTY_PORTS)) { |
| LOG(ERROR) << "Device missing property: " << MM_MODEM_PROPERTY_PORTS; |
| return false; |
| } |
| |
| auto ports = modem_props.GetVariant(MM_MODEM_PROPERTY_PORTS) |
| .Get<std::vector<std::tuple<string, uint32_t>>>(); |
| string net_port; |
| for (const auto& port_pair : ports) { |
| if (std::get<1>(port_pair) == MM_MODEM_PORT_TYPE_NET) { |
| net_port = std::get<0>(port_pair); |
| break; |
| } |
| } |
| |
| if (net_port.empty()) { |
| LOG(ERROR) << "Could not find net port used by the device."; |
| return false; |
| } |
| |
| *name = net_port; |
| return true; |
| } |
| |
| void Modem::CreateDeviceFromModemProperties( |
| const InterfaceToProperties& properties) { |
| if (device_) |
| return; |
| |
| SLOG(this, 1) << __func__; |
| |
| InterfaceToProperties::const_iterator properties_it = |
| properties.find(GetModemInterface()); |
| if (properties_it == properties.end()) { |
| LOG(ERROR) << "Unable to find modem interface properties."; |
| return; |
| } |
| |
| string mac_address; |
| int interface_index = -1; |
| if (GetLinkName(properties_it->second, &link_name_)) { |
| GetDeviceParams(&mac_address, &interface_index); |
| if (interface_index < 0) { |
| LOG(ERROR) << "Unable to create cellular device -- no interface index."; |
| return; |
| } |
| if (mac_address.empty()) { |
| // Save our properties, wait for OnDeviceInfoAvailable to be called. |
| LOG(WARNING) |
| << __func__ |
| << ": No hardware address, device creation pending device info."; |
| initial_properties_ = properties; |
| has_pending_device_info_ = true; |
| return; |
| } |
| // Got the interface index and MAC address. Fall-through to actually |
| // creating the Cellular object. |
| } else { |
| // Probably a PPP dongle. |
| LOG(INFO) << "Cellular device without link name; assuming PPP dongle."; |
| link_name_ = base::StringPrintf(kFakeDevNameFormat, fake_dev_serial_++); |
| mac_address = kFakeDevAddress; |
| interface_index = kFakeDevInterfaceIndex; |
| } |
| |
| DeviceInfo* device_info = modem_info_->manager()->device_info(); |
| if (device_info->IsDeviceBlocked(link_name_)) { |
| LOG(INFO) << "Not creating cellular device for blocked interface " |
| << link_name_ << "."; |
| return; |
| } |
| |
| device_ = device_info->GetCellularDevice(interface_index, mac_address, this); |
| |
| // Give the device a chance to extract any capability-specific properties. |
| for (properties_it = properties.begin(); properties_it != properties.end(); |
| ++properties_it) { |
| device_->OnPropertiesChanged(properties_it->first, properties_it->second); |
| } |
| |
| SLOG(this, 1) << "Cellular device created: " << device_->link_name() |
| << " Enabled: " << device_->enabled(); |
| } |
| |
| bool Modem::GetDeviceParams(string* mac_address, int* interface_index) { |
| // TODO(petkov): Get the interface index from DeviceInfo, similar to the MAC |
| // address below. |
| *interface_index = rtnl_handler_->GetInterfaceIndex(link_name_); |
| if (*interface_index < 0) { |
| return false; |
| } |
| |
| ByteString address_bytes; |
| if (!modem_info_->manager()->device_info()->GetMacAddress(*interface_index, |
| &address_bytes)) { |
| return false; |
| } |
| |
| *mac_address = address_bytes.HexEncode(); |
| return true; |
| } |
| |
| void Modem::OnPropertiesChanged(const string& interface, |
| const KeyValueStore& changed_properties) { |
| SLOG(this, 2) << __func__; |
| SLOG(this, 3) << "PropertiesChanged signal received."; |
| if (device_) { |
| device_->OnPropertiesChanged(interface, changed_properties); |
| } |
| } |
| |
| void Modem::OnModemManagerPropertiesChanged(const string& interface, |
| const KeyValueStore& properties) { |
| OnPropertiesChanged(interface, properties); |
| } |
| |
| } // namespace shill |