// Copyright 2013 The Chromium 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 "chromeos/dbus/fake_shill_device_client.h" #include #include "base/bind.h" #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/thread_task_runner_handle.h" #include "base/values.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_manager_client.h" #include "chromeos/dbus/shill_property_changed_observer.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_path.h" #include "dbus/object_proxy.h" #include "dbus/values_util.h" #include "net/base/ip_endpoint.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace { const char kSimPuk[] = "12345678"; // Matches pseudomodem. const int kSimPinMinLength = 4; const int kSimPukRetryCount = 10; const char kFailedMessage[] = "Failed"; void ErrorFunction(const std::string& device_path, const std::string& error_name, const std::string& error_message) { LOG(ERROR) << "Shill Error for: " << device_path << ": " << error_name << " : " << error_message; } void PostError(const std::string& error, const ShillDeviceClient::ErrorCallback& error_callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, error, kFailedMessage)); } void PostNotFoundError(const ShillDeviceClient::ErrorCallback& error_callback) { PostError(shill::kErrorResultNotFound, error_callback); } bool IsReadOnlyProperty(const std::string& name) { if (name == shill::kCarrierProperty) return true; return false; } } // namespace const char FakeShillDeviceClient::kDefaultSimPin[] = "1111"; const int FakeShillDeviceClient::kSimPinRetryCount = 3; FakeShillDeviceClient::FakeShillDeviceClient() : initial_tdls_busy_count_(0), tdls_busy_count_(0), weak_ptr_factory_(this) {} FakeShillDeviceClient::~FakeShillDeviceClient() { STLDeleteContainerPairSecondPointers( observer_list_.begin(), observer_list_.end()); } // ShillDeviceClient overrides. void FakeShillDeviceClient::Init(dbus::Bus* bus) {} void FakeShillDeviceClient::AddPropertyChangedObserver( const dbus::ObjectPath& device_path, ShillPropertyChangedObserver* observer) { GetObserverList(device_path).AddObserver(observer); } void FakeShillDeviceClient::RemovePropertyChangedObserver( const dbus::ObjectPath& device_path, ShillPropertyChangedObserver* observer) { GetObserverList(device_path).RemoveObserver(observer); } void FakeShillDeviceClient::GetProperties( const dbus::ObjectPath& device_path, const DictionaryValueCallback& callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeShillDeviceClient::PassStubDeviceProperties, weak_ptr_factory_.GetWeakPtr(), device_path, callback)); } void FakeShillDeviceClient::ProposeScan( const dbus::ObjectPath& device_path, const VoidDBusMethodCallback& callback) { PostVoidCallback(callback, DBUS_METHOD_CALL_SUCCESS); } void FakeShillDeviceClient::SetProperty(const dbus::ObjectPath& device_path, const std::string& name, const base::Value& value, const base::Closure& callback, const ErrorCallback& error_callback) { if (IsReadOnlyProperty(name)) PostError(shill::kErrorResultInvalidArguments, error_callback); SetPropertyInternal(device_path, name, value, callback, error_callback); } void FakeShillDeviceClient::SetPropertyInternal( const dbus::ObjectPath& device_path, const std::string& name, const base::Value& value, const base::Closure& callback, const ErrorCallback& error_callback) { base::DictionaryValue* device_properties = NULL; if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(), &device_properties)) { PostNotFoundError(error_callback); return; } device_properties->SetWithoutPathExpansion(name, value.DeepCopy()); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&FakeShillDeviceClient::NotifyObserversPropertyChanged, weak_ptr_factory_.GetWeakPtr(), device_path, name)); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::ClearProperty( const dbus::ObjectPath& device_path, const std::string& name, const VoidDBusMethodCallback& callback) { base::DictionaryValue* device_properties = NULL; if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(), &device_properties)) { PostVoidCallback(callback, DBUS_METHOD_CALL_FAILURE); return; } device_properties->RemoveWithoutPathExpansion(name, NULL); PostVoidCallback(callback, DBUS_METHOD_CALL_SUCCESS); } void FakeShillDeviceClient::AddIPConfig( const dbus::ObjectPath& device_path, const std::string& method, const ObjectPathDBusMethodCallback& callback) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, dbus::ObjectPath())); } void FakeShillDeviceClient::RequirePin(const dbus::ObjectPath& device_path, const std::string& pin, bool require, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "RequirePin: " << device_path.value(); if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } if (!SimTryPin(device_path.value(), pin)) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultIncorrectPin, "")); return; } SimLockStatus status = GetSimLockStatus(device_path.value()); status.lock_enabled = require; SetSimLockStatus(device_path.value(), status); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::EnterPin(const dbus::ObjectPath& device_path, const std::string& pin, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "EnterPin: " << device_path.value(); if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } if (!SimTryPin(device_path.value(), pin)) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultIncorrectPin, "")); return; } SetSimLocked(device_path.value(), false); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::UnblockPin(const dbus::ObjectPath& device_path, const std::string& puk, const std::string& pin, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "UnblockPin: " << device_path.value(); if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } if (!SimTryPuk(device_path.value(), puk)) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultIncorrectPin, "")); return; } if (pin.length() < kSimPinMinLength) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultInvalidArguments, "")); return; } sim_pin_[device_path.value()] = pin; SetSimLocked(device_path.value(), false); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::ChangePin(const dbus::ObjectPath& device_path, const std::string& old_pin, const std::string& new_pin, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "ChangePin: " << device_path.value(); if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } if (!SimTryPin(device_path.value(), old_pin)) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultIncorrectPin, "")); return; } if (new_pin.length() < kSimPinMinLength) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultInvalidArguments, "")); return; } sim_pin_[device_path.value()] = new_pin; base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::Register(const dbus::ObjectPath& device_path, const std::string& network_id, const base::Closure& callback, const ErrorCallback& error_callback) { if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::SetCarrier(const dbus::ObjectPath& device_path, const std::string& carrier, const base::Closure& callback, const ErrorCallback& error_callback) { SetPropertyInternal(device_path, shill::kCarrierProperty, base::StringValue(carrier), callback, error_callback); } void FakeShillDeviceClient::Reset(const dbus::ObjectPath& device_path, const base::Closure& callback, const ErrorCallback& error_callback) { if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::PerformTDLSOperation( const dbus::ObjectPath& device_path, const std::string& operation, const std::string& peer, const StringCallback& callback, const ErrorCallback& error_callback) { if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } // Use -1 to emulate a TDLS failure. if (tdls_busy_count_ == -1) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorDhcpFailed, "Failed")); return; } if (operation != shill::kTDLSStatusOperation && tdls_busy_count_ > 0) { --tdls_busy_count_; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(error_callback, shill::kErrorResultInProgress, "In-Progress")); return; } tdls_busy_count_ = initial_tdls_busy_count_; std::string result; if (operation == shill::kTDLSDiscoverOperation) { if (tdls_state_.empty()) tdls_state_ = shill::kTDLSDisconnectedState; } else if (operation == shill::kTDLSSetupOperation) { if (tdls_state_.empty()) tdls_state_ = shill::kTDLSConnectedState; } else if (operation == shill::kTDLSTeardownOperation) { if (tdls_state_.empty()) tdls_state_ = shill::kTDLSDisconnectedState; } else if (operation == shill::kTDLSStatusOperation) { result = tdls_state_; } base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(callback, result)); } void FakeShillDeviceClient::AddWakeOnPacketConnection( const dbus::ObjectPath& device_path, const net::IPEndPoint& ip_endpoint, const base::Closure& callback, const ErrorCallback& error_callback) { if (!stub_devices_.HasKey(device_path.value())) { PostNotFoundError(error_callback); return; } wake_on_packet_connections_[device_path].insert(ip_endpoint); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::RemoveWakeOnPacketConnection( const dbus::ObjectPath& device_path, const net::IPEndPoint& ip_endpoint, const base::Closure& callback, const ErrorCallback& error_callback) { const auto device_iter = wake_on_packet_connections_.find(device_path); if (!stub_devices_.HasKey(device_path.value()) || device_iter == wake_on_packet_connections_.end()) { PostNotFoundError(error_callback); return; } const auto endpoint_iter = device_iter->second.find(ip_endpoint); if (endpoint_iter == device_iter->second.end()) { PostNotFoundError(error_callback); return; } device_iter->second.erase(endpoint_iter); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } void FakeShillDeviceClient::RemoveAllWakeOnPacketConnections( const dbus::ObjectPath& device_path, const base::Closure& callback, const ErrorCallback& error_callback) { const auto iter = wake_on_packet_connections_.find(device_path); if (!stub_devices_.HasKey(device_path.value()) || iter == wake_on_packet_connections_.end()) { PostNotFoundError(error_callback); return; } wake_on_packet_connections_.erase(iter); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); } ShillDeviceClient::TestInterface* FakeShillDeviceClient::GetTestInterface() { return this; } // ShillDeviceClient::TestInterface overrides. void FakeShillDeviceClient::AddDevice(const std::string& device_path, const std::string& type, const std::string& name) { DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> AddDevice(device_path); base::DictionaryValue* properties = GetDeviceProperties(device_path); properties->SetStringWithoutPathExpansion(shill::kTypeProperty, type); properties->SetStringWithoutPathExpansion(shill::kNameProperty, name); properties->SetStringWithoutPathExpansion(shill::kDBusObjectProperty, device_path); properties->SetStringWithoutPathExpansion( shill::kDBusServiceProperty, modemmanager::kModemManager1ServiceName); if (type == shill::kTypeCellular) { properties->SetBooleanWithoutPathExpansion( shill::kCellularAllowRoamingProperty, false); } } void FakeShillDeviceClient::RemoveDevice(const std::string& device_path) { DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> RemoveDevice(device_path); stub_devices_.RemoveWithoutPathExpansion(device_path, NULL); } void FakeShillDeviceClient::ClearDevices() { DBusThreadManager::Get()->GetShillManagerClient()->GetTestInterface()-> ClearDevices(); stub_devices_.Clear(); } void FakeShillDeviceClient::SetDeviceProperty(const std::string& device_path, const std::string& name, const base::Value& value) { VLOG(1) << "SetDeviceProperty: " << device_path << ": " << name << " = " << value; SetPropertyInternal(dbus::ObjectPath(device_path), name, value, base::Bind(&base::DoNothing), base::Bind(&ErrorFunction, device_path)); } std::string FakeShillDeviceClient::GetDevicePathForType( const std::string& type) { for (base::DictionaryValue::Iterator iter(stub_devices_); !iter.IsAtEnd(); iter.Advance()) { const base::DictionaryValue* properties = NULL; if (!iter.value().GetAsDictionary(&properties)) continue; std::string prop_type; if (!properties->GetStringWithoutPathExpansion( shill::kTypeProperty, &prop_type) || prop_type != type) continue; return iter.key(); } return std::string(); } void FakeShillDeviceClient::SetTDLSBusyCount(int count) { tdls_busy_count_ = std::max(count, -1); } void FakeShillDeviceClient::SetTDLSState(const std::string& state) { tdls_state_ = state; } void FakeShillDeviceClient::SetSimLocked(const std::string& device_path, bool locked) { SimLockStatus status = GetSimLockStatus(device_path); status.type = locked ? shill::kSIMLockPin : ""; status.retries_left = kSimPinRetryCount; SetSimLockStatus(device_path, status); } // Private Methods ------------------------------------------------------------- FakeShillDeviceClient::SimLockStatus FakeShillDeviceClient::GetSimLockStatus( const std::string& device_path) { SimLockStatus status; base::DictionaryValue* device_properties = nullptr; base::DictionaryValue* simlock_dict = nullptr; if (stub_devices_.GetDictionaryWithoutPathExpansion(device_path, &device_properties) && device_properties->GetDictionaryWithoutPathExpansion( shill::kSIMLockStatusProperty, &simlock_dict)) { simlock_dict->GetStringWithoutPathExpansion(shill::kSIMLockTypeProperty, &status.type); simlock_dict->GetIntegerWithoutPathExpansion( shill::kSIMLockRetriesLeftProperty, &status.retries_left); simlock_dict->GetBooleanWithoutPathExpansion(shill::kSIMLockEnabledProperty, &status.lock_enabled); if (status.type == shill::kSIMLockPin && status.retries_left == 0) status.retries_left = kSimPinRetryCount; } return status; } void FakeShillDeviceClient::SetSimLockStatus(const std::string& device_path, const SimLockStatus& status) { base::DictionaryValue* device_properties = NULL; if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path, &device_properties)) { NOTREACHED() << "Device not found: " << device_path; return; } base::DictionaryValue* simlock_dict = nullptr; if (!device_properties->GetDictionaryWithoutPathExpansion( shill::kSIMLockStatusProperty, &simlock_dict)) { simlock_dict = new base::DictionaryValue; device_properties->SetWithoutPathExpansion(shill::kSIMLockStatusProperty, simlock_dict); } simlock_dict->Clear(); simlock_dict->SetStringWithoutPathExpansion(shill::kSIMLockTypeProperty, status.type); simlock_dict->SetIntegerWithoutPathExpansion( shill::kSIMLockRetriesLeftProperty, status.retries_left); simlock_dict->SetBooleanWithoutPathExpansion(shill::kSIMLockEnabledProperty, status.lock_enabled); NotifyObserversPropertyChanged(dbus::ObjectPath(device_path), shill::kSIMLockStatusProperty); } bool FakeShillDeviceClient::SimTryPin(const std::string& device_path, const std::string& pin) { SimLockStatus status = GetSimLockStatus(device_path); if (status.type == shill::kSIMLockPuk) { VLOG(1) << "SimTryPin called with PUK locked."; return false; // PUK locked, PIN won't work. } if (pin.length() < kSimPinMinLength) return false; std::string sim_pin = sim_pin_[device_path]; if (sim_pin.empty()) { sim_pin = kDefaultSimPin; sim_pin_[device_path] = sim_pin; } if (pin == sim_pin) { status.type = ""; status.retries_left = kSimPinRetryCount; SetSimLockStatus(device_path, status); return true; } VLOG(1) << "SIM PIN: " << pin << " != " << sim_pin << " Retries left: " << (status.retries_left - 1); if (--status.retries_left <= 0) { status.retries_left = kSimPukRetryCount; status.type = shill::kSIMLockPuk; status.lock_enabled = true; } SetSimLockStatus(device_path, status); return false; } bool FakeShillDeviceClient::SimTryPuk(const std::string& device_path, const std::string& puk) { SimLockStatus status = GetSimLockStatus(device_path); if (status.type != shill::kSIMLockPuk) { VLOG(1) << "PUK Not locked"; return true; // Not PUK locked. } if (status.retries_left == 0) { VLOG(1) << "PUK: No retries left"; return false; // Permanently locked. } if (puk == kSimPuk) { status.type = ""; status.retries_left = kSimPinRetryCount; SetSimLockStatus(device_path, status); return true; } --status.retries_left; VLOG(1) << "SIM PUK: " << puk << " != " << kSimPuk << " Retries left: " << status.retries_left; SetSimLockStatus(device_path, status); return false; } void FakeShillDeviceClient::PassStubDeviceProperties( const dbus::ObjectPath& device_path, const DictionaryValueCallback& callback) const { const base::DictionaryValue* device_properties = NULL; if (!stub_devices_.GetDictionaryWithoutPathExpansion( device_path.value(), &device_properties)) { base::DictionaryValue empty_dictionary; callback.Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary); return; } callback.Run(DBUS_METHOD_CALL_SUCCESS, *device_properties); } // Posts a task to run a void callback with status code |status|. void FakeShillDeviceClient::PostVoidCallback( const VoidDBusMethodCallback& callback, DBusMethodCallStatus status) { base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(callback, status)); } void FakeShillDeviceClient::NotifyObserversPropertyChanged( const dbus::ObjectPath& device_path, const std::string& property) { base::DictionaryValue* dict = NULL; std::string path = device_path.value(); if (!stub_devices_.GetDictionaryWithoutPathExpansion(path, &dict)) { LOG(ERROR) << "Notify for unknown service: " << path; return; } base::Value* value = NULL; if (!dict->GetWithoutPathExpansion(property, &value)) { LOG(ERROR) << "Notify for unknown property: " << path << " : " << property; return; } FOR_EACH_OBSERVER(ShillPropertyChangedObserver, GetObserverList(device_path), OnPropertyChanged(property, *value)); } base::DictionaryValue* FakeShillDeviceClient::GetDeviceProperties( const std::string& device_path) { base::DictionaryValue* properties = NULL; if (!stub_devices_.GetDictionaryWithoutPathExpansion( device_path, &properties)) { properties = new base::DictionaryValue; stub_devices_.SetWithoutPathExpansion(device_path, properties); } return properties; } FakeShillDeviceClient::PropertyObserverList& FakeShillDeviceClient::GetObserverList(const dbus::ObjectPath& device_path) { std::map::iterator iter = observer_list_.find(device_path); if (iter != observer_list_.end()) return *(iter->second); PropertyObserverList* observer_list = new PropertyObserverList(); observer_list_[device_path] = observer_list; return *observer_list; } } // namespace chromeos