blob: b28fb23a679f44dc264067052006f6e8ccb72a08 [file] [log] [blame] [edit]
// Copyright 2017 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modemfwd/logging.h"
#include "modemfwd/modem.h"
#include "modemfwd/modem_tracker.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/task/single_thread_task_runner.h>
#include <brillo/variant_dictionary.h>
#include <dbus/shill/dbus-constants.h>
#include <ModemManager/ModemManager.h>
namespace modemfwd {
namespace {
void OnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
DVLOG(1) << (success ? "Connected" : "Failed to connect") << " to signal "
<< signal_name << " of " << interface_name;
}
} // namespace
ModemTracker::ModemTracker(
scoped_refptr<dbus::Bus> bus,
OnModemCarrierIdReadyCallback on_modem_carrier_id_ready_callback,
OnModemDeviceSeenCallback on_modem_device_seen_callback,
OnModemStateChangeCallback on_modem_state_change_callback,
OnModemPowerStateChangeCallback on_modem_power_state_change_callback)
: bus_(bus),
shill_proxy_(new org::chromium::flimflam::ManagerProxy(bus)),
on_modem_carrier_id_ready_callback_(on_modem_carrier_id_ready_callback),
on_modem_device_seen_callback_(on_modem_device_seen_callback),
on_modem_state_change_callback_(on_modem_state_change_callback),
on_modem_power_state_change_callback_(
on_modem_power_state_change_callback),
weak_ptr_factory_(this) {
shill_proxy_->GetObjectProxy()->WaitForServiceToBeAvailable(base::BindOnce(
&ModemTracker::OnServiceAvailable, weak_ptr_factory_.GetWeakPtr()));
}
void ModemTracker::OnServiceAvailable(bool available) {
if (!available) {
LOG(WARNING) << "shill disappeared";
modem_objects_.clear();
modem_proxys_.clear();
return;
}
shill_proxy_->RegisterPropertyChangedSignalHandler(
base::BindRepeating(&ModemTracker::OnManagerPropertyChanged,
weak_ptr_factory_.GetWeakPtr()),
base::BindRepeating(&OnSignalConnected));
brillo::ErrorPtr error;
brillo::VariantDictionary properties;
if (!shill_proxy_->GetProperties(&properties, &error)) {
LOG(ERROR) << "Could not get property list from shill: "
<< error->GetMessage();
return;
}
OnDeviceListChanged(properties[shill::kDevicesProperty]
.TryGet<std::vector<dbus::ObjectPath>>());
// Update |modem_proxys_| in case modem object path has changed.
UpdateModemProxyMultiDevice(properties[shill::kDevicesProperty]
.TryGet<std::vector<dbus::ObjectPath>>());
}
void ModemTracker::UpdateModemProxySingleDevice(dbus::ObjectPath device_path) {
brillo::VariantDictionary properties;
std::string modem_object_path;
auto device =
std::make_unique<org::chromium::flimflam::DeviceProxy>(bus_, device_path);
if (!device->GetProperties(&properties, NULL))
return;
if (properties[shill::kTypeProperty].TryGet<std::string>() !=
shill::kTypeCellular) {
return;
}
if (!properties[shill::kDBusObjectProperty].GetValue(&modem_object_path)) {
ELOG(ERROR) << "Could not get modem object path for device "
<< device_path.value();
return;
}
// Empty modem object path, reset modem proxy
if (modem_object_path.empty()) {
modem_proxys_.erase(device_path);
return;
}
// Modem object path did not change, no need to update proxy
auto modem_proxy = modem_proxys_.find(device_path);
if (modem_proxy != modem_proxys_.end()) {
auto old_path = modem_proxy->second->GetObjectPath().value();
ELOG(INFO) << __func__ << ": modem object old path: " << old_path;
if (old_path == modem_object_path)
return;
}
ELOG(INFO) << __func__ << ": modem object new path: " << modem_object_path;
auto modem_proxy_ptr =
std::make_unique<org::freedesktop::ModemManager1::ModemProxy>(
bus_, MM_DBUS_SERVICE, dbus::ObjectPath(modem_object_path));
if (!modem_proxy_ptr) {
ELOG(ERROR) << ": could not create modem proxy for object path: "
<< modem_object_path;
return;
}
// Start listening property change on the new modem object
modem_proxy_ptr->InitializeProperties(
base::BindRepeating(&ModemTracker::OnModemPropertyChanged,
weak_ptr_factory_.GetWeakPtr(), device_path));
std::string device_id;
if (properties[shill::kDeviceIdProperty].GetValue(&device_id)) {
// Get current power state of the new modem object
if (modem_proxy_ptr->GetProperties()->power_state.GetAndBlock()) {
on_modem_power_state_change_callback_.Run(
device_id,
static_cast<Modem::PowerState>(modem_proxy_ptr->power_state()));
}
// Get current modem state of the new modem object
if (modem_proxy_ptr->GetProperties()->state.GetAndBlock()) {
on_modem_state_change_callback_.Run(
device_id, static_cast<Modem::State>(modem_proxy_ptr->state()));
}
}
// Save the updated modem proxy
modem_proxys_[device_path] = std::move(modem_proxy_ptr);
}
void ModemTracker::UpdateModemProxyMultiDevice(
const std::vector<dbus::ObjectPath>& device_list) {
for (const auto& device_path : device_list) {
UpdateModemProxySingleDevice(device_path);
}
}
void ModemTracker::OnManagerPropertyChanged(const std::string& property_name,
const brillo::Any& property_value) {
if (property_name == shill::kDevicesProperty)
OnDeviceListChanged(property_value.TryGet<std::vector<dbus::ObjectPath>>());
}
void ModemTracker::OnModemPropertyChanged(
dbus::ObjectPath device_path,
org::freedesktop::ModemManager1::ModemProxyInterface* modem_proxy_interface,
const std::string& prop) {
auto device =
std::make_unique<org::chromium::flimflam::DeviceProxy>(bus_, device_path);
std::string device_id;
brillo::VariantDictionary properties;
// Cannot get valid device id
if (!(device->GetProperties(&properties, NULL) &&
properties[shill::kDeviceIdProperty].GetValue(&device_id))) {
return;
}
auto modem_proxy = modem_proxys_.find(device_path);
if (modem_proxy == modem_proxys_.end()) {
return;
}
if (prop == MM_MODEM_PROPERTY_POWERSTATE || prop == MM_MODEM_PROPERTY_STATE) {
// Update both power state and modem state whenever one of them has changed
// in case a property update signal is missed
if (modem_proxy->second->GetProperties()->power_state.is_valid()) {
Modem::PowerState power_state =
static_cast<Modem::PowerState>(modem_proxy->second->power_state());
ELOG(INFO) << __func__ << ": new power state: " << power_state;
on_modem_power_state_change_callback_.Run(device_id, power_state);
}
if (modem_proxy->second->GetProperties()->state.is_valid()) {
Modem::State modem_state =
static_cast<Modem::State>(modem_proxy->second->state());
ELOG(INFO) << __func__ << ": new modem state: " << modem_state;
on_modem_state_change_callback_.Run(device_id, modem_state);
}
}
}
void ModemTracker::OnDevicePropertyChanged(dbus::ObjectPath device_path,
const std::string& property_name,
const brillo::Any& property_value) {
// Modem object has changed. Update modem proxy
if (property_name == shill::kDBusObjectProperty) {
UpdateModemProxySingleDevice(device_path);
}
// Listen for the HomeProvider change triggered by a SIM change
if (property_name != shill::kHomeProviderProperty)
return;
auto current_carrier_id = modem_objects_.find(device_path);
if (current_carrier_id == modem_objects_.end())
return;
std::map<std::string, std::string> operator_info(
property_value.TryGet<std::map<std::string, std::string>>());
std::string carrier_id = operator_info[shill::kOperatorUuidKey];
if (carrier_id == current_carrier_id->second)
return;
current_carrier_id->second = carrier_id;
ELOG(INFO) << "Carrier UUID changed to [" << carrier_id << "] for device "
<< device_path.value();
// Skip update if no carrier info
if (carrier_id.empty())
return;
// Trigger the firmware update
auto device =
std::make_unique<org::chromium::flimflam::DeviceProxy>(bus_, device_path);
on_modem_carrier_id_ready_callback_.Run(std::move(device));
}
void ModemTracker::DelayedSimCheck(dbus::ObjectPath device_path) {
brillo::VariantDictionary properties;
bool sim_present;
auto device =
std::make_unique<org::chromium::flimflam::DeviceProxy>(bus_, device_path);
if (!device->GetProperties(&properties, NULL) ||
!properties[shill::kSIMPresentProperty].GetValue(&sim_present)) {
LOG(ERROR) << "Could not get SIMPresent property for device "
<< device_path.value();
return;
}
if (!sim_present) {
on_modem_carrier_id_ready_callback_.Run(std::move(device));
}
}
void ModemTracker::OnDeviceListChanged(
const std::vector<dbus::ObjectPath>& new_list) {
std::map<dbus::ObjectPath, std::string> new_modems;
for (const auto& device_path : new_list) {
if (modem_objects_.find(device_path) != modem_objects_.end()) {
// Keep existing devices in the new list.
new_modems[device_path] = modem_objects_[device_path];
continue;
}
// See if the modem object is of cellular type.
auto device = std::make_unique<org::chromium::flimflam::DeviceProxy>(
bus_, device_path);
brillo::ErrorPtr error;
brillo::VariantDictionary properties;
if (!device->GetProperties(&properties, &error)) {
LOG(ERROR) << "Could not get property list for device "
<< device_path.value() << ": " << error->GetMessage();
continue;
}
if (properties[shill::kTypeProperty].TryGet<std::string>() !=
shill::kTypeCellular) {
DVLOG(1) << "Device " << device_path.value()
<< " is not cellular type, ignoring";
continue;
}
std::string device_id;
std::string equipment_id;
if (!properties[shill::kDeviceIdProperty].GetValue(&device_id) ||
!properties[shill::kEquipmentIdProperty].GetValue(&equipment_id)) {
LOG(ERROR) << "Modem " << device_path.value()
<< " has no device ID or no equipment ID, ignoring";
continue;
}
on_modem_device_seen_callback_.Run(device_id, equipment_id);
std::map<std::string, std::string> operator_info;
if (!properties[shill::kHomeProviderProperty].GetValue(&operator_info))
continue;
bool sim_present;
if (!properties[shill::kSIMPresentProperty].GetValue(&sim_present)) {
LOG(ERROR) << "Modem " << device_path.value()
<< " has no SIM Present property, ignoring";
continue;
}
if (!sim_present) {
// Test the SIMPresent property again after short delay before fw update
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
BindOnce(&ModemTracker::DelayedSimCheck,
weak_ptr_factory_.GetWeakPtr(), device_path),
base::Seconds(10));
}
// Record the modem device with its current carrier UUID
std::string carrier_id = operator_info[shill::kOperatorUuidKey];
new_modems[device_path] = carrier_id;
// Listen to the Device HomeProvider property in order to detect future
// SIM swaps.
device->RegisterPropertyChangedSignalHandler(
base::BindRepeating(&ModemTracker::OnDevicePropertyChanged,
weak_ptr_factory_.GetWeakPtr(), device_path),
base::BindRepeating(&OnSignalConnected));
// Try to update if carrier is known
if (!carrier_id.empty())
on_modem_carrier_id_ready_callback_.Run(std::move(device));
}
modem_objects_ = new_modems;
}
} // namespace modemfwd